#include #include #include #include "apfs/libzbitmap.h" #define __packed __attribute__((packed)) #define MIN(x, y) ((x) > (y) ? (y) : (x)) #define ZBM_MAGIC 0x094D425A #define ZBM_MAX_DECMP_CHUNK_SIZE 0x8000 #define ZBM_MAX_DECMP_CHUNK_SIZE_BITS 15 struct uint24 { uint16_t lo; uint8_t hi; } __packed; /* This header is shared by both compressed and decompressed chunks */ struct zbm_chunk_hdr { struct uint24 len; /* Length of the chunk */ struct uint24 decmp_len; /* Length of the chunk after decompression */ } __packed; /* The full header for compressed chunks */ struct zbm_cmp_chunk_hdr { /* Shared with decompressed chunks */ struct zbm_chunk_hdr hdr; /* Offset for each of the three metadata areas */ struct uint24 meta_off_1; struct uint24 meta_off_2; struct uint24 meta_off_3; } __packed; /* Pointer to a half-byte */ struct nybl_ptr { uint8_t *addr; /* Address of the byte */ int nibble; /* Which of the two nibbles? */ }; /* 0-2 and 0xf are not real bitmap indexes */ #define ZBM_BITMAP_COUNT (16 - 1 - 3) #define ZBM_BITMAP_BASE 3 #define ZBM_BITMAP_BYTECNT 17 #define ZBM_MAX_PERIOD_BYTECNT 2 struct zbm_bmap { uint8_t bitmap; /* The bitmap */ uint8_t period_bytecnt; /* Read this many bytes to get the new period */ }; struct zbm_state { /* Updated during a chunk read */ uint8_t *dest; /* Write the next byte here */ size_t dest_left; /* Room left in destination buffer */ uint32_t written; /* Bytes written so far for current chunk */ uint16_t period; /* Repetition period for decompression, in bytes */ /* Updated right before a chunk read */ const uint8_t *src_end; /* End of current chunk */ uint32_t len; /* Length of the chunk */ uint32_t decmp_len; /* Expected chunk length after decompression */ /* Updated after a chunk read */ const uint8_t *src; /* Start of buffer, or current chunk if any */ size_t src_left; /* Room left in the source buffer */ size_t prewritten; /* Bytes written for previous chunks */ /* Current position in data and metadata areas for this chunk */ const uint8_t *data; const uint8_t *meta_1; const uint8_t *meta_2; struct nybl_ptr meta_3; /* Array of bitmaps for the current chunk */ struct zbm_bmap bitmaps[ZBM_BITMAP_COUNT]; }; static int zbm_check_magic(struct zbm_state *state) { uint32_t magic; if(state->src_left < sizeof(magic)) return ZBM_INVAL; magic = *(uint32_t *)state->src; if(magic != ZBM_MAGIC) return ZBM_INVAL; state->src += sizeof(magic); state->src_left -= sizeof(magic); return 0; } static uint32_t zbm_u24_to_u32(struct uint24 n) { uint32_t res; res = n.hi; res <<= 16; res += n.lo; return res; } /* Some chunks just have regular uncompressed data, but with a header */ static int zbm_chunk_is_uncompressed(struct zbm_state *state) { return state->len == state->decmp_len + sizeof(struct zbm_chunk_hdr); } static int zbm_handle_uncompressed_chunk(struct zbm_state *state) { state->meta_1 = state->meta_2 = NULL; state->meta_3.addr = NULL; state->meta_3.nibble = 0; state->data = state->src + sizeof(struct zbm_chunk_hdr); memcpy(state->dest, state->data, state->decmp_len); state->dest += state->decmp_len; state->dest_left -= state->decmp_len; state->written = state->decmp_len; return 0; } static int zbm_read_nibble(struct nybl_ptr *nybl, const uint8_t *limit, uint8_t *result) { if(nybl->addr >= limit) return ZBM_INVAL; if(nybl->nibble == 0) { *result = *nybl->addr & 0xf; nybl->nibble = 1; } else { *result = (*nybl->addr >> 4) & 0xf; nybl->nibble = 0; ++nybl->addr; } return 0; } static void zbm_rewind_nibble(struct nybl_ptr *nybl) { if(nybl->nibble == 0) { nybl->nibble = 1; --nybl->addr; } else { nybl->nibble = 0; } } static int zbm_apply_bitmap(struct zbm_state *state, struct zbm_bmap *bitmap) { int i; /* The periods are stored in the first metadata area */ if(bitmap->period_bytecnt) { state->period = 0; for(i = 0; i < bitmap->period_bytecnt; ++i) { if(state->meta_1 >= state->src_end) return ZBM_INVAL; state->period |= *state->meta_1 << i * 8; ++state->meta_1; } } if(state->period == 0) return ZBM_INVAL; for(i = 0; i < 8; ++i) { if(state->written == state->decmp_len) break; if(bitmap->bitmap & 1 << i) { if(state->data >= state->src_end) return ZBM_INVAL; *state->dest = *state->data; ++state->data; } else { if(state->prewritten + state->written < state->period) return ZBM_INVAL; *state->dest = *(state->dest - state->period); } ++state->dest; --state->dest_left; ++state->written; } return 0; } static int zbm_apply_bitmap_number(struct zbm_state *state, uint8_t bmp_num) { struct zbm_bmap next = {0}; /* Not a valid bitmap number (it signals a repetition) */ if(bmp_num == 0xf) return ZBM_INVAL; /* An actual index in the bitmap array */ if(bmp_num > ZBM_MAX_PERIOD_BYTECNT) return zbm_apply_bitmap(state, &state->bitmaps[bmp_num - ZBM_BITMAP_BASE]); /* For < 2, use the next bitmap in the second metadata area */ if(state->meta_2 >= state->src_end) return ZBM_INVAL; next.bitmap = *state->meta_2; next.period_bytecnt = bmp_num; ++state->meta_2; return zbm_apply_bitmap(state, &next); } /* Find out how many times we need to repeat the current bitmap operation */ static int zbm_read_repetition_count(struct zbm_state *state, uint16_t *repeat) { uint8_t nibble; uint16_t total; int err; /* Don't confuse the trailing bitmaps with a repetition count */ if(state->decmp_len - state->written <= 8) { *repeat = 1; return 0; } err = zbm_read_nibble(&state->meta_3, state->src_end, &nibble); if(err) return err; if(nibble != 0xf) { /* No repetition count: the previous bitmap number gets applied once */ zbm_rewind_nibble(&state->meta_3); *repeat = 1; return 0; } /* * Under this scheme, repeating a bitmap number 3 times wouldn't save any * space, so the repetition count starts from 4. */ total = 4; while(nibble == 0xf) { err = zbm_read_nibble(&state->meta_3, state->src_end, &nibble); if(err) return err; total += nibble; if(total < nibble) return ZBM_INVAL; } *repeat = total; return 0; } static int zbm_decompress_single_bitmap(struct zbm_state *state) { uint8_t bmp_num; uint16_t repeat; int i; int err; /* The current nibble is the offset of the next bitmap to apply */ err = zbm_read_nibble(&state->meta_3, state->src_end, &bmp_num); if(err) return err; err = zbm_read_repetition_count(state, &repeat); if(err) return err; for(i = 0; i < repeat; ++i) { err = zbm_apply_bitmap_number(state, bmp_num); if(err) return err; } return 0; } /* Pointer to a bit */ struct bit_ptr { uint8_t *addr; /* Address of the byte */ int offset; /* Bit number */ }; /* This function does not perform boundary checks, the caller must do it */ static int zbm_read_single_bit(struct bit_ptr *bit) { int res = *bit->addr >> bit->offset & 1; ++bit->offset; if(bit->offset != 8) return res; bit->offset = 0; ++bit->addr; return res; } static int zbm_read_single_bitmap(struct bit_ptr *bit, const uint8_t *limit, struct zbm_bmap *result) { int i; result->bitmap = 0; result->period_bytecnt = 0; /* The bitmap itself */ for(i = 0; i < 8; ++i) { if(bit->addr >= limit) return ZBM_INVAL; result->bitmap |= zbm_read_single_bit(bit) << i; } /* * The two trailing bits tell us how many bytes to read for the next * repetition period */ for(i = 0; i < 2; ++i) { if(bit->addr >= limit) return ZBM_INVAL; result->period_bytecnt |= zbm_read_single_bit(bit) << i; } return 0; } static int zbm_read_bitmaps(struct zbm_state *state) { struct bit_ptr bmap = {0}; int err, i; if(state->len < ZBM_BITMAP_BYTECNT) return ZBM_INVAL; bmap.addr = (uint8_t *)state->src_end - ZBM_BITMAP_BYTECNT; bmap.offset = 0; for(i = 0; i < ZBM_BITMAP_COUNT; ++i) { err = zbm_read_single_bitmap(&bmap, state->src_end, &state->bitmaps[i]); if(err) return err; if(state->bitmaps[i].period_bytecnt > ZBM_MAX_PERIOD_BYTECNT) return ZBM_INVAL; } return 0; } static int zbm_handle_compressed_chunk(struct zbm_state *state) { const struct zbm_cmp_chunk_hdr *hdr = NULL; uint32_t meta_off_1, meta_off_2, meta_off_3; int err; state->written = 0; state->period = 8; if(state->len < sizeof(*hdr)) return ZBM_INVAL; hdr = (struct zbm_cmp_chunk_hdr *)state->src; state->data = state->src + sizeof(*hdr); meta_off_1 = zbm_u24_to_u32(hdr->meta_off_1); meta_off_2 = zbm_u24_to_u32(hdr->meta_off_2); meta_off_3 = zbm_u24_to_u32(hdr->meta_off_3); if(meta_off_1 >= state->len || meta_off_2 >= state->len || meta_off_3 >= state->len) return ZBM_INVAL; state->meta_1 = state->src + meta_off_1; state->meta_2 = state->src + meta_off_2; state->meta_3.addr = (uint8_t *)state->src + meta_off_3; state->meta_3.nibble = 0; err = zbm_read_bitmaps(state); if(err) return err; while(state->written < state->decmp_len) { err = zbm_decompress_single_bitmap(state); if(err) return err; } return 0; } static int zbm_handle_chunk(struct zbm_state *state) { const struct zbm_chunk_hdr *decmp_hdr = NULL; if(state->src_left < sizeof(*decmp_hdr)) return ZBM_INVAL; decmp_hdr = (struct zbm_chunk_hdr *)state->src; state->len = zbm_u24_to_u32(decmp_hdr->len); if(state->len > state->src_left) return ZBM_INVAL; state->src_end = state->src + state->len; state->decmp_len = zbm_u24_to_u32(decmp_hdr->decmp_len); if(state->decmp_len > ZBM_MAX_DECMP_CHUNK_SIZE) return ZBM_INVAL; if(!state->dest) /* We just wanted the length, so we are done */ return 0; if(state->decmp_len > state->dest_left) return ZBM_RANGE; if(zbm_chunk_is_uncompressed(state)) return zbm_handle_uncompressed_chunk(state); return zbm_handle_compressed_chunk(state); } int zbm_decompress(void *dest, size_t dest_size, const void *src, size_t src_size, size_t *out_len) { struct zbm_state state = {0}; int err; state.src = src; state.src_left = src_size; state.dest = dest; state.dest_left = dest_size; state.prewritten = 0; err = zbm_check_magic(&state); if(err) return err; /* The final chunk has zero decompressed length */ do { err = zbm_handle_chunk(&state); if(err) return err; state.src += state.len; state.src_left -= state.len; state.prewritten += state.decmp_len; } while(state.decmp_len != 0); *out_len = state.prewritten; return 0; } #define ZBM_MAX_BITMAP_COUNT (ZBM_MAX_DECMP_CHUNK_SIZE >> 3) #define ZBM_POSSIBLE_BMPROTS (1 << 10) struct zbm_compress_state { /* Updated during a chunk write */ const uint8_t *src; /* Next byte to read */ uint32_t read; /* Bytes processed so far for current chunk */ uint32_t written; /* Bytes written so far for current chunk */ uint8_t *dest; /* Write the next byte here */ uint16_t period; /* Repetition period for compression, in bytes */ /* Updated right before a chunk write */ const uint8_t *dest_end; /* Maximum limit for the current chunk */ const uint8_t *src_end; /* End of current decompressed chunk */ uint32_t decmp_len; /* Decompressed length of the chunk */ /* Updated after a chunk write */ size_t dest_left; /* Room left in destination buffer */ size_t preread; /* Bytes processed for previous chunks */ size_t prewritten; /* Bytes written for previous chunks */ size_t src_left; /* Room left in the source buffer */ /* Array of all bitmaps applied for the current chunk */ struct zbm_bmap bitmaps[ZBM_MAX_BITMAP_COUNT]; uint16_t periods[ZBM_MAX_BITMAP_COUNT]; int bmp_cnt; /* How many times was each bitmap-rotation combination applied? */ uint64_t usecnts[ZBM_POSSIBLE_BMPROTS]; /* Array of bitmaps applied most often */ struct zbm_bmap top_bitmaps[ZBM_BITMAP_COUNT]; }; /* Internal error used when a compression attempt was ineffective */ #define ZBM_CANT_COMPRESS (-1024) static int zbm_bmprot(struct zbm_bmap *bmap) { return (int)bmap->bitmap << 2 | (int)bmap->period_bytecnt; } static int zbm_write_magic(struct zbm_compress_state *state) { uint32_t *magic_p; if(state->dest_left < sizeof(*magic_p)) return ZBM_INVAL; magic_p = (uint32_t *)state->dest; *magic_p = ZBM_MAGIC; state->dest += sizeof(*magic_p); state->dest_left -= sizeof(*magic_p); state->prewritten += sizeof(*magic_p); return 0; } static int zbm_build_uncompressed_chunk(struct zbm_compress_state *state) { /* Undo the previous compression attempt */ state->src -= state->read; state->read = 0; state->dest -= state->written - sizeof(struct zbm_chunk_hdr); state->written = sizeof(struct zbm_chunk_hdr); memcpy(state->dest, state->src, state->decmp_len); state->src += state->decmp_len; state->read += state->decmp_len; state->written += state->decmp_len; state->dest += state->decmp_len; return 0; } static uint8_t zbm_compare_bytes(uint64_t bytes1, uint64_t bytes2) { static const uint64_t mask0 = 0xff; static const uint64_t mask1 = 0xff00; static const uint64_t mask2 = 0xff0000; static const uint64_t mask3 = 0xff000000; static const uint64_t mask4 = 0xff00000000; static const uint64_t mask5 = 0xff0000000000; static const uint64_t mask6 = 0xff000000000000; static const uint64_t mask7 = 0xff00000000000000; uint8_t diff = 0; uint64_t xor; xor = bytes1 ^ bytes2; diff += (xor & mask0) != 0; diff += (xor & mask1) != 0; diff += (xor & mask2) != 0; diff += (xor & mask3) != 0; diff += (xor & mask4) != 0; diff += (xor & mask5) != 0; diff += (xor & mask6) != 0; diff += (xor & mask7) != 0; return diff; } static void zbm_append_new_bitmap(struct zbm_compress_state *state, uint8_t bitmap, uint16_t period) { struct zbm_bmap *new_bmp = NULL; new_bmp = &state->bitmaps[state->bmp_cnt]; new_bmp->bitmap = bitmap; state->periods[state->bmp_cnt] = period; if(period == state->period) new_bmp->period_bytecnt = 0; else if(period <= 0xff) new_bmp->period_bytecnt = 1; else new_bmp->period_bytecnt = 2; ++state->bmp_cnt; ++state->usecnts[zbm_bmprot(new_bmp)]; state->period = period; } static uint8_t zbm_calculate_bitmap(const uint8_t *bytes1, const uint8_t *bytes2, uint8_t size) { uint8_t bitmap; uint8_t i; bitmap = 0; for(i = 0; i < size; ++i) { if(bytes1[i] != bytes2[i]) bitmap |= 1 << i; } return bitmap; } static void zbm_find_good_pattern(struct zbm_compress_state *state) { uint64_t needle; const uint8_t *to_compare = NULL; const uint8_t *next = NULL; const uint8_t *best = NULL; const uint8_t *split = NULL; int bytecnt, diff, best_cost; uint8_t bitmap; int i; bytecnt = MIN(8, state->decmp_len - state->read); needle = 0; for(i = 0; i < bytecnt; ++i) needle |= (uint64_t)state->src[i] << i * 8; /* * We'll estimate the cost of a period as the number of digits needed to * store it plus the number of characters that get changed. This isn't * perfect of course, because bitmap reuse is also a good idea. */ diff = zbm_compare_bytes(*(uint64_t *)(state->src - state->period), needle); best = state->src - state->period; best_cost = diff; if(best_cost <= 1) goto done; /* We first look for patterns that can be reached with a 1-byte period */ to_compare = state->src - MIN(0xff, state->preread + state->read); split = to_compare; while(to_compare <= state->src - 8) { diff = zbm_compare_bytes(*(uint64_t *)to_compare, needle); if(diff + 1 < best_cost) { best = to_compare; best_cost = diff + 1; if(best_cost == 1) goto done; } ++to_compare; } if(best_cost == 2) goto done; /* * We now look for patterns that can be reached with a 2-byte period, but * we focus only on the ones that start with the same byte. Checking * everything would take too long. */ to_compare = state->src - MIN(0xffff, state->preread + state->read); while(to_compare < split) { next = memchr(to_compare, state->src[0], split - to_compare); if(!next) break; diff = zbm_compare_bytes(*(uint64_t *)next, needle); if(diff + 2 < best_cost) { best = next; best_cost = diff + 2; if(best_cost == 2) goto done; } to_compare = next + 1; } done: bitmap = zbm_calculate_bitmap(best, state->src, bytecnt); zbm_append_new_bitmap(state, bitmap, state->src - best); } static int zbm_write_new_data(struct zbm_compress_state *state) { struct zbm_bmap *bmap = &state->bitmaps[state->bmp_cnt - 1]; int i; for(i = 0; i < 8; ++i) { if(state->src == state->src_end) break; if(state->dest == state->dest_end) return ZBM_CANT_COMPRESS; if(bmap->bitmap & 1 << i) { *state->dest = *state->src; ++state->dest; ++state->written; } ++state->src; ++state->read; } return 0; } static int zbm_compress_eight_bytes(struct zbm_compress_state *state) { zbm_find_good_pattern(state); return zbm_write_new_data(state); } static int zbm_build_initial_bitmap(struct zbm_compress_state *state) { struct zbm_bmap *init = &state->bitmaps[0]; init->bitmap = 0xff; init->period_bytecnt = 0; state->bmp_cnt = 1; state->usecnts[zbm_bmprot(init)] = 1; if(state->decmp_len - state->read < 8) return ZBM_CANT_COMPRESS; memcpy(state->dest, state->src, 8); state->dest += 8; state->src += 8; state->read += 8; state->written += 8; return 0; } static int zbm_write_metadata_1(struct zbm_compress_state *state) { struct zbm_bmap *bmap = NULL; uint16_t period; int i; /* The first metadata area stores the periods, when they change */ for(i = 0; i < state->bmp_cnt; ++i) { bmap = &state->bitmaps[i]; period = state->periods[i]; if(bmap->period_bytecnt == 0) continue; if(state->dest == state->dest_end) return ZBM_CANT_COMPRESS; *state->dest = period; ++state->dest; ++state->written; if(bmap->period_bytecnt == 1) continue; if(state->dest == state->dest_end) return ZBM_CANT_COMPRESS; *state->dest = period >> 8; ++state->dest; ++state->written; } return 0; } static int zbm_write_metadata_2(struct zbm_compress_state *state) { struct zbm_bmap *bmap = NULL; int i; /* The second metadata area stores most of the bitmaps */ for(i = 0; i < state->bmp_cnt; ++i) { bmap = &state->bitmaps[i]; /* This is one of the top bitmaps that go in the end of the chunk */ if(state->usecnts[zbm_bmprot(bmap)] == 0) continue; if(state->dest == state->dest_end) return ZBM_CANT_COMPRESS; *state->dest = bmap->bitmap; ++state->dest; ++state->written; } return 0; } static int zbm_equal_bmaps(struct zbm_bmap *bmap1, struct zbm_bmap *bmap2) { if(!bmap1 || !bmap2) return 0; if(bmap1->bitmap != bmap2->bitmap) return 0; if(bmap1->period_bytecnt != bmap2->period_bytecnt) return 0; return 1; } static int zbm_bmap_num_for_index(struct zbm_compress_state *state, int idx) { struct zbm_bmap *bmap = NULL; struct zbm_bmap *top_bmap = NULL; int i; bmap = &state->bitmaps[idx]; /* This is one of the top bitmaps that go in the end of the chunk */ if(state->usecnts[zbm_bmprot(bmap)] == 0) { for(i = 0; i < ZBM_BITMAP_COUNT; ++i) { top_bmap = &state->top_bitmaps[i]; if(zbm_equal_bmaps(top_bmap, bmap)) return i + 3; } } /* This is a regular bitmap from the second metadata area */ return bmap->period_bytecnt; } static int zbm_bmap_num_and_repcount(struct zbm_compress_state *state, int idx, int *repeat) { int bmap_num, next_num; int i; bmap_num = zbm_bmap_num_for_index(state, idx); *repeat = 1; for(i = idx + 1; i < state->bmp_cnt; ++i) { next_num = zbm_bmap_num_for_index(state, i); if(next_num != bmap_num) break; *repeat += 1; } return bmap_num; } static int zbm_write_nibble(struct nybl_ptr *nybl, const uint8_t *limit, uint8_t val) { if(nybl->addr >= limit) return ZBM_CANT_COMPRESS; if(nybl->nibble == 0) { *nybl->addr = val; nybl->nibble = 1; } else { *nybl->addr |= val << 4; nybl->nibble = 0; ++nybl->addr; } return 0; } static int zbm_write_repetition_count(struct nybl_ptr *nybl, const uint8_t *limit, int repeat) { int nibble = 0xf; int err; /* 0xf marks that this is a repetition */ err = zbm_write_nibble(nybl, limit, nibble); if(err) return err; /* We count from the minimum possible repetition count */ repeat -= 4; while(repeat > 0) { nibble = MIN(repeat, 0xf); err = zbm_write_nibble(nybl, limit, nibble); if(err) return err; repeat -= nibble; } /* The repetition must always end in a non-0xf nibble */ if(nibble == 0xf) { err = zbm_write_nibble(nybl, limit, 0); if(err) return err; } return 0; } static int zbm_write_metadata_3(struct zbm_compress_state *state) { struct nybl_ptr nybl = {0}; int to_write, repeat; int i, j; int err; nybl.addr = state->dest; nybl.nibble = 0; for(i = 0; i < state->bmp_cnt; i += repeat) { to_write = zbm_bmap_num_and_repcount(state, i, &repeat); err = zbm_write_nibble(&nybl, state->dest_end, to_write); if(err) return err; /* Fewer than 3 repetitions are done trivially */ if(repeat <= 3) { for(j = 1; j < repeat; ++j) { err = zbm_write_nibble(&nybl, state->dest_end, to_write); if(err) return err; } } else { err = zbm_write_repetition_count(&nybl, state->dest_end, repeat); if(err) return err; } } /* Leave the trailing nibble alone and move on to the next byte */ if(nybl.nibble == 1) { ++nybl.addr; nybl.nibble = 0; } state->written += nybl.addr - state->dest; state->dest = nybl.addr; return 0; } /* This function does not perform boundary checks, the caller must do it */ static void zbm_write_single_bit(struct bit_ptr *bit, int val) { *bit->addr |= val << bit->offset; ++bit->offset; if(bit->offset != 8) return; bit->offset = 0; ++bit->addr; return; } /* This function does not perform boundary checks, the caller must do it */ static void zbm_write_single_bitmap(struct bit_ptr *bit, struct zbm_bmap bmap) { int i; /* The bitmap itself */ for(i = 0; i < 8; ++i) zbm_write_single_bit(bit, bmap.bitmap >> i & 1); /* The trailing bits for the period bytecount */ for(i = 0; i < 2; ++i) zbm_write_single_bit(bit, bmap.period_bytecnt >> i & 1); } static int zbm_write_trailing_bitmaps(struct zbm_compress_state *state) { struct bit_ptr bmap = {0}; int i; if(state->dest_end - state->dest < ZBM_BITMAP_BYTECNT) return ZBM_CANT_COMPRESS; memset(state->dest, 0, ZBM_BITMAP_BYTECNT); bmap.addr = state->dest; bmap.offset = 0; for(i = 0; i < ZBM_BITMAP_COUNT; ++i) zbm_write_single_bitmap(&bmap, state->top_bitmaps[i]); state->dest += ZBM_BITMAP_BYTECNT; state->written += ZBM_BITMAP_BYTECNT; return 0; } static struct uint24 zbm_u32_to_u24(uint32_t n) { struct uint24 res; res.lo = n; res.hi = n >> 16; return res; } struct top_bmap { struct zbm_bmap *bmap; uint64_t usecnt; }; /* The array is sorted by use count, in descending order */ static void zbm_insert_in_top_bmaps(struct zbm_bmap *bmap, uint64_t usecnt, struct top_bmap tops[ZBM_BITMAP_COUNT]) { int i; for(i = 0; i < ZBM_BITMAP_COUNT; ++i) { if(zbm_equal_bmaps(tops[i].bmap, bmap)) /* Already in the array */ return; if(tops[i].usecnt < usecnt) break; } if(i == ZBM_BITMAP_COUNT) return; memmove(&tops[i + 1], &tops[i], (ZBM_BITMAP_COUNT - i - 1) * sizeof(tops[i])); tops[i].bmap = bmap; tops[i].usecnt = usecnt; } static void zbm_find_most_common_bitmaps(struct zbm_compress_state *state) { struct top_bmap tops[ZBM_BITMAP_COUNT] = {0}; struct zbm_bmap *bmap = NULL; int i; for(i = 0; i < state->bmp_cnt; ++i) { bmap = &state->bitmaps[i]; zbm_insert_in_top_bmaps(bmap, state->usecnts[zbm_bmprot(bmap)], tops); bmap = NULL; } for(i = 0; i < ZBM_BITMAP_COUNT; ++i) { if(tops[i].usecnt == 0) { /* Very few bitmaps, all are top */ state->top_bitmaps[i].bitmap = 0; state->top_bitmaps[i].period_bytecnt = 0; continue; } bmap = tops[i].bmap; state->top_bitmaps[i] = *bmap; /* Mark this as unused for the second metadata area */ state->usecnts[zbm_bmprot(bmap)] = 0; } } static int zbm_build_metadata(struct zbm_compress_state *state, struct zbm_cmp_chunk_hdr *hdr) { int err; zbm_find_most_common_bitmaps(state); hdr->meta_off_1 = zbm_u32_to_u24(state->written); err = zbm_write_metadata_1(state); if(err) return err; hdr->meta_off_2 = zbm_u32_to_u24(state->written); err = zbm_write_metadata_2(state); if(err) return err; hdr->meta_off_3 = zbm_u32_to_u24(state->written); err = zbm_write_metadata_3(state); if(err) return err; return zbm_write_trailing_bitmaps(state); } static int zbm_build_compressed_chunk(struct zbm_compress_state *state) { struct zbm_cmp_chunk_hdr *hdr = NULL; int err; if(sizeof(*hdr) > (size_t)(state->dest_end - state->dest)) return ZBM_CANT_COMPRESS; state->dest -= sizeof(hdr->hdr); state->written -= sizeof(hdr->hdr); hdr = (struct zbm_cmp_chunk_hdr *)state->dest; state->dest += sizeof(*hdr); state->written += sizeof(*hdr); memset(state->bitmaps, 0, sizeof(state->bitmaps)); memset(state->periods, 0, sizeof(state->bitmaps)); state->bmp_cnt = 0; memset(state->usecnts, 0, sizeof(state->usecnts)); memset(state->top_bitmaps, 0, sizeof(state->top_bitmaps)); if(state->preread == 0) { err = zbm_build_initial_bitmap(state); if(err) return err; } state->period = 8; while(state->read < state->decmp_len) { err = zbm_compress_eight_bytes(state); if(err) return err; } return zbm_build_metadata(state, hdr); } static int zbm_compress_single_chunk(struct zbm_compress_state *state) { struct zbm_chunk_hdr *decmp_hdr = NULL; unsigned int max_chunk_sz; int err; state->read = 0; state->written = 0; state->decmp_len = MIN(state->src_left, ZBM_MAX_DECMP_CHUNK_SIZE); state->src_end = state->src + state->decmp_len; /* If a chunk gets bigger than this, we'll just store it uncompressed */ max_chunk_sz = state->decmp_len + sizeof(*decmp_hdr); if(max_chunk_sz > state->dest_left) return ZBM_RANGE; state->dest_end = state->dest + max_chunk_sz; decmp_hdr = (struct zbm_chunk_hdr *)state->dest; state->dest += sizeof(*decmp_hdr); state->written += sizeof(*decmp_hdr); err = zbm_build_compressed_chunk(state); if(err == ZBM_CANT_COMPRESS) err = zbm_build_uncompressed_chunk(state); if(err) return err; decmp_hdr->len = zbm_u32_to_u24(state->written); decmp_hdr->decmp_len = zbm_u32_to_u24(state->decmp_len); return 0; } static int zmb_max_compressed_length(size_t src_size, size_t *out_len) { size_t max_chunk_size = sizeof(struct zbm_chunk_hdr) + 0x8000; size_t chunk_count; chunk_count = (src_size + ZBM_MAX_DECMP_CHUNK_SIZE - 1) >> ZBM_MAX_DECMP_CHUNK_SIZE_BITS; if(chunk_count << ZBM_MAX_DECMP_CHUNK_SIZE_BITS < src_size) return ZBM_OVERFLOW; /* The magic, all the chunks, and the terminating empty chunk */ *out_len = sizeof(uint32_t) + chunk_count * max_chunk_size + ZBM_LAST_CHUNK_SIZE; if(*out_len < src_size) return ZBM_OVERFLOW; return 0; } int zbm_compress(void *dest, size_t dest_size, const void *src, size_t src_size, size_t *out_len) { struct zbm_compress_state *state = NULL; int err = 0; if(!dest) return zmb_max_compressed_length(src_size, out_len); state = calloc(1, sizeof(*state)); if(!state) return ZBM_NOMEM; state->src = src; state->src_left = src_size; state->dest = dest; state->dest_left = dest_size; state->preread = 0; state->prewritten = 0; err = zbm_write_magic(state); if(err) goto fail; do { err = zbm_compress_single_chunk(state); if(err) goto fail; state->src_left -= state->read; state->dest_left -= state->written; state->preread += state->decmp_len; state->prewritten += state->written; } while(state->read != 0); *out_len = state->prewritten; fail: free(state); return err; } int zbm_compress_chunk(void *dest, size_t dest_size, const void *src, size_t src_size, size_t index, size_t *out_len) { struct zbm_compress_state *state = NULL; size_t offset; int err = 0; state = calloc(1, sizeof(*state)); if(!state) return ZBM_NOMEM; state->src = src; state->src_left = src_size; state->dest = dest; state->dest_left = dest_size; state->preread = 0; state->prewritten = 0; if(index == 0) { err = zbm_write_magic(state); if(err) goto fail; } offset = index << ZBM_MAX_DECMP_CHUNK_SIZE_BITS; if(offset >> ZBM_MAX_DECMP_CHUNK_SIZE_BITS != index) return ZBM_OVERFLOW; if(state->src_left < offset) { state->preread = state->src_left; state->src += state->src_left; state->src_left = 0; } else { state->preread = offset; state->src += offset; state->src_left -= offset; } err = zbm_compress_single_chunk(state); if(err) goto fail; state->src_left -= state->read; state->dest_left -= state->written; state->preread += state->decmp_len; state->prewritten += state->written; *out_len = state->prewritten; fail: free(state); return err; }