-
Notifications
You must be signed in to change notification settings - Fork 36
/
redisparser.dart
175 lines (159 loc) · 4.42 KB
/
redisparser.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
* Free software licenced under
* MIT License
*
* Check for document LICENCE forfull licence text
*
* Luka Rahne
*/
part of redis;
class RedisParser extends Parser {}
class RedisParserBulkBinary extends Parser {
Future parseBulk(LazyStream s) {
return parseInt(s).then((i) {
//get len
if (i == -1) //null
return null;
if (i >= 0) {
//i of bulk data
return s
.take_n(i)
.then((lst) => takeCRLF(s, lst)); //consume CRLF and return list
} else {
return Future.error(
RedisRuntimeError("cant process buld data less than -1"));
}
});
}
}
class Parser {
static final UTF8 = const Utf8Codec();
static const int CR = 13;
static const int LF = 10;
static const int TYPE_SS = 43; //+
static const int TYPE_ERROR = 45; //-
static const int TYPE_INT = 58; //:
static const int TYPE_BULK = 36; //$
static const int TYPE_ARRAY = 42; //*
//read untill it finds CR and LF
//by protocol it is enough to find just CR and LF folows
//this method can be used only on types that complies with such rule
//it consumes both CR and LF from stream, but is not returned
Future read_simple(LazyStream s) {
return s.take_while((c) => (c != CR)).then((list) {
//takeWile consumed CR from stream,
//now check for LF
return s.take_n(1).then((lf) {
if (lf[0] != LF) {
return Future.error(RedisRuntimeError("received element is not LF"));
}
return list;
});
});
}
//return Future<r> if next two elemets are CRLF
//or thows if failed
Future takeCRLF(LazyStream s, r) {
return s.take_n(2).then((data) {
if (data[0] == CR && data[1] == LF) {
return r;
} else {
return Future.error(RedisRuntimeError("expeting CRLF"));
}
});
}
Future parse(LazyStream s) {
return parseredisresponse(s);
}
Future parseredisresponse(LazyStream s) {
return s.take_n(1).then((list) {
int cmd = list[0];
switch (cmd) {
case TYPE_SS:
return parseSimpleString(s);
case TYPE_INT:
return parseInt(s);
case TYPE_ARRAY:
return parseArray(s);
case TYPE_BULK:
return parseBulk(s);
case TYPE_ERROR:
return parseError(s);
default:
return Future.error(
RedisRuntimeError("got element that cant not be parsed"));
}
});
}
Future<String> parseSimpleString(LazyStream s) {
return read_simple(s).then((v) {
return UTF8.decode(v);
});
}
Future<RedisError> parseError(LazyStream s) {
return parseSimpleString(s).then((str) => RedisError(str));
}
Future<int> parseInt(LazyStream s) {
return read_simple(s).then((v) => _ParseIntRaw(v));
}
Future parseBulk(LazyStream s) {
return parseInt(s).then((i) {
//get len
if (i == -1) //null
return null;
if (i >= 0) {
//i of bulk data
return s.take_n(i).then((lst) => takeCRLF(
s, UTF8.decode(lst))); //consume CRLF and return decoded list
} else {
return Future.error(
RedisRuntimeError("cant process buld data less than -1"));
}
});
}
//it first consume array as N and then
//consume N elements with parseredisresponse function
Future<List> parseArray(LazyStream s) {
//closure
Future<List> consumeList(LazyStream s, int len, List lst) {
assert(len >= 0);
if (len == 0) {
return Future.value(lst);
}
return parseredisresponse(s).then((resp) {
lst.add(resp);
return consumeList(s, len - 1, lst);
});
}
//end of closure
return parseInt(s).then((i) {
//get len
if (i == -1) //null
return [null];
if (i >= 0) {
//i of array data
List a = [];
return consumeList(s, i, a);
} else {
return Future.error(
RedisRuntimeError("cant process array data less than -1"));
}
});
}
//maualy parse int from raw data (faster)
static int _ParseIntRaw(Iterable<int> arr) {
int sign = 1;
var v = arr.fold(0, (dynamic a, b) {
if (b == 45) {
if (a != 0) throw RedisRuntimeError("cannot parse int");
sign = -1;
return 0;
} else if ((b >= 48) && (b < 58)) {
return a * 10 + b - 48;
} else {
throw RedisRuntimeError("cannot parse int");
}
});
return v * sign;
}
}