|
|
@@ -5,8 +5,11 @@
|
|
|
package protoapi
|
|
|
|
|
|
import (
|
|
|
+ "encoding/binary"
|
|
|
"encoding/json"
|
|
|
"fmt"
|
|
|
+ "hash/crc32"
|
|
|
+ "math"
|
|
|
"strconv"
|
|
|
)
|
|
|
|
|
|
@@ -49,5 +52,40 @@ func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32,
|
|
|
return val, nil
|
|
|
}
|
|
|
|
|
|
+// CompressGZIP compresses the input as a GZIP-encoded file.
|
|
|
+// The current implementation does no compression.
|
|
|
+func CompressGZIP(in []byte) (out []byte) {
|
|
|
+ // RFC 1952, section 2.3.1.
|
|
|
+ var gzipHeader = [10]byte{0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}
|
|
|
+
|
|
|
+ // RFC 1951, section 3.2.4.
|
|
|
+ var blockHeader [5]byte
|
|
|
+ const maxBlockSize = math.MaxUint16
|
|
|
+ numBlocks := 1 + len(in)/maxBlockSize
|
|
|
+
|
|
|
+ // RFC 1952, section 2.3.1.
|
|
|
+ var gzipFooter [8]byte
|
|
|
+ binary.LittleEndian.PutUint32(gzipFooter[0:4], crc32.ChecksumIEEE(in))
|
|
|
+ binary.LittleEndian.PutUint32(gzipFooter[4:8], uint32(len(in)))
|
|
|
+
|
|
|
+ // Encode the input without compression using raw DEFLATE blocks.
|
|
|
+ out = make([]byte, 0, len(gzipHeader)+len(blockHeader)*numBlocks+len(in)+len(gzipFooter))
|
|
|
+ out = append(out, gzipHeader[:]...)
|
|
|
+ for blockHeader[0] == 0 {
|
|
|
+ blockSize := maxBlockSize
|
|
|
+ if blockSize > len(in) {
|
|
|
+ blockHeader[0] = 0x01 // final bit per RFC 1951, section 3.2.3.
|
|
|
+ blockSize = len(in)
|
|
|
+ }
|
|
|
+ binary.LittleEndian.PutUint16(blockHeader[1:3], uint16(blockSize)^0x0000)
|
|
|
+ binary.LittleEndian.PutUint16(blockHeader[3:5], uint16(blockSize)^0xffff)
|
|
|
+ out = append(out, blockHeader[:]...)
|
|
|
+ out = append(out, in[:blockSize]...)
|
|
|
+ in = in[blockSize:]
|
|
|
+ }
|
|
|
+ out = append(out, gzipFooter[:]...)
|
|
|
+ return out
|
|
|
+}
|
|
|
+
|
|
|
// TODO: Remove this when v2 textpb is available.
|
|
|
var CompactTextString func(Message) string
|