char* InStream_buf(InStream *self, size_t request) { InStreamIVARS *const ivars = InStream_IVARS(self); const int64_t bytes_in_buf = PTR_TO_I64(ivars->limit) - PTR_TO_I64(ivars->buf); /* It's common for client code to overestimate how much is needed, because * the request has to figure in worst-case for compressed data. However, * if we can still serve them everything they request (e.g. they ask for 5 * bytes, they really need 1 byte, and there's 1k in the buffer), we can * skip the following refill block. */ if ((int64_t)request > bytes_in_buf) { const int64_t remaining_in_file = ivars->len - SI_tell(self); int64_t amount = request; // Try to bump up small requests. if (amount < IO_STREAM_BUF_SIZE) { amount = IO_STREAM_BUF_SIZE; } // Don't read past EOF. if (remaining_in_file < amount) { amount = remaining_in_file; } // Only fill if the recalculated, possibly smaller request exceeds the // amount available in the buffer. if (amount > bytes_in_buf) { S_fill(self, amount); } } return ivars->buf; }
static void S_fill(InStream *self, int64_t amount) { InStreamIVARS *const ivars = InStream_IVARS(self); FileWindow *const window = ivars->window; const int64_t virtual_file_pos = SI_tell(self); const int64_t real_file_pos = virtual_file_pos + ivars->offset; const int64_t remaining = ivars->len - virtual_file_pos; // Throw an error if the requested amount would take us beyond EOF. if (amount > remaining) { THROW(ERR, "Read past EOF of %o (pos: %u64 len: %u64 request: %u64)", ivars->filename, virtual_file_pos, ivars->len, amount); } // Make the request. if (FH_Window(ivars->file_handle, window, real_file_pos, amount)) { char *fw_buf = FileWindow_Get_Buf(window); int64_t fw_offset = FileWindow_Get_Offset(window); int64_t fw_len = FileWindow_Get_Len(window); char *const window_limit = fw_buf + fw_len; ivars->buf = fw_buf - fw_offset // theoretical start of real file + ivars->offset // top of virtual file + virtual_file_pos; // position within virtual file ivars->limit = window_limit - ivars->buf > remaining ? ivars->buf + remaining : window_limit; } else { Err *error = Err_get_error(); CB_catf(Err_Get_Mess(error), " (%o)", ivars->filename); RETHROW(INCREF(error)); } }
static void S_fill(InStream *self, int64_t amount) { FileWindow *const window = self->window; const int64_t virtual_file_pos = SI_tell(self); const int64_t real_file_pos = virtual_file_pos + self->offset; const int64_t remaining = self->len - virtual_file_pos; // Throw an error if the requested amount would take us beyond EOF. if (amount > remaining) { THROW(ERR, "Read past EOF of %o (pos: %u64 len: %u64 request: %u64)", self->filename, virtual_file_pos, self->len, amount); } // Make the request. if (FH_Window(self->file_handle, window, real_file_pos, amount)) { char *const window_limit = window->buf + window->len; self->buf = window->buf - window->offset // theoretical start of real file + self->offset // top of virtual file + virtual_file_pos; // position within virtual file self->limit = window_limit - self->buf > remaining ? self->buf + remaining : window_limit; } else { Err *error = Err_get_error(); CB_catf(Err_Get_Mess(error), " (%o)", self->filename); RETHROW(INCREF(error)); } }
InStream* InStream_clone(InStream *self) { InStream *twin = (InStream*)VTable_Make_Obj(self->vtable); InStream_do_open(twin, (Obj*)self->file_handle); InStream_Seek(twin, SI_tell(self)); return twin; }
static CFISH_INLINE void SI_read_bytes(InStream *self, char* buf, size_t len) { InStreamIVARS *const ivars = InStream_IVARS(self); const int64_t available = CHY_PTR_TO_I64(ivars->limit) - CHY_PTR_TO_I64(ivars->buf); if (available >= (int64_t)len) { // Request is entirely within buffer, so copy. memcpy(buf, ivars->buf, len); ivars->buf += len; } else { // Pass along whatever we've got in the buffer. if (available > 0) { memcpy(buf, ivars->buf, (size_t)available); buf += available; len -= (size_t)available; ivars->buf += available; } if (len < IO_STREAM_BUF_SIZE) { // Ensure that we have enough mapped, then copy the rest. int64_t got = S_refill(self); if (got < (int64_t)len) { int64_t orig_pos = SI_tell(self) - available; int64_t orig_len = len + available; THROW(ERR, "Read past EOF of %o (pos: %i64 len: %i64 " "request: %i64)", ivars->filename, orig_pos, ivars->len, orig_len); } memcpy(buf, ivars->buf, len); ivars->buf += len; } else { // Too big to handle via the buffer, so resort to a brute-force // read. const int64_t sub_file_pos = SI_tell(self); const int64_t real_file_pos = sub_file_pos + ivars->offset; bool success = FH_Read(ivars->file_handle, buf, real_file_pos, len); if (!success) { RETHROW(INCREF(Err_get_error())); } InStream_Seek_IMP(self, sub_file_pos + len); } } }
InStream* InStream_clone(InStream *self) { InStreamIVARS *const ivars = InStream_IVARS(self); VTable *vtable = InStream_Get_VTable(self); InStream *twin = (InStream*)VTable_Make_Obj(vtable); InStream_do_open(twin, (Obj*)ivars->file_handle); InStream_Seek(twin, SI_tell(self)); return twin; }
InStream* InStream_Clone_IMP(InStream *self) { InStreamIVARS *const ivars = InStream_IVARS(self); Class *klass = InStream_get_class(self); InStream *twin = (InStream*)Class_Make_Obj(klass); InStream_do_open(twin, (Obj*)ivars->file_handle); InStream_Seek(twin, SI_tell(self)); return twin; }
static int64_t S_refill(InStream *self) { // Determine the amount to request. const int64_t sub_file_pos = SI_tell(self); const int64_t remaining = self->len - sub_file_pos; const int64_t amount = remaining < IO_STREAM_BUF_SIZE ? remaining : IO_STREAM_BUF_SIZE; if (!remaining) { THROW(ERR, "Read past EOF of '%o' (offset: %i64 len: %i64)", self->filename, self->offset, self->len); } // Make the request. S_fill(self, amount); return amount; }
int64_t InStream_tell(InStream *self) { return SI_tell(self); }
int64_t InStream_Tell_IMP(InStream *self) { return SI_tell(self); }