Compress-Snappy

 view release on metacpan or  search on metacpan

src/csnappy_compress.c  view on Meta::CPAN

	return op + length;
}

static uint8_t* emit_copy(
	uint8_t *op,
	uint32_t offset,
	uint32_t len)
{
	DCHECK_GT(offset, 0);
	
	/* Emit 64 byte copies but make sure to keep at least four bytes
	 * reserved */
	while (unlikely(len >= 68)) {
		*op++ = COPY_2_BYTE_OFFSET | ((64 - 1) << 2);
		*op++ = offset & 255;
		*op++ = offset >> 8;
		len -= 64;
	}

	/* Emit an extra 60 byte copy if have too much data to fit in one
	 * copy */
	if (unlikely(len > 64)) {
		*op++ = COPY_2_BYTE_OFFSET | ((60 - 1) << 2);
		*op++ = offset & 255;
		*op++ = offset >> 8;
		len -= 60;
	}

	/* Emit remainder */
	DCHECK_GE(len, 4);
	if ((len < 12) && (offset < 2048)) {
		int len_minus_4 = len - 4;
		*op++ = COPY_1_BYTE_OFFSET   |
			((len_minus_4) << 2) |
			((offset >> 8) << 5);
		*op++ = offset & 0xff;
	} else {
		*op++ = COPY_2_BYTE_OFFSET | ((len-1) << 2);
		*op++ = offset & 255;
		*op++ = offset >> 8;
	}
	return op;
}

static uint32_t find_match_length(
	const uint8_t *s1,
	const uint8_t *s2,
	const uint8_t *s2_end)
{
	const uint8_t * const s2_start = s2;
	while (s2 < s2_end && *s1++ == *s2++) /*nothing*/;
	return s2 - s2_start - 1;
}

static uint32_t hash(uint32_t v)
{
	return v * UINT32_C(0x1e35a7bd);
}

char*
csnappy_compress_fragment(
	const char *input,
	const uint32_t input_size,
	char *dst,
	void *working_memory,
	const int workmem_bytes_power_of_two)
{
	const uint8_t * const src_start = (const uint8_t *)input;
	const uint8_t * const src_end_minus4 = src_start + input_size - 4;
	const uint8_t *src = src_start, *done_upto = src_start, *match;
	uint8_t *op = (uint8_t *)dst;
	uint16_t *wm = (uint16_t *)working_memory;
	int shift = 33 - workmem_bytes_power_of_two;
	uint32_t curr_val, curr_hash, match_val, offset, length;
	if (unlikely(input_size < 4))
		goto the_end;
	memset(wm, 0, 1 << workmem_bytes_power_of_two);
	for (;;) {
		curr_val = (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
		do {
			src++;
			if (unlikely(src >= src_end_minus4))
				goto the_end;
			curr_val = (curr_val >> 8) | (src[3] << 24);
			DCHECK_EQ(curr_val, get_unaligned_le32(src));
			curr_hash = hash(curr_val) >> shift;
			match = src_start + wm[curr_hash];
			DCHECK_LT(match, src);
			wm[curr_hash] = src - src_start;
			match_val = get_unaligned_le32(match);
		} while (likely(curr_val != match_val));
		offset = src - match;
		length = 4 + find_match_length(
			match + 4, src + 4, src_end_minus4 + 4);
		DCHECK_EQ(memcmp(src, match, length), 0);
		op = emit_literal(op, done_upto, src);
		op = emit_copy(op, offset, length);
		done_upto = src + length;
		src = done_upto - 1;
	}
the_end:
	op = emit_literal(op, done_upto, src_end_minus4 + 4);
	return (char *)op;
}

#else /* !simple */

/*
 * Any hash function will produce a valid compressed bitstream, but a good
 * hash function reduces the number of collisions and thus yields better
 * compression for compressible input, and more speed for incompressible
 * input. Of course, it doesn't hurt if the hash function is reasonably fast
 * either, as it gets called a lot.
 */
static INLINE uint32_t HashBytes(uint32_t bytes, int shift)
{
	uint32_t kMul = 0x1e35a7bd;
	return (bytes * kMul) >> shift;
}
static INLINE uint32_t Hash(const char *p, int shift)
{

src/csnappy_compress.c  view on Meta::CPAN

	}

	/* Emit remainder */
	op = EmitCopyLessThan64(op, offset, len);
	return op;
}


