Berry Webclient: Change StreamBytes to StreamBeBytesWriter, and improve wc_getbytes (#18837)

This reduces allocation to a single allocaiton, but also theroetically allows the Bytes object to grow if content-length was unset.

(cherry picked from commit 936477f5977dd1a4f899498cc7cc0baf05b34a89)
This commit is contained in:
btsimonh 2023-06-10 10:28:31 +01:00 committed by GitHub
parent 6aed929e17
commit 63cc575c8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 57 additions and 17 deletions

View File

@ -654,21 +654,59 @@ extern "C" {
}
}
class StreamBytes: public Stream
// a stream which writes to the Bytes object on the top of the stack
class StreamBeBytesWriter: public Stream
{
public:
StreamBytes(uint8_t * buf_in, int len_in) : buf(buf_in), offset(0), len(len_in) {};
StreamBeBytesWriter(bvm *vm_in, int increment = 1024) : vm(vm_in), offset(0), incr(increment) {};
size_t write(const uint8_t *buffer, size_t size) override {
// AddLog(LOG_LEVEL_INFO, "FLASH: addr=%p hex=%*_H size=%i", addr_start + offset, 32, buffer, size);
if (size > 0) {
if (offset + size > len){
AddLog(LOG_LEVEL_ERROR, "BERRYWC: bufer overrun");
return size;
// we need size, not len, so can;t just get len with be_tobytes
be_getmember(vm, -1, ".size");
int32_t signed_size = be_toint(vm, -1);
be_pop(vm, 1); /* bytes() instance is at top */
// if it won't fit, make the bytes object bigger
if (offset + size > signed_size){
int newsize = offset + size + incr;
AddLog(LOG_LEVEL_INFO, "BE: realloc bytes in StreamBeBytesWriter newsize=%i", newsize);
be_getmember(vm, -1, "resize");
be_pushvalue(vm, -2);
be_pushint(vm, size);
be_call(vm, 2); /* call b.resize(size) */
be_pop(vm, 3); /* bytes() instance is at top */
// checkw e got it, because Berry just maxes out?
be_getmember(vm, -1, ".size");
signed_size = be_toint(vm, -1);
be_pop(vm, 1); /* bytes() instance is at top */
if (offset + size > signed_size){
// what should we raise here???
be_raise(vm, "alloc_error", "did not get enough extra bytes");
}
memcpy(buf+offset, buffer, size);
offset += size;
}
// AddLog(LOG_LEVEL_INFO, "FLASH: addr=%p hex=%*_H size=%i", addr_start + offset, 32, buffer, size);
if (offset + size > signed_size){
AddLog(LOG_LEVEL_ERROR, "BERRYWC: buffer overrun");
return size;
}
char *bytebuf = (char*) be_tobytes(vm, -1, NULL); /* we get the address of the internam buffer of size 'size' */
if (!bytebuf){
AddLog(LOG_LEVEL_ERROR, "BERRYWC: buffer null??");
return size;
}
// stream in our chunk
memcpy(bytebuf + offset, buffer, size);
offset += size;
// set the len
be_pushint(vm, offset);
be_setmember(vm, -2, ".len");
be_pop(vm, 1);
return size;
}
size_t write(uint8_t data) override {
@ -682,9 +720,9 @@ public:
void flush() override { }
protected:
uint8_t *buf; // start address
bvm *vm; // the berry VM
uint32_t offset; // how many bytes have already been written
uint32_t len; // allocated len
size_t incr; // amount to add to size if it does not fit.
};
extern "C" {
@ -693,14 +731,16 @@ extern "C" {
HTTPClientLight * cl = wc_getclient(vm);
int32_t sz = cl->getSize();
// abort if we exceed 32KB size, things will not go well otherwise
if (sz >= 32767 || sz < 0) {
be_raise(vm, "value_error", "response size -1 or too big (>32KB)");
if (sz >= 32767) {
be_raise(vm, "value_error", "response size too big (>32KB)");
}
uint8_t * buf = (uint8_t*) be_pushbuffer(vm, sz);
StreamBytes memory_writer(buf, sz);
// default to 1K starter if contetn-length not present
if (sz < 0) sz = 1024;
// create a bytes object at top of stack.
// the streamwriter knows how to get it.
uint8_t * buf = (uint8_t*) be_pushbytes(vm, nullptr, sz);
StreamBeBytesWriter memory_writer(vm);
int32_t written = cl->writeToStream(&memory_writer);
be_pushbytes(vm, buf, sz);
cl->end(); // free allocated memory ~16KB
be_return(vm); /* return code */
}