dlzma: changed license to WTFPL ;-)
[iv.d.git] / ascii85.d
blob8176f5deab450fc32184b64cb0a857bca744b666
1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // ASCII85 codec
18 module iv.ascii85 /*is aliced*/;
20 import std.range;
21 import std.traits;
22 import iv.alice;
25 /// returns input range
26 auto ascii85Decoder(RI) (auto ref RI src)
27 if (isInputRange!RI &&
28 (isSomeChar!(ElementType!RI)) ||
29 is(ElementType!RI : ubyte) ||
30 is(ElementType!RI : byte))
32 static struct A85D {
33 private:
34 static immutable uint[5] pow85 = [85*85*85*85u, 85*85*85u, 85*85u, 85u, 1u];
35 uint btuple = 0;
36 int count = 0;
37 ubyte curCh = 0;
38 static if (isInfinite!RI) {
39 enum bool empty = false;
40 } else {
41 bool isEmpty = false;
43 RI rng = void;
45 public:
46 this() (auto ref RI sr) {
47 if (sr.empty) {
48 static if (!isInfinite!RI) isEmpty = true;
49 rng = rng.init;
50 } else {
51 rng = sr;
52 popFront; // populate curCh
56 static if (!isInfinite!RI) {
57 @property bool empty () const pure nothrow @nogc { return isEmpty; }
59 @property ubyte front () const pure nothrow @nogc { return curCh; }
61 static if (isForwardRange!RI) {
62 @property auto save() () {
63 auto res = this;
64 if (!empty) res.rng = rng.save();
65 return res;
69 // template to allow autodeducing attributes
70 void popFront() () {
71 static if (!isInfinite!RI) {
72 if (isEmpty) { curCh = 0; return; }
74 if (count == 0) {
75 // read more chars
76 btuple = 0;
77 while (count != 5) {
78 if (rng.empty) break;
79 uint b = cast(uint)rng.front;
80 rng.popFront;
81 if (count == 0 && b == 'z') {
82 // 4 zeroes
83 count = 5;
84 } else if (count == 0 && b == 'y') {
85 // 4 spaces
86 count = 5;
87 btuple = 0x20202020;
88 } else if (b >= '!' && b <= 'u') {
89 btuple += (b-'!')*pow85[count++];
92 if (count < 2) {
93 static if (isInfinite!RI) {
94 assert(0);
95 } else {
96 isEmpty = true;
97 curCh = 0;
98 return;
101 if (count != 5) {
102 // we have some bytes ready
103 btuple += pow85[--count];
104 } else {
105 // we have 4 bytes ready
106 count = 4;
109 assert(count > 0);
110 curCh = (btuple>>24)&0xff;
111 btuple <<= 8;
112 --count;
115 return A85D(src);
119 version(test_ascii85)
120 unittest {
121 import std.array;
122 import std.utf : byChar;
124 immutable e = `:Ms_p+EVgG/0IE&ARo=s-Z^D?Df'3+B-:f)EZfXGFT`;
125 immutable s = cast(string)array(ascii85Decoder(e.byChar));
126 //import iv.writer; writeln(s);
127 assert(s == "One, two, Freddy's coming for you");
130 immutable e =
131 ":Ms_p+EVgG/0IE&ARo=s-Z^D?Df'3+B-:f)EZfXGFU\n"~
132 "D)]Eb/f5+D,P7E\\&>\"ATW$*EZf1:@r!34Dfp(CA8cC\n"~
133 ",$:\\`QALnsFBm;0OB6%Ei+CQC&Eckl+AncKB$<(MZA\n"~
134 "Ss%AASGdjF=\\P)Df0H$+EMX5Gp%6K+DbJ.AM+<bBl7\n"~
135 "K5+EV14/0I]!G%G\\:F)5E!E$/S%@;0U3/hRJ";
136 immutable s = cast(string)array(ascii85Decoder(e.byChar));
137 //import iv.writer; writeln(s);
138 assert(s ==
139 "One, two, Freddy's coming for you\n"~
140 "Three, four, Better lock your door\n"~
141 "Five, six, grab a crucifix.\n"~
142 "Seven, eight, Gonna stay up late.\n"~
143 "Nine, ten, Never sleep again...\n");
148 /// returns input range
149 auto ascii85Encoder(RI) (auto ref RI src)
150 if (isInputRange!RI &&
151 (is(ElementType!RI : char) ||
152 is(ElementType!RI : ubyte) ||
153 is(ElementType!RI : byte)))
155 static struct A85E {
156 private:
157 usize bpos = 0;
158 int count = 0;
159 ubyte[5] buf;
160 static if (isInfinite!RI) {
161 enum bool empty = false;
162 } else {
163 bool isEmpty = false;
165 RI rng = void;
167 public:
168 this() (auto ref RI sr) {
169 if (sr.empty) {
170 static if (!isInfinite!RI) isEmpty = true;
171 rng = rng.init;
172 } else {
173 rng = sr;
174 popFront; // populate curCh
178 static if (!isInfinite!RI) {
179 @property bool empty () const pure nothrow @nogc { return isEmpty; }
180 @property char front () const pure nothrow @nogc { return (isEmpty ? 0 : cast(char)buf[bpos]); }
181 } else {
182 @property char front () const pure nothrow @nogc { return cast(char)buf[bpos]; }
185 static if (isForwardRange!RI) {
186 @property auto save() () {
187 auto res = this;
188 if (!empty) res.rng = rng.save();
189 return res;
193 // template to allow autodeducing attributes
194 void popFront() () {
195 static if (!isInfinite!RI) {
196 if (isEmpty) return;
198 if (count > 0) {
199 --bpos;
200 --count;
201 return;
203 // read at most 4 bytes and encode 'em
204 count = 0;
205 uint btuple = 0;
206 while (count < 4) {
207 if (rng.empty) break;
208 auto b = cast(uint)rng.front;
209 rng.popFront;
210 btuple |= b<<((3-count)*8);
211 ++count;
213 if (count < 1) {
214 static if (isInfinite!RI) {
215 assert(0);
216 } else {
217 isEmpty = true;
218 return;
221 if (count == 4 && btuple == 0) {
222 // special case
223 bpos = 1;
224 buf[0] = 'z';
225 } else {
226 // encode tuple
227 for (bpos = 0; bpos < 5; ++bpos) {
228 buf[bpos] = btuple%85+'!';
229 btuple /= 85;
231 --bpos; // current char
235 return A85E(src);
239 version(test_ascii85)
240 unittest {
241 import std.array;
242 import std.utf : byChar;
244 immutable s = "One, two, Freddy's coming for you";
245 immutable e = cast(string)array(ascii85Encoder(s.byChar));
246 assert(e == `:Ms_p+EVgG/0IE&ARo=s-Z^D?Df'3+B-:f)EZfXGFT`);
247 //import iv.writer; writeln(e);
250 immutable s =
251 "One, two, Freddy's coming for you\n"~
252 "Three, four, Better lock your door\n"~
253 "Five, six, grab a crucifix.\n"~
254 "Seven, eight, Gonna stay up late.\n"~
255 "Nine, ten, Never sleep again...\n";
256 immutable e = cast(string)array(ascii85Encoder(s.byChar));
257 assert(e ==
258 ":Ms_p+EVgG/0IE&ARo=s-Z^D?Df'3+B-:f)EZfXGFU"~
259 "D)]Eb/f5+D,P7E\\&>\"ATW$*EZf1:@r!34Dfp(CA8cC"~
260 ",$:\\`QALnsFBm;0OB6%Ei+CQC&Eckl+AncKB$<(MZA"~
261 "Ss%AASGdjF=\\P)Df0H$+EMX5Gp%6K+DbJ.AM+<bBl7"~
262 "K5+EV14/0I]!G%G\\:F)5E!E$/S%@;0U3/hRJ");
263 //import iv.writer; writeln(e);