30 #include "snappy-internal.h" 31 #include "snappy-sinksource.h" 48 static inline uint32 HashBytes(uint32 bytes,
int shift) {
49 uint32 kMul = 0x1e35a7bd;
50 return (bytes * kMul) >> shift;
52 static inline uint32 Hash(
const char* p,
int shift) {
53 return HashBytes(UNALIGNED_LOAD32(p), shift);
56 size_t MaxCompressedLength(
size_t source_len) {
77 return 32 + source_len + source_len/6;
82 COPY_1_BYTE_OFFSET = 1,
83 COPY_2_BYTE_OFFSET = 2,
84 COPY_4_BYTE_OFFSET = 3
86 static const int kMaximumTagLength = 5;
99 static inline void IncrementalCopy(
const char* src,
char* op, int64_t len) {
139 const int kMaxIncrementCopyOverflow = 10;
141 inline void IncrementalCopyFastPath(
const char* src,
char* op, int64_t len) {
142 while (PREDICT_FALSE(op - src < 8)) {
143 UnalignedCopy64(src, op);
148 UnalignedCopy64(src, op);
157 static inline char* EmitLiteral(
char* op,
160 bool allow_fast_path) {
164 *op++ = LITERAL | (n << 2);
176 if (allow_fast_path && len <= 16) {
177 UnalignedCopy64(literal, op);
178 UnalignedCopy64(literal + 8, op + 8);
193 *base = LITERAL | ((59+count) << 2);
195 memcpy(op, literal, len);
199 static inline char* EmitCopyLessThan64(
char* op,
size_t offset,
int len) {
202 assert(offset < 65536);
204 if ((len < 12) && (offset < 2048)) {
205 size_t len_minus_4 = len - 4;
206 assert(len_minus_4 < 8);
207 *op++ = COPY_1_BYTE_OFFSET + ((len_minus_4) << 2) + ((offset >> 8) << 5);
208 *op++ = offset & 0xff;
210 *op++ = COPY_2_BYTE_OFFSET + ((len-1) << 2);
211 LittleEndian::Store16(op, offset);
217 static inline char* EmitCopy(
char* op,
size_t offset,
int len) {
219 while (PREDICT_FALSE(len >= 68)) {
220 op = EmitCopyLessThan64(op, offset, 64);
226 op = EmitCopyLessThan64(op, offset, 60);
231 op = EmitCopyLessThan64(op, offset, len);
236 bool GetUncompressedLength(
const char* start,
size_t n,
size_t* result) {
238 const char* limit = start + n;
239 if (Varint::Parse32WithLimit(start, limit, &v) != NULL) {
248 uint16* WorkingMemory::GetHashTable(
size_t input_size,
int* table_size) {
253 assert(kMaxHashTableSize >= 256);
255 while (htsize < kMaxHashTableSize && htsize < input_size) {
260 if (htsize <= ARRAYSIZE(small_table_)) {
261 table = small_table_;
263 if (large_table_ == NULL) {
264 large_table_ =
new uint16[kMaxHashTableSize];
266 table = large_table_;
269 *table_size = htsize;
270 memset(table, 0, htsize *
sizeof(*table));
290 typedef uint64 EightBytesReference;
292 static inline EightBytesReference GetEightBytesAt(
const char* ptr) {
293 return UNALIGNED_LOAD64(ptr);
296 static inline uint32 GetUint32AtOffset(uint64 v,
int offset) {
299 return v >> (LittleEndian::IsLittleEndian() ? 8 * offset : 32 - 8 * offset);
304 typedef const char* EightBytesReference;
306 static inline EightBytesReference GetEightBytesAt(
const char* ptr) {
310 static inline uint32 GetUint32AtOffset(
const char* v,
int offset) {
313 return UNALIGNED_LOAD32(v + offset);
330 char* CompressFragment(
const char* input,
334 const int table_size) {
336 const char* ip = input;
337 assert(input_size <= kBlockSize);
338 assert((table_size & (table_size - 1)) == 0);
339 const int shift = 32 - Bits::Log2Floor(table_size);
340 assert(static_cast<int>(kuint32max >> shift) == table_size - 1);
341 const char* ip_end = input + input_size;
342 const char* base_ip = ip;
345 const char* next_emit = ip;
347 const size_t kInputMarginBytes = 15;
348 if (PREDICT_TRUE(input_size >= kInputMarginBytes)) {
349 const char* ip_limit = input + input_size - kInputMarginBytes;
351 for (uint32 next_hash = Hash(++ip, shift); ; ) {
352 assert(next_emit < ip);
380 const char* next_ip = ip;
381 const char* candidate;
384 uint32 hash = next_hash;
385 assert(hash == Hash(ip, shift));
386 uint32 bytes_between_hash_lookups = skip++ >> 5;
387 next_ip = ip + bytes_between_hash_lookups;
388 if (PREDICT_FALSE(next_ip > ip_limit)) {
391 next_hash = Hash(next_ip, shift);
392 candidate = base_ip + table[hash];
393 assert(candidate >= base_ip);
394 assert(candidate < ip);
396 table[hash] = ip - base_ip;
397 }
while (PREDICT_TRUE(UNALIGNED_LOAD32(ip) !=
398 UNALIGNED_LOAD32(candidate)));
403 assert(next_emit + 16 <= ip_end);
404 op = EmitLiteral(op, next_emit, ip - next_emit,
true);
414 EightBytesReference input_bytes;
415 uint32 candidate_bytes = 0;
420 const char* base = ip;
421 int matched = 4 + FindMatchLength(candidate + 4, ip + 4, ip_end);
423 size_t offset = base - candidate;
424 assert(0 == memcmp(base, candidate, matched));
425 op = EmitCopy(op, offset, matched);
428 const char* insert_tail = ip - 1;
430 if (PREDICT_FALSE(ip >= ip_limit)) {
433 input_bytes = GetEightBytesAt(insert_tail);
434 uint32 prev_hash = HashBytes(GetUint32AtOffset(input_bytes, 0), shift);
435 table[prev_hash] = ip - base_ip - 1;
436 uint32 cur_hash = HashBytes(GetUint32AtOffset(input_bytes, 1), shift);
437 candidate = base_ip + table[cur_hash];
438 candidate_bytes = UNALIGNED_LOAD32(candidate);
439 table[cur_hash] = ip - base_ip;
440 }
while (GetUint32AtOffset(input_bytes, 1) == candidate_bytes);
442 next_hash = HashBytes(GetUint32AtOffset(input_bytes, 2), shift);
449 if (next_emit < ip_end) {
450 op = EmitLiteral(op, next_emit, ip_end - next_emit,
false);
502 static const uint32 wordmask[] = {
503 0u, 0xffu, 0xffffu, 0xffffffu, 0xffffffffu
517 static const uint16 char_table[256] = {
518 0x0001, 0x0804, 0x1001, 0x2001, 0x0002, 0x0805, 0x1002, 0x2002,
519 0x0003, 0x0806, 0x1003, 0x2003, 0x0004, 0x0807, 0x1004, 0x2004,
520 0x0005, 0x0808, 0x1005, 0x2005, 0x0006, 0x0809, 0x1006, 0x2006,
521 0x0007, 0x080a, 0x1007, 0x2007, 0x0008, 0x080b, 0x1008, 0x2008,
522 0x0009, 0x0904, 0x1009, 0x2009, 0x000a, 0x0905, 0x100a, 0x200a,
523 0x000b, 0x0906, 0x100b, 0x200b, 0x000c, 0x0907, 0x100c, 0x200c,
524 0x000d, 0x0908, 0x100d, 0x200d, 0x000e, 0x0909, 0x100e, 0x200e,
525 0x000f, 0x090a, 0x100f, 0x200f, 0x0010, 0x090b, 0x1010, 0x2010,
526 0x0011, 0x0a04, 0x1011, 0x2011, 0x0012, 0x0a05, 0x1012, 0x2012,
527 0x0013, 0x0a06, 0x1013, 0x2013, 0x0014, 0x0a07, 0x1014, 0x2014,
528 0x0015, 0x0a08, 0x1015, 0x2015, 0x0016, 0x0a09, 0x1016, 0x2016,
529 0x0017, 0x0a0a, 0x1017, 0x2017, 0x0018, 0x0a0b, 0x1018, 0x2018,
530 0x0019, 0x0b04, 0x1019, 0x2019, 0x001a, 0x0b05, 0x101a, 0x201a,
531 0x001b, 0x0b06, 0x101b, 0x201b, 0x001c, 0x0b07, 0x101c, 0x201c,
532 0x001d, 0x0b08, 0x101d, 0x201d, 0x001e, 0x0b09, 0x101e, 0x201e,
533 0x001f, 0x0b0a, 0x101f, 0x201f, 0x0020, 0x0b0b, 0x1020, 0x2020,
534 0x0021, 0x0c04, 0x1021, 0x2021, 0x0022, 0x0c05, 0x1022, 0x2022,
535 0x0023, 0x0c06, 0x1023, 0x2023, 0x0024, 0x0c07, 0x1024, 0x2024,
536 0x0025, 0x0c08, 0x1025, 0x2025, 0x0026, 0x0c09, 0x1026, 0x2026,
537 0x0027, 0x0c0a, 0x1027, 0x2027, 0x0028, 0x0c0b, 0x1028, 0x2028,
538 0x0029, 0x0d04, 0x1029, 0x2029, 0x002a, 0x0d05, 0x102a, 0x202a,
539 0x002b, 0x0d06, 0x102b, 0x202b, 0x002c, 0x0d07, 0x102c, 0x202c,
540 0x002d, 0x0d08, 0x102d, 0x202d, 0x002e, 0x0d09, 0x102e, 0x202e,
541 0x002f, 0x0d0a, 0x102f, 0x202f, 0x0030, 0x0d0b, 0x1030, 0x2030,
542 0x0031, 0x0e04, 0x1031, 0x2031, 0x0032, 0x0e05, 0x1032, 0x2032,
543 0x0033, 0x0e06, 0x1033, 0x2033, 0x0034, 0x0e07, 0x1034, 0x2034,
544 0x0035, 0x0e08, 0x1035, 0x2035, 0x0036, 0x0e09, 0x1036, 0x2036,
545 0x0037, 0x0e0a, 0x1037, 0x2037, 0x0038, 0x0e0b, 0x1038, 0x2038,
546 0x0039, 0x0f04, 0x1039, 0x2039, 0x003a, 0x0f05, 0x103a, 0x203a,
547 0x003b, 0x0f06, 0x103b, 0x203b, 0x003c, 0x0f07, 0x103c, 0x203c,
548 0x0801, 0x0f08, 0x103d, 0x203d, 0x1001, 0x0f09, 0x103e, 0x203e,
549 0x1801, 0x0f0a, 0x103f, 0x203f, 0x2001, 0x0f0b, 0x1040, 0x2040
555 DEFINE_bool(snappy_dump_decompression_table,
false,
556 "If true, we print the decompression table at startup.");
558 static uint16 MakeEntry(
unsigned int extra,
560 unsigned int copy_offset) {
562 assert(extra == (extra & 0x7));
563 assert(copy_offset == (copy_offset & 0x7));
564 assert(len == (len & 0x7f));
565 return len | (copy_offset << 8) | (extra << 11);
568 static void ComputeTable() {
573 for (
int i = 0; i < 256; i++) {
578 for (
unsigned int len = 1; len <= 60; len++) {
579 dst[LITERAL | ((len-1) << 2)] = MakeEntry(0, len, 0);
585 for (
unsigned int extra_bytes = 1; extra_bytes <= 4; extra_bytes++) {
588 dst[LITERAL | ((extra_bytes+59) << 2)] = MakeEntry(extra_bytes, 1, 0);
599 for (
unsigned int len = 4; len < 12; len++) {
600 for (
unsigned int offset = 0; offset < 2048; offset += 256) {
601 dst[COPY_1_BYTE_OFFSET | ((len-4)<<2) | ((offset>>8)<<5)] =
602 MakeEntry(1, len, offset>>8);
609 for (
unsigned int len = 1; len <= 64; len++) {
610 dst[COPY_2_BYTE_OFFSET | ((len-1)<<2)] = MakeEntry(2, len, 0);
616 for (
unsigned int len = 1; len <= 64; len++) {
617 dst[COPY_4_BYTE_OFFSET | ((len-1)<<2)] = MakeEntry(4, len, 0);
622 if (assigned != 256) {
623 fprintf(stderr,
"ComputeTable: assigned only %d of 256\n", assigned);
626 for (
int i = 0; i < 256; i++) {
627 if (dst[i] == 0xffff) {
628 fprintf(stderr,
"ComputeTable: did not assign byte %d\n", i);
633 if (FLAGS_snappy_dump_decompression_table) {
634 printf(
"static const uint16 char_table[256] = {\n ");
635 for (
int i = 0; i < 256; i++) {
638 ((i == 255) ?
"\n" : (((i%8) == 7) ?
",\n " :
", ")));
644 for (
int i = 0; i < 256; i++) {
645 if (dst[i] != char_table[i]) {
646 fprintf(stderr,
"ComputeTable: byte %d: computed (%x), expect (%x)\n",
647 i, static_cast<int>(dst[i]), static_cast<int>(char_table[i]));
659 const char* ip_limit_;
662 char scratch_[kMaximumTagLength];
683 reader_->Skip(peeked_);
694 bool ReadUncompressedLength(uint32* result) {
700 if (shift >= 32)
return false;
702 const char* ip = reader_->Peek(&n);
703 if (n == 0)
return false;
704 const unsigned char c = *(
reinterpret_cast<const unsigned char*
>(ip));
706 *result |=
static_cast<uint32
>(c & 0x7f) << shift;
717 template <
class Writer>
718 void DecompressAllTags(Writer* writer) {
719 const char* ip = ip_;
725 #define MAYBE_REFILL() \ 726 if (ip_limit_ - ip < kMaximumTagLength) { \ 728 if (!RefillTag()) return; \ 734 const unsigned char c = *(
reinterpret_cast<const unsigned char*
>(ip++));
736 if ((c & 0x3) == LITERAL) {
737 size_t literal_length = (c >> 2) + 1u;
738 if (writer->TryFastAppend(ip, ip_limit_ - ip, literal_length)) {
739 assert(literal_length < 61);
740 ip += literal_length;
746 if (PREDICT_FALSE(literal_length >= 61)) {
748 const size_t literal_length_length = literal_length - 60;
750 (LittleEndian::Load32(ip) & wordmask[literal_length_length]) + 1;
751 ip += literal_length_length;
754 size_t avail = ip_limit_ - ip;
755 while (avail < literal_length) {
756 if (!writer->Append(ip, avail))
return;
757 literal_length -= avail;
758 reader_->Skip(peeked_);
760 ip = reader_->Peek(&n);
763 if (avail == 0)
return;
764 ip_limit_ = ip + avail;
766 if (!writer->Append(ip, literal_length)) {
769 ip += literal_length;
772 const uint32 entry = char_table[c];
773 const uint32 trailer = LittleEndian::Load32(ip) & wordmask[entry >> 11];
774 const uint32 length = entry & 0xff;
780 const uint32 copy_offset = entry & 0x700;
781 if (!writer->AppendFromSelf(copy_offset + trailer, length)) {
792 bool SnappyDecompressor::RefillTag() {
793 const char* ip = ip_;
794 if (ip == ip_limit_) {
796 reader_->Skip(peeked_);
798 ip = reader_->Peek(&n);
808 assert(ip < ip_limit_);
809 const unsigned char c = *(
reinterpret_cast<const unsigned char*
>(ip));
810 const uint32 entry = char_table[c];
811 const uint32 needed = (entry >> 11) + 1;
812 assert(needed <=
sizeof(scratch_));
815 uint32 nbuf = ip_limit_ - ip;
821 memmove(scratch_, ip, nbuf);
822 reader_->Skip(peeked_);
824 while (nbuf < needed) {
826 const char* src = reader_->Peek(&length);
827 if (length == 0)
return false;
828 uint32 to_add = min<uint32>(needed - nbuf, length);
829 memcpy(scratch_ + nbuf, src, to_add);
831 reader_->Skip(to_add);
833 assert(nbuf == needed);
835 ip_limit_ = scratch_ + needed;
836 }
else if (nbuf < kMaximumTagLength) {
839 memmove(scratch_, ip, nbuf);
840 reader_->Skip(peeked_);
843 ip_limit_ = scratch_ + nbuf;
851 template <
typename Writer>
852 static bool InternalUncompress(
Source* r, Writer* writer) {
855 uint32 uncompressed_len = 0;
856 if (!decompressor.ReadUncompressedLength(&uncompressed_len))
return false;
857 return InternalUncompressAllTags(&decompressor, writer, uncompressed_len);
860 template <
typename Writer>
863 uint32 uncompressed_len) {
864 writer->SetExpectedLength(uncompressed_len);
867 decompressor->DecompressAllTags(writer);
869 return (decompressor->eof() && writer->CheckLength());
872 bool GetUncompressedLength(
Source* source, uint32* result) {
874 return decompressor.ReadUncompressedLength(result);
877 size_t Compress(
Source* reader,
Sink* writer) {
879 size_t N = reader->Available();
880 char ulength[Varint::kMax32];
881 char* p = Varint::Encode32(ulength, N);
882 writer->Append(ulength, p-ulength);
883 written += (p - ulength);
886 char* scratch = NULL;
887 char* scratch_output = NULL;
891 size_t fragment_size;
892 const char* fragment = reader->Peek(&fragment_size);
893 assert(fragment_size != 0);
894 const size_t num_to_read = min(N, kBlockSize);
895 size_t bytes_read = fragment_size;
897 size_t pending_advance = 0;
898 if (bytes_read >= num_to_read) {
900 pending_advance = num_to_read;
901 fragment_size = num_to_read;
904 if (scratch == NULL) {
908 scratch =
new char[num_to_read];
910 memcpy(scratch, fragment, bytes_read);
911 reader->Skip(bytes_read);
913 while (bytes_read < num_to_read) {
914 fragment = reader->Peek(&fragment_size);
915 size_t n = min<size_t>(fragment_size, num_to_read - bytes_read);
916 memcpy(scratch + bytes_read, fragment, n);
920 assert(bytes_read == num_to_read);
922 fragment_size = num_to_read;
924 assert(fragment_size == num_to_read);
928 uint16* table = wmem.GetHashTable(num_to_read, &table_size);
931 const int max_output = MaxCompressedLength(num_to_read);
935 if (scratch_output == NULL) {
936 scratch_output =
new char[max_output];
942 char* dest = writer->GetAppendBuffer(max_output, scratch_output);
943 char* end = internal::CompressFragment(fragment, fragment_size,
944 dest, table, table_size);
945 writer->Append(dest, end - dest);
946 written += (end - dest);
949 reader->Skip(pending_advance);
953 delete[] scratch_output;
967 const struct iovec* output_iov_;
968 const size_t output_iov_count_;
974 size_t curr_iov_written_;
977 size_t total_written_;
980 size_t output_limit_;
982 inline char* GetIOVecPointer(
int index,
size_t offset) {
983 return reinterpret_cast<char*
>(output_iov_[index].iov_base) +
992 output_iov_count_(iov_count),
994 curr_iov_written_(0),
999 inline void SetExpectedLength(
size_t len) {
1000 output_limit_ = len;
1003 inline bool CheckLength()
const {
1004 return total_written_ == output_limit_;
1007 inline bool Append(
const char* ip,
size_t len) {
1008 if (total_written_ + len > output_limit_) {
1013 assert(curr_iov_written_ <= output_iov_[curr_iov_index_].iov_len);
1014 if (curr_iov_written_ >= output_iov_[curr_iov_index_].iov_len) {
1016 if (
size_t(curr_iov_index_) + 1 >= output_iov_count_) {
1019 curr_iov_written_ = 0;
1023 const size_t to_write = std::min(
1024 len, output_iov_[curr_iov_index_].iov_len - curr_iov_written_);
1025 memcpy(GetIOVecPointer(curr_iov_index_, curr_iov_written_),
1028 curr_iov_written_ += to_write;
1029 total_written_ += to_write;
1037 inline bool TryFastAppend(
const char* ip,
size_t available,
size_t len) {
1038 const size_t space_left = output_limit_ - total_written_;
1039 if (len <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16 &&
1040 output_iov_[curr_iov_index_].iov_len - curr_iov_written_ >= 16) {
1042 char* ptr = GetIOVecPointer(curr_iov_index_, curr_iov_written_);
1043 UnalignedCopy64(ip, ptr);
1044 UnalignedCopy64(ip + 8, ptr + 8);
1045 curr_iov_written_ += len;
1046 total_written_ += len;
1053 inline bool AppendFromSelf(
size_t offset,
size_t len) {
1054 if (offset > total_written_ || offset == 0) {
1057 const size_t space_left = output_limit_ - total_written_;
1058 if (len > space_left) {
1063 int from_iov_index = curr_iov_index_;
1064 size_t from_iov_offset = curr_iov_written_;
1065 while (offset > 0) {
1066 if (from_iov_offset >= offset) {
1067 from_iov_offset -= offset;
1071 offset -= from_iov_offset;
1073 assert(from_iov_index >= 0);
1074 from_iov_offset = output_iov_[from_iov_index].iov_len;
1080 assert(from_iov_index <= curr_iov_index_);
1081 if (from_iov_index != curr_iov_index_) {
1082 const size_t to_copy = std::min(
1083 output_iov_[from_iov_index].iov_len - from_iov_offset,
1085 Append(GetIOVecPointer(from_iov_index, from_iov_offset), to_copy);
1089 from_iov_offset = 0;
1092 assert(curr_iov_written_ <= output_iov_[curr_iov_index_].iov_len);
1093 size_t to_copy = std::min(output_iov_[curr_iov_index_].iov_len -
1098 if (
size_t(curr_iov_index_) + 1 >= output_iov_count_) {
1102 curr_iov_written_ = 0;
1105 if (to_copy > len) {
1108 IncrementalCopy(GetIOVecPointer(from_iov_index, from_iov_offset),
1109 GetIOVecPointer(curr_iov_index_, curr_iov_written_),
1111 curr_iov_written_ += to_copy;
1112 from_iov_offset += to_copy;
1113 total_written_ += to_copy;
1121 inline void Flush() {}
1124 bool RawUncompressToIOVec(
const char* compressed,
size_t compressed_length,
1125 const struct iovec* iov,
size_t iov_cnt) {
1127 return RawUncompressToIOVec(&reader, iov, iov_cnt);
1130 bool RawUncompressToIOVec(
Source* compressed,
const struct iovec* iov,
1133 return InternalUncompress(compressed, &output);
1156 inline void SetExpectedLength(
size_t len) {
1157 op_limit_ = op_ + len;
1160 inline bool CheckLength()
const {
1161 return op_ == op_limit_;
1164 inline bool Append(
const char* ip,
size_t len) {
1166 const size_t space_left = op_limit_ - op;
1167 if (space_left < len) {
1170 memcpy(op, ip, len);
1175 inline bool TryFastAppend(
const char* ip,
size_t available,
size_t len) {
1177 const size_t space_left = op_limit_ - op;
1178 if (len <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16) {
1180 UnalignedCopy64(ip, op);
1181 UnalignedCopy64(ip + 8, op + 8);
1189 inline bool AppendFromSelf(
size_t offset,
size_t len) {
1191 const size_t space_left = op_limit_ - op;
1200 assert(op >= base_);
1201 size_t produced = op - base_;
1202 if (produced <= offset - 1u) {
1205 if (len <= 16 && offset >= 8 && space_left >= 16) {
1207 UnalignedCopy64(op - offset, op);
1208 UnalignedCopy64(op - offset + 8, op + 8);
1210 if (space_left >= len + kMaxIncrementCopyOverflow) {
1211 IncrementalCopyFastPath(op - offset, op, len);
1213 if (space_left < len) {
1216 IncrementalCopy(op - offset, op, len);
1223 inline size_t Produced()
const {
1226 inline void Flush() {}
1229 bool RawUncompress(
const char* compressed,
size_t n,
char* uncompressed) {
1231 return RawUncompress(&reader, uncompressed);
1234 bool RawUncompress(
Source* compressed,
char* uncompressed) {
1236 return InternalUncompress(compressed, &output);
1239 bool Uncompress(
const char* compressed,
size_t n,
string* uncompressed) {
1241 if (!GetUncompressedLength(compressed, n, &ulength)) {
1246 if (ulength > uncompressed->max_size()) {
1249 STLStringResizeUninitialized(uncompressed, ulength);
1250 return RawUncompress(compressed, n, string_as_array(uncompressed));
1261 inline void SetExpectedLength(
size_t len) {
1264 inline bool CheckLength()
const {
1265 return expected_ == produced_;
1267 inline bool Append(
const char* ip,
size_t len) {
1269 return produced_ <= expected_;
1271 inline bool TryFastAppend(
const char* ip,
size_t available,
size_t length) {
1274 inline bool AppendFromSelf(
size_t offset,
size_t len) {
1277 if (produced_ <= offset - 1u)
return false;
1279 return produced_ <= expected_;
1281 inline void Flush() {}
1284 bool IsValidCompressedBuffer(
const char* compressed,
size_t n) {
1287 return InternalUncompress(&reader, &writer);
1290 bool IsValidCompressed(
Source* compressed) {
1292 return InternalUncompress(compressed, &writer);
1295 void RawCompress(
const char* input,
1296 size_t input_length,
1298 size_t* compressed_length) {
1301 Compress(&reader, &writer);
1304 *compressed_length = (writer.CurrentDestination() - compressed);
1307 size_t Compress(
const char* input,
size_t input_length,
string* compressed) {
1309 compressed->resize(MaxCompressedLength(input_length));
1311 size_t compressed_length;
1312 RawCompress(input, input_length, string_as_array(compressed),
1313 &compressed_length);
1314 compressed->resize(compressed_length);
1315 return compressed_length;
1325 template <
typename Allocator>
1327 Allocator allocator_;
1332 vector<char*> blocks_;
1343 inline size_t Size()
const {
1344 return full_size_ + (op_ptr_ - op_base_);
1347 bool SlowAppend(
const char* ip,
size_t len);
1348 bool SlowAppendFromSelf(
size_t offset,
size_t len);
1352 : allocator_(allocator),
1360 inline void SetExpectedLength(
size_t len) {
1361 assert(blocks_.empty());
1365 inline bool CheckLength()
const {
1366 return Size() == expected_;
1370 inline size_t Produced()
const {
1374 inline bool Append(
const char* ip,
size_t len) {
1375 size_t avail = op_limit_ - op_ptr_;
1378 memcpy(op_ptr_, ip, len);
1382 return SlowAppend(ip, len);
1386 inline bool TryFastAppend(
const char* ip,
size_t available,
size_t length) {
1388 const int space_left = op_limit_ - op;
1389 if (length <= 16 && available >= 16 + kMaximumTagLength &&
1392 UNALIGNED_STORE64(op, UNALIGNED_LOAD64(ip));
1393 UNALIGNED_STORE64(op + 8, UNALIGNED_LOAD64(ip + 8));
1394 op_ptr_ = op + length;
1401 inline bool AppendFromSelf(
size_t offset,
size_t len) {
1406 if ( offset != 0 &&
long(offset) <= op_ptr_ - op_base_) {
1407 const size_t space_left = op_limit_ - op_ptr_;
1408 if (space_left >= len + kMaxIncrementCopyOverflow) {
1410 IncrementalCopyFastPath(op_ptr_ - offset, op_ptr_, len);
1415 return SlowAppendFromSelf(offset, len);
1420 inline void Flush() { allocator_.Flush(Produced()); }
1423 template<
typename Allocator>
1425 size_t avail = op_limit_ - op_ptr_;
1426 while (len > avail) {
1428 memcpy(op_ptr_, ip, avail);
1430 assert(op_limit_ - op_ptr_ == 0);
1431 full_size_ += (op_ptr_ - op_base_);
1436 if (full_size_ + len > expected_) {
1441 size_t bsize = min<size_t>(kBlockSize, expected_ - full_size_);
1442 op_base_ = allocator_.Allocate(bsize);
1444 op_limit_ = op_base_ + bsize;
1445 blocks_.push_back(op_base_);
1449 memcpy(op_ptr_, ip, len);
1454 template<
typename Allocator>
1460 const size_t cur = Size();
1461 if (offset - 1u >= cur)
return false;
1462 if (expected_ - cur < len)
return false;
1469 size_t src = cur - offset;
1471 char c = blocks_[src >> kBlockLog][src & (kBlockSize-1)];
1483 char* Allocate(
int size) {
1485 blocks_.push_back(block);
1494 void Flush(
size_t size) {
1495 size_t size_written = 0;
1496 for (
size_t i = 0; i < blocks_.size(); ++i) {
1497 size_t block_size = min<size_t>(blocks_[i].size, size - size_written);
1498 dest_->AppendAndTakeOwnership(blocks_[i].data, block_size,
1499 &SnappySinkAllocator::Deleter, NULL);
1500 size_written += block_size;
1509 Datablock(
char* p,
size_t s) : data(p), size(s) {}
1512 static void Deleter(
void* arg,
const char* bytes,
size_t size) {
1517 vector<Datablock> blocks_;
1522 size_t UncompressAsMuchAsPossible(
Source* compressed,
Sink* uncompressed) {
1525 InternalUncompress(compressed, &writer);
1526 return writer.Produced();
1529 bool Uncompress(
Source* compressed,
Sink* uncompressed) {
1532 uint32 uncompressed_len = 0;
1533 if (!decompressor.ReadUncompressedLength(&uncompressed_len)) {
1538 size_t allocated_size;
1539 char* buf = uncompressed->GetAppendBufferVariable(
1540 1, uncompressed_len, &c, 1, &allocated_size);
1544 if (allocated_size >= uncompressed_len) {
1546 bool result = InternalUncompressAllTags(
1547 &decompressor, &writer, uncompressed_len);
1548 uncompressed->Append(buf, writer.Produced());
1553 return InternalUncompressAllTags(&decompressor, &writer, uncompressed_len);