// SPDX-License-Identifier: ISC // database impl #include "y16t_db.h" #include "y16t_internal.h" #include "y16t_idx.h" #include #include #include #include typedef struct { int fd; } y16t_db_actx_t; __attribute__((pure)) static off_t y16t_db_offstr(const uint32_t offset) { return ((off_t)offset) << 12; } __attribute__((pure)) static uint32_t y16t_db_unoffstr(const off_t x) { return x >> 12; } __attribute__((nonnull)) static uint32_t y16t_db_palloc(void * context_) { y16t_db_actx_t *context = context_; const int fd = context->fd; const off_t eoffs_orig = lseek(fd, 0, SEEK_END); if(eoffs_orig == -1) return 0; const off_t eoffs = (eoffs_orig >= (1 << 18)) ? eoffs_orig : (1 << 18); return (-1 == ftruncate(fd, eoffs + (1 << 18))) ? 0 : y16t_db_unoffstr(eoffs); } __attribute__((nonnull)) static uint32_t y16t_db_palloc2(void * context_) { y16t_db_actx_t *context = context_; const int fd = context->fd; const off_t eoffs = lseek(fd, 0, SEEK_END); if(eoffs == -1) return 0; return (-1 == ftruncate(fd, eoffs + y16t_db_offstr(1))) ? 0 : y16t_db_unoffstr(eoffs); } // @param bufexp must point to 44 valid bytes __attribute__((nonnull)) static int y16t_db_intern_pgsearch( const int fd, off_t start_offset, uint8_t bufexp[], uint32_t *i_ ) { uint8_t buf[4096] = {0}; const uint8_t zeros[44] = {0}; ssize_t tmp = y16t_pread_all(fd, buf, sizeof(buf), start_offset); if(tmp < 0) return (int)tmp; *i_ = 0; uint32_t i; for(i = 0; i < sizeof(buf); i += 44) { if(memcmp(&buf[i], zeros, 44) == 0) break; if(memcmp(&buf[i], bufexp, 44) == 0) { *i_ = i; return 1; } } *i_ = i; return 0; } // inserts a database entry // x, y, z should point to arrays of 16 bytes each int y16t_db_insert( y16t_db_t db, const uint8_t *x, const uint8_t *y, const uint8_t *z ) { // first table uint32_t offset = 0; int tmp = 0; y16t_db_actx_t actx = { db }; { const y16t_idx_t idx = { db, 0 }; tmp = y16t_idx_get_or_insert( idx, x[0], y[0], &offset, y16t_db_palloc, (void*)&actx ); if(tmp < 0) return tmp; } // second table { const y16t_idx_t idx = { db, offset }; tmp = y16t_idx_get_or_insert( idx, x[1], y[1], &offset, y16t_db_palloc2, (void*)&actx ); if(tmp < 0) return tmp; } // prepare entry uint8_t bufexp[44]; memcpy(bufexp, &x[2], 14); memcpy(bufexp + 14, &y[2], 14); memcpy(bufexp + 28, &z, 16); // sequential data, read complete page const off_t start_offset = y16t_db_offstr(offset); uint32_t i; tmp = y16t_db_intern_pgsearch(db, start_offset, bufexp, &i); switch(tmp) { case 1: return 0; case 0: if(i >= 4052) return -ENOMEM; return y16t_pwrite_all(db, bufexp, sizeof(bufexp), start_offset + i); default: return tmp; } } // lookup a database entry // @return 1 means found, 0 means not found, negative is a negated errno value // x, y, z should point to arrays of 16 bytes each int y16t_db_lookup( y16t_db_t db, const uint8_t *x, const uint8_t *y, const uint8_t *z ) { uint32_t offset = 0; int tmp = 0; // first table y16t_idx_t idx = { db, 0 }; tmp = y16t_idx_lookup(idx, x[0], y[0], &offset); if(tmp < 0) return tmp; if(!offset) return 0; // second table idx.offset = offset; tmp = y16t_idx_lookup(idx, x[1], y[1], &offset); if(tmp < 0) return tmp; if(!offset) return 0; // prepare entry uint8_t bufexp[44]; memcpy(bufexp, &x[2], 14); memcpy(bufexp + 14, &y[2], 14); memcpy(bufexp + 28, &z, 16); // sequential data, read complete page uint32_t i; return y16t_db_intern_pgsearch(db, y16t_db_offstr(offset), bufexp, &i); } // iterate over the database typedef struct { y16t_db_t db; const uint8_t *xy; int (*callback)(const uint8_t*, const uint8_t*, void*); void *context; } y16t_db_fectx_t; static int y16t_db_foreach_x3(uint32_t offset, void *context_) { if(!offset) return 0; const y16t_db_fectx_t *context = context_; // sequential data, read complete page uint8_t buf[4096] = {0}; const uint8_t zeros[44] = {0}; const off_t start_offset = y16t_db_offstr(offset); ssize_t tmp = y16t_pread_all(context->db, buf, sizeof(buf), start_offset); if(tmp < 0) return (int)tmp; uint32_t i; const uint8_t *xyrest = &context->xy[2]; for(i = 0; i < sizeof(buf); i += 44) { if(memcmp(&buf[i], zeros, 44) == 0) break; if(memcmp(&buf[i + 14], xyrest, 14) == 0) { const int tmp2 = context->callback(&buf[i], &buf[i + 28], context->context); if(tmp2) return tmp2; } } return 0; } static int y16t_db_foreach_y3(uint32_t offset, void *context_) { if(!offset) return 0; const y16t_db_fectx_t *context = context_; // sequential data, read complete page uint8_t buf[4096] = {0}; const uint8_t zeros[44] = {0}; const off_t start_offset = y16t_db_offstr(offset); ssize_t tmp = y16t_pread_all(context->db, buf, sizeof(buf), start_offset); if(tmp < 0) return (int)tmp; uint32_t i; const uint8_t *xyrest = &context->xy[2]; for(i = 0; i < sizeof(buf); i += 44) { if(memcmp(&buf[i], zeros, 44) == 0) break; if(memcmp(&buf[i], xyrest, 14) == 0) { const int tmp2 = context->callback(&buf[i + 14], &buf[i + 28], context->context); if(tmp2) return tmp2; } } return 0; } static int y16t_db_foreach_x2(uint32_t offset, void *context_) { const y16t_db_fectx_t *context = context_; if(!offset) return 0; const y16t_idx_t idx = { context->db, offset }; return y16t_idx_foreach_x(idx, context->xy[1], y16t_db_foreach_x3, context_); } static int y16t_db_foreach_y2(uint32_t offset, void *context_) { const y16t_db_fectx_t *context = context_; if(!offset) return 0; const y16t_idx_t idx = { context->db, offset }; return y16t_idx_foreach_y(idx, context->xy[1], y16t_db_foreach_y3, context_); } int y16t_db_foreach_x( y16t_db_t db, const uint8_t *y, int (*callback)(const uint8_t*, const uint8_t*, void*), void *context ) { y16t_db_fectx_t fectx = { db, y, callback, context }; const y16t_idx_t idx = { db, 0 }; return y16t_idx_foreach_x(idx, y[0], y16t_db_foreach_x2, (void*) &fectx); } int y16t_db_foreach_y( y16t_db_t db, const uint8_t *x, int (*callback)(const uint8_t*, const uint8_t*, void*), void *context ) { y16t_db_fectx_t fectx = { db, x, callback, context }; const y16t_idx_t idx = { db, 0 }; return y16t_idx_foreach_y(idx, x[0], y16t_db_foreach_y2, (void*) &fectx); }