/*
For 0 <= offset <= 4, GetUint32AtOffset(GetEightBytesAt(p), offset) will
equal UNALIGNED_LOAD32(p + offset).  Motivation: On x86-64 hardware we have
empirically found that overlapping loads such as
 UNALIGNED_LOAD32(p) ... UNALIGNED_LOAD32(p+1) ... UNALIGNED_LOAD32(p+2)
are slower than UNALIGNED_LOAD64(p) followed by shifts and casts to uint32.

We have different versions for 64- and 32-bit; ideally we would avoid the
two functions and just INLINE the UNALIGNED_LOAD64 call into
GetUint32AtOffset, but GCC (at least not as of 4.6) is seemingly not clever
enough to avoid loading the value multiple times then. For 64-bit, the load
is done when GetEightBytesAt() is called, whereas for 32-bit, the load is
done at GetUint32AtOffset() time.
*/

#if defined(__x86_64__) || (__SIZEOF_SIZE_T__ == 8)

typedef uint64_t EightBytesReference;

static INLINE EightBytesReference GetEightBytesAt(const char* ptr) {
	return UNALIGNED_LOAD64(ptr);
}

static INLINE uint32_t GetUint32AtOffset(uint64_t v, int offset) {
	DCHECK_GE(offset, 0);
	DCHECK_LE(offset, 4);
#if __BYTE_ORDER == __LITTLE_ENDIAN
	return v >> (8 * offset);
#else
	return v >> (32 - 8 * offset);
#endif
}

#else /* !ARCH_K8 */

typedef const char* EightBytesReference;

static INLINE EightBytesReference GetEightBytesAt(const char* ptr) {
	return ptr;
}

static INLINE uint32_t GetUint32AtOffset(const char* v, int offset) {
	DCHECK_GE(offset, 0);
	DCHECK_LE(offset, 4);
	return UNALIGNED_LOAD32(v + offset);
}

#endif /* !ARCH_K8 */


