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 super(ByteBuf.class);
102 if (level != LEVEL_AUTO && level != LEVEL_1 && level != LEVEL_2) {
103 throw new IllegalArgumentException(String.format(
104 "level: %d (expected: %d or %d or %d)", level, LEVEL_AUTO, LEVEL_1, LEVEL_2));
105 }
106 this.level = level;
107 this.checksum = checksum == null ? null : ByteBufChecksum.wrapChecksum(checksum);
108 }
109
110 @Override
111 protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception {
112 final ByteBufChecksum checksum = this.checksum;
113
114 for (;;) {
115 if (!in.isReadable()) {
116 return;
117 }
118 final int idx = in.readerIndex();
119 final int length = Math.min(in.readableBytes(), MAX_CHUNK_LENGTH);
120
121 final int outputIdx = out.writerIndex();
122 out.setMedium(outputIdx, MAGIC_NUMBER);
123 int outputOffset = outputIdx + CHECKSUM_OFFSET + (checksum != null ? 4 : 0);
124
125 final byte blockType;
126 final int chunkLength;
127 if (length < MIN_LENGTH_TO_COMPRESSION) {
128 blockType = BLOCK_TYPE_NON_COMPRESSED;
129
130 out.ensureWritable(outputOffset + 2 + length);
131 final int outputPtr = outputOffset + 2;
132
133 if (checksum != null) {
134 checksum.reset();
135 checksum.update(in, idx, length);
136 out.setInt(outputIdx + CHECKSUM_OFFSET, (int) checksum.getValue());
137 }
138 out.setBytes(outputPtr, in, idx, length);
139 chunkLength = length;
140 } else {
141
142 if (checksum != null) {
143 checksum.reset();
144 checksum.update(in, idx, length);
145 out.setInt(outputIdx + CHECKSUM_OFFSET, (int) checksum.getValue());
146 }
147
148 final int maxOutputLength = calculateOutputBufferLength(length);
149 out.ensureWritable(outputOffset + 4 + maxOutputLength);
150 final int outputPtr = outputOffset + 4;
151 final int compressedLength = compress(in, in.readerIndex(), length, out, outputPtr, level);
152
153 if (compressedLength < length) {
154 blockType = BLOCK_TYPE_COMPRESSED;
155 chunkLength = compressedLength;
156
157 out.setShort(outputOffset, chunkLength);
158 outputOffset += 2;
159 } else {
160 blockType = BLOCK_TYPE_NON_COMPRESSED;
161 out.setBytes(outputOffset + 2, in, idx, length);
162 chunkLength = length;
163 }
164 }
165 out.setShort(outputOffset, length);
166
167 out.setByte(outputIdx + OPTIONS_OFFSET,
168 blockType | (checksum != null ? BLOCK_WITH_CHECKSUM : BLOCK_WITHOUT_CHECKSUM));
169 out.writerIndex(outputOffset + 2 + chunkLength);
170 in.skipBytes(length);
171 }
172 }
173 }