1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.compression;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.ChannelHandlerContext;
20 import io.netty.handler.codec.MessageToByteEncoder;
21
22 import java.util.zip.Adler32;
23 import java.util.zip.Checksum;
24
25 import static io.netty.handler.codec.compression.FastLz.BLOCK_TYPE_COMPRESSED;
26 import static io.netty.handler.codec.compression.FastLz.BLOCK_TYPE_NON_COMPRESSED;
27 import static io.netty.handler.codec.compression.FastLz.BLOCK_WITHOUT_CHECKSUM;
28 import static io.netty.handler.codec.compression.FastLz.BLOCK_WITH_CHECKSUM;
29 import static io.netty.handler.codec.compression.FastLz.CHECKSUM_OFFSET;
30 import static io.netty.handler.codec.compression.FastLz.LEVEL_1;
31 import static io.netty.handler.codec.compression.FastLz.LEVEL_2;
32 import static io.netty.handler.codec.compression.FastLz.LEVEL_AUTO;
33 import static io.netty.handler.codec.compression.FastLz.MAGIC_NUMBER;
34 import static io.netty.handler.codec.compression.FastLz.MAX_CHUNK_LENGTH;
35 import static io.netty.handler.codec.compression.FastLz.MIN_LENGTH_TO_COMPRESSION;
36 import static io.netty.handler.codec.compression.FastLz.OPTIONS_OFFSET;
37 import static io.netty.handler.codec.compression.FastLz.calculateOutputBufferLength;
38 import static io.netty.handler.codec.compression.FastLz.compress;
39
40
41
42
43
44
45 public class FastLzFrameEncoder extends MessageToByteEncoder<ByteBuf> {
46
47
48
49 private final int level;
50
51
52
53
54 private final ByteBufChecksum checksum;
55
56
57
58
59 public FastLzFrameEncoder() {
60 this(LEVEL_AUTO, null);
61 }
62
63
64
65
66
67
68
69
70
71 public FastLzFrameEncoder(int level) {
72 this(level, null);
73 }
74
75
76
77
78
79
80
81
82
83
84
85 public FastLzFrameEncoder(boolean validateChecksums) {
86 this(LEVEL_AUTO, validateChecksums ? new Adler32() : null);
87 }
88
89
90
91
92
93
94
95
96
97
98
99
100 public FastLzFrameEncoder(int level, Checksum checksum) {
101 if (level != LEVEL_AUTO && level != LEVEL_1 && level != LEVEL_2) {
102 throw new IllegalArgumentException(String.format(
103 "level: %d (expected: %d or %d or %d)", level, LEVEL_AUTO, LEVEL_1, LEVEL_2));
104 }
105 this.level = level;
106 this.checksum = checksum == null ? null : ByteBufChecksum.wrapChecksum(checksum);
107 }
108
109 @Override
110 protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception {
111 final ByteBufChecksum checksum = this.checksum;
112
113 for (;;) {
114 if (!in.isReadable()) {
115 return;
116 }
117 final int idx = in.readerIndex();
118 final int length = Math.min(in.readableBytes(), MAX_CHUNK_LENGTH);
119
120 final int outputIdx = out.writerIndex();
121 out.setMedium(outputIdx, MAGIC_NUMBER);
122 int outputOffset = outputIdx + CHECKSUM_OFFSET + (checksum != null ? 4 : 0);
123
124 final byte blockType;
125 final int chunkLength;
126 if (length < MIN_LENGTH_TO_COMPRESSION) {
127 blockType = BLOCK_TYPE_NON_COMPRESSED;
128
129 out.ensureWritable(outputOffset + 2 + length);
130 final int outputPtr = outputOffset + 2;
131
132 if (checksum != null) {
133 checksum.reset();
134 checksum.update(in, idx, length);
135 out.setInt(outputIdx + CHECKSUM_OFFSET, (int) checksum.getValue());
136 }
137 out.setBytes(outputPtr, in, idx, length);
138 chunkLength = length;
139 } else {
140
141 if (checksum != null) {
142 checksum.reset();
143 checksum.update(in, idx, length);
144 out.setInt(outputIdx + CHECKSUM_OFFSET, (int) checksum.getValue());
145 }
146
147 final int maxOutputLength = calculateOutputBufferLength(length);
148 out.ensureWritable(outputOffset + 4 + maxOutputLength);
149 final int outputPtr = outputOffset + 4;
150 final int compressedLength = compress(in, in.readerIndex(), length, out, outputPtr, level);
151
152 if (compressedLength < length) {
153 blockType = BLOCK_TYPE_COMPRESSED;
154 chunkLength = compressedLength;
155
156 out.setShort(outputOffset, chunkLength);
157 outputOffset += 2;
158 } else {
159 blockType = BLOCK_TYPE_NON_COMPRESSED;
160 out.setBytes(outputOffset + 2, in, idx, length);
161 chunkLength = length;
162 }
163 }
164 out.setShort(outputOffset, length);
165
166 out.setByte(outputIdx + OPTIONS_OFFSET,
167 blockType | (checksum != null ? BLOCK_WITH_CHECKSUM : BLOCK_WITHOUT_CHECKSUM));
168 out.writerIndex(outputOffset + 2 + chunkLength);
169 in.skipBytes(length);
170 }
171 }
172 }