-
Notifications
You must be signed in to change notification settings - Fork 8
/
rle_example.dart
153 lines (134 loc) · 5.11 KB
/
rle_example.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright (c) 2020, Instantiations, Inc. Please see the AUTHORS
// file for details. All rights reserved. Use of this source code is governed by
// a BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:es_compression/framework.dart';
import 'utils/example_utils.dart';
/// This program demonstrates using the compression framework to implement a new
/// encode/decoder defining a very simple run-length compression scheme.
///
/// RLE Algorithm Description: The data to compress will encode repetitive
/// sequences of characters (up to 9) in a 2 byte form.
/// a -> 1a
/// bb -> 2b
/// cccccccccccc -> 9c3c
///
/// In the example below:
/// 'abbcccddddeeeeeffffffffff'
/// will encode to
/// '1a2b3c4d5e9f2f'
///
/// The [exitCode] of this script is 0 if the decoded bytes match the original,
/// otherwise the [exitCode] is -1.
Future<int> main() async => await _runRleExample();
/// Rle Example which answers 0 on success, -1 on error
Future<int> _runRleExample() async {
final bytes = utf8.encode('abbcccddddeeeeefffffffffff');
// One-shot encode/decode
final encoded = runLengthCodec.encode(bytes);
verifyEquality(utf8.encode('1a2b3c4d5e9f2f'), encoded,
header: 'Verify encoding output');
var decoded = runLengthCodec.decode(encoded);
final oneShotResult = verifyEquality(bytes, decoded, header: 'One-shot: ');
// Streaming encode/decode
// Split bytes into 10 buckets
final chunks = splitIntoChunks(bytes, 10);
final randomStream = Stream.fromIterable(chunks);
decoded = await randomStream
.transform(runLengthCodec.encoder)
.transform(runLengthCodec.decoder)
.fold<List<int>>(<int>[], (buffer, data) {
buffer.addAll(data);
return buffer;
});
final streamResult = verifyEquality(bytes, decoded, header: 'Streaming: ');
return (oneShotResult == true && streamResult == true) ? 0 : -1;
}
/// An instance of the default implementation of the [RunLengthCodec].
const RunLengthCodec runLengthCodec = RunLengthCodec._default();
/// Custom codec providing an [encoder] and [decoder].
class RunLengthCodec extends Codec<List<int>, List<int>> {
RunLengthCodec();
const RunLengthCodec._default();
@override
Converter<List<int>, List<int>> get decoder => RunLengthDecoder();
@override
Converter<List<int>, List<int>> get encoder => RunLengthEncoder();
}
/// Custom encoder that provides a [CodecSink] with the algorithm
/// [RunLengthEncoderFilter].
class RunLengthEncoder extends CodecConverter {
@override
ByteConversionSink startChunkedConversion(Sink<List<int>> sink) {
final byteSink = asByteSink(sink);
return CodecSink(byteSink, RunLengthEncoderFilter());
}
}
/// Filter that encodes the incoming bytes using a Dart in-memory buffer.
class RunLengthEncoderFilter extends DartCodecFilterBase {
int runLength = 1;
@override
CodecResult doProcessing(
DartCodecBuffer inputBuffer, DartCodecBuffer outputBuffer) {
final readPos = inputBuffer.readCount;
final writePos = outputBuffer.writeCount;
while (!inputBuffer.atEnd() && outputBuffer.unwrittenCount > 1) {
const maxRunLength = 9;
final next = inputBuffer.next();
if (runLength < maxRunLength && inputBuffer.peek() == next) {
runLength++;
} else {
final runLengthBytes = utf8.encode(runLength.toString());
outputBuffer
..nextPutAll(runLengthBytes)
..nextPut(next);
runLength = 1;
}
}
final read = inputBuffer.readCount - readPos;
final written = outputBuffer.writeCount - writePos;
return CodecResult(read, written, adjustBufferCounts: false);
}
}
/// Custom decoder that provides a [CodecSink] with the algorithm
/// [RunLengthDecoderFilter].
class RunLengthDecoder extends CodecConverter {
@override
ByteConversionSink startChunkedConversion(Sink<List<int>> sink) {
final byteSink = asByteSink(sink);
return CodecSink(byteSink, RunLengthDecoderFilter());
}
}
enum RleState { expectingLength, expectingData }
/// Filter that decodes the incoming bytes using a Dart in-memory buffer.
class RunLengthDecoderFilter extends DartCodecFilterBase {
RleState _state = RleState.expectingLength;
int runLength = 1;
@override
CodecResult doProcessing(
DartCodecBuffer inputBuffer, DartCodecBuffer outputBuffer) {
final readPos = inputBuffer.readCount;
final writePos = outputBuffer.writeCount;
while (!inputBuffer.atEnd() && !outputBuffer.isFull()) {
switch (_state) {
case RleState.expectingLength:
final runLengthStr = String.fromCharCode(inputBuffer.next());
runLength = int.parse(runLengthStr);
_state = RleState.expectingData;
break;
case RleState.expectingData:
final nextChar = inputBuffer.next();
for (var i = 0; i < runLength; i++) {
outputBuffer.nextPut(nextChar);
}
_state = RleState.expectingLength;
break;
}
}
final read = inputBuffer.readCount - readPos;
final written = outputBuffer.writeCount - writePos;
return CodecResult(read, written, adjustBufferCounts: false);
}
}