#define kInputMarginBytes 15
char*
csnappy_compress_fragment(
	const char *input,
	const uint32_t input_size,
	char *op,
	void *working_memory,
	const int workmem_bytes_power_of_two)
{
	const char *ip, *ip_end, *base_ip, *next_emit, *ip_limit, *next_ip,
			*candidate, *base;
	uint16_t *table = (uint16_t *)working_memory;
	EightBytesReference input_bytes;
	uint32_t hash, next_hash, prev_hash, cur_hash, skip, candidate_bytes;
	int shift, matched;

	DCHECK_GE(workmem_bytes_power_of_two, 9);
	DCHECK_LE(workmem_bytes_power_of_two, 15);
	/* Table of 2^X bytes, need (X-1) bits to address table of uint16_t.
	 * How many bits of 32bit hash function result are discarded? */
	shift = 33 - workmem_bytes_power_of_two;
	/* "ip" is the input pointer, and "op" is the output pointer. */
	ip = input;
	DCHECK_LE(input_size, kBlockSize);
	ip_end = input + input_size;
	base_ip = ip;
	/* Bytes in [next_emit, ip) will be emitted as literal bytes. Or
	   [next_emit, ip_end) after the main loop. */
	next_emit = ip;

	if (unlikely(input_size < kInputMarginBytes))
		goto emit_remainder;

	memset(working_memory, 0, 1 << workmem_bytes_power_of_two);

	ip_limit = input + input_size - kInputMarginBytes;
	next_hash = Hash(++ip, shift);

main_loop:
	DCHECK_LT(next_emit, ip);
	/*
	* The body of this loop calls EmitLiteral once and then EmitCopy one or
	* more times. (The exception is that when we're close to exhausting
	* the input we goto emit_remainder.)
	*
	* In the first iteration of this loop we're just starting, so
	* there's nothing to copy, so calling EmitLiteral once is
	* necessary. And we only start a new iteration when the
	* current iteration has determined that a call to EmitLiteral will
	* precede the next call to EmitCopy (if any).
	*
	* Step 1: Scan forward in the input looking for a 4-byte-long match.
	* If we get close to exhausting the input then goto emit_remainder.
	*
	* Heuristic match skipping: If 32 bytes are scanned with no matches
	* found, start looking only at every other byte. If 32 more bytes are
	* scanned, look at every third byte, etc.. When a match is found,
	* immediately go back to looking at every byte. This is a small loss
	* (~5% performance, ~0.1% density) for compressible data due to more
	* bookkeeping, but for non-compressible data (such as JPEG) it's a huge
	* win since the compressor quickly "realizes" the data is incompressible
	* and doesn't bother looking for matches everywhere.
	*

src/csnappy_compress.c  view on Meta::CPAN


		table[hash] = ip - base_ip;
	} while (likely(UNALIGNED_LOAD32(ip) !=
			UNALIGNED_LOAD32(candidate)));

	/*
	* Step 2: A 4-byte match has been found. We'll later see if more
	* than 4 bytes match. But, prior to the match, input
	* bytes [next_emit, ip) are unmatched. Emit them as "literal bytes."
	*/
	DCHECK_LE(next_emit + 16, ip_end);
	op = EmitLiteral(op, next_emit, ip - next_emit, 1);

	/*
	* Step 3: Call EmitCopy, and then see if another EmitCopy could
	* be our next move. Repeat until we find no match for the
	* input immediately after what was consumed by the last EmitCopy call.
	*
	* If we exit this loop normally then we need to call EmitLiteral next,
	* though we don't yet know how big the literal will be. We handle that
	* by proceeding to the next iteration of the main loop. We also can exit
	* this loop via goto if we get close to exhausting the input.
	*/
	candidate_bytes = 0;

	do {
		/* We have a 4-byte match at ip, and no need to emit any
		 "literal bytes" prior to ip. */
		base = ip;
		matched = 4 + FindMatchLength(candidate + 4, ip + 4, ip_end);
		ip += matched;
		DCHECK_EQ(0, memcmp(base, candidate, matched));
		op = EmitCopy(op, base - candidate, matched);
		/* We could immediately start working at ip now, but to improve
		 compression we first update table[Hash(ip - 1, ...)]. */
		next_emit = ip;
		if (unlikely(ip >= ip_limit))
			goto emit_remainder;
		input_bytes = GetEightBytesAt(ip - 1);
		prev_hash = HashBytes(GetUint32AtOffset(input_bytes, 0), shift);
		table[prev_hash] = ip - base_ip - 1;
		cur_hash = HashBytes(GetUint32AtOffset(input_bytes, 1), shift);
		candidate = base_ip + table[cur_hash];
		candidate_bytes = UNALIGNED_LOAD32(candidate);
		table[cur_hash] = ip - base_ip;
	} while (GetUint32AtOffset(input_bytes, 1) == candidate_bytes);

	next_hash = HashBytes(GetUint32AtOffset(input_bytes, 2), shift);
	++ip;
	goto main_loop;

emit_remainder:
	/* Emit the remaining bytes as a literal */
	if (next_emit < ip_end)
		op = EmitLiteral(op, next_emit, ip_end - next_emit, 0);

	return op;
}
#endif /* !simple */
#if defined(__KERNEL__) && !defined(STATIC)
EXPORT_SYMBOL(csnappy_compress_fragment);
#endif

uint32_t __attribute__((const))
csnappy_max_compressed_length(uint32_t source_len)
{
	return 32 + source_len + source_len/6;
}
#if defined(__KERNEL__) && !defined(STATIC)
EXPORT_SYMBOL(csnappy_max_compressed_length);
#endif

void
csnappy_compress(
	const char *input,
	uint32_t input_length,
	char *compressed,
	uint32_t *compressed_length,
	void *working_memory,
	const int workmem_bytes_power_of_two)
{
	int workmem_size;
	int num_to_read;
	uint32_t written = 0;
	char *p = encode_varint32(compressed, input_length);
	written += (p - compressed);
	compressed = p;
	while (input_length > 0) {
		num_to_read = min(input_length, (uint32_t)kBlockSize);
		workmem_size = workmem_bytes_power_of_two;
		if (unlikely(num_to_read < kBlockSize)) {
			for (workmem_size = 9;
			     workmem_size < workmem_bytes_power_of_two;
			     ++workmem_size) {
				if ((1 << (workmem_size-1)) >= num_to_read)
					break;
			}
		}
		p = csnappy_compress_fragment(
				input, num_to_read, compressed,
				working_memory, workmem_size);
		written += (p - compressed);
		compressed = p;
		input_length -= num_to_read;
		input += num_to_read;
	}
	*compressed_length = written;
}
#if defined(__KERNEL__) && !defined(STATIC)
EXPORT_SYMBOL(csnappy_compress);

MODULE_LICENSE("BSD");
MODULE_DESCRIPTION("Snappy Compressor");
#endif



( run in 0.614 second using v1.01-cache-2.11-cpan-364913b4093 )