UNPKG

10.8 kBJavaScriptView Raw
1/*
2Copyright (c) 2012 Jacob Rus
3
4Permission is hereby granted, free of charge, to any person obtaining
5a copy of this software and associated documentation files (the
6"Software"), to deal in the Software without restriction, including
7without limitation the rights to use, copy, modify, merge, publish,
8distribute, sublicense, and/or sell copies of the Software, and to
9permit persons to whom the Software is furnished to do so, subject to
10the following conditions:
11
12The above copyright notice and this permission notice shall be
13included in all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23File-like objects that read from or write to a string buffer.
24A nearly direct port of Python’s StringIO module.
25f = StringIO() # ready for writing
26f = StringIO(buf) # ready for reading
27f.close() # explicitly release resources held
28pos = f.tell() # get current position
29f.seek(pos) # set current position
30f.seek(pos, mode) # mode 0: absolute; 1: relative; 2: relative to EOF
31buf = f.read() # read until EOF
32buf = f.read(n) # read up to n bytes
33buf = f.readline() # read until end of line ('\n') or EOF
34list = f.readlines() # list of f.readline() results until EOF
35f.truncate([size]) # truncate file to at most size (default: current pos)
36f.write(buf) # write at current position
37f.writelines(list) # for line in list: f.write(line)
38f.getvalue() # return whole file's contents as a string
39Notes:
40- Seeking far beyond EOF and then writing will insert real null
41 bytes that occupy space in the buffer.
42- There's a simple test set (see end of this file).
43 */
44var StringIO, _complain_ifclosed, _test, module_root;
45
46_complain_ifclosed = function(closed) {
47 if (closed) {
48 throw new Error('I/O operation on closed file');
49 }
50};
51
52
53/* class StringIO([buffer])
54When a StringIO object is created, it can be initialized to an existing
55string by passing the string to the constructor. If no string is given,
56the StringIO will start empty.
57 */
58
59StringIO = (function() {
60 function StringIO(buf) {
61 if (buf == null) {
62 buf = '';
63 }
64 this.buf = '' + buf;
65 this.length = this.buf.length;
66 this.buflist = [];
67 this.pos = 0;
68 this.closed = false;
69 }
70
71
72 /* Free the memory buffer. */
73
74 StringIO.prototype.close = function() {
75 if (!this.closed) {
76 this.closed = true;
77 delete this.buf;
78 delete this.pos;
79 }
80 };
81
82 StringIO.prototype._flush_buflist = function() {
83 this.buf += this.buflist.join('');
84 return this.buflist = [];
85 };
86
87
88 /* Set the file's current position.
89 The mode argument is optional and defaults to 0 (absolute file
90 positioning); other values are 1 (seek relative to the current
91 position) and 2 (seek relative to the file's end).
92 There is no return value.
93 */
94
95 StringIO.prototype.seek = function(pos, mode) {
96 if (mode == null) {
97 mode = 0;
98 }
99 _complain_ifclosed(this.closed);
100 if (this.buflist.length) {
101 this._flush_buflist();
102 }
103 if (mode === 1) {
104 pos += this.pos;
105 } else if (mode === 2) {
106 pos += this.length;
107 }
108 this.pos = Math.max(0, pos);
109 };
110
111
112 /* Return the file's current position. */
113
114 StringIO.prototype.tell = function() {
115 _complain_ifclosed(this.closed);
116 return this.pos;
117 };
118
119
120 /* Read at most size bytes from the file
121 (less if the read hits EOF before obtaining size bytes).
122 If the size argument is negative or omitted, read all data until EOF
123 is reached. The bytes are returned as a string object. An empty
124 string is returned when EOF is encountered immediately.
125 */
126
127 StringIO.prototype.read = function(n) {
128 var newpos, r;
129 if (n == null) {
130 n = -1;
131 }
132 _complain_ifclosed(this.closed);
133 if (this.buflist.length) {
134 this._flush_buflist();
135 }
136 if (n < 0) {
137 newpos = this.length;
138 } else {
139 newpos = Math.min(this.pos + n, this.length);
140 }
141 r = this.buf.slice(this.pos, newpos);
142 this.pos = newpos;
143 return r;
144 };
145
146
147 /* Read one entire line from the file.
148
149 A trailing newline character is kept in the string (but may be absent
150 when a file ends with an incomplete line). If the size argument is
151 present and non-negative, it is a maximum byte count (including the
152 trailing newline) and an incomplete line may be returned.
153 An empty string is returned only when EOF is encountered immediately.
154 */
155
156 StringIO.prototype.readline = function(length) {
157 var i, newpos, r;
158 if (length == null) {
159 length = null;
160 }
161 _complain_ifclosed(this.closed);
162 if (this.buflist.length) {
163 this._flush_buflist();
164 }
165 i = this.buf.indexOf('\n', this.pos);
166 if (i < 0) {
167 newpos = this.length;
168 } else {
169 newpos = i + 1;
170 }
171 if ((length != null) && this.pos + length < newpos) {
172 newpos = this.pos + length;
173 }
174 r = this.buf.slice(this.pos, newpos);
175 this.pos = newpos;
176 return r;
177 };
178
179
180 /* Read until EOF using readline() and return a list containing the
181 lines thus read.
182 If the optional sizehint argument is present, instead of reading up
183 to EOF, whole lines totalling approximately sizehint bytes (or more
184 to accommodate a final whole line).
185 */
186
187 StringIO.prototype.readlines = function(sizehint) {
188 var line, lines, total;
189 if (sizehint == null) {
190 sizehint = 0;
191 }
192 total = 0;
193 lines = [];
194 line = this.readline();
195 while (line) {
196 lines.push(line);
197 total += line.length;
198 if ((0 < sizehint && sizehint <= total)) {
199 break;
200 }
201 line = this.readline();
202 }
203 return lines;
204 };
205
206
207 /* Truncate the file's size.
208 If the optional size argument is present, the file is truncated to
209 (at most) that size. The size defaults to the current position.
210 The current file position is not changed unless the position
211 is beyond the new file size.
212 If the specified size exceeds the file's current size, the
213 file remains unchanged.
214 */
215
216 StringIO.prototype.truncate = function(size) {
217 if (size == null) {
218 size = null;
219 }
220 _complain_ifclosed(this.closed);
221 if (size == null) {
222 size = this.pos;
223 } else if (size < 0) {
224 throw new Error('Negative size not allowed');
225 } else if (size < this.pos) {
226 this.pos = size;
227 }
228 this.buf = this.getvalue().slice(0, size);
229 this.length = size;
230 };
231
232
233 /* Write a string to the file.
234 There is no return value.
235 */
236
237 StringIO.prototype.write = function(s) {
238 var newpos, null_bytes, slen, spos;
239 _complain_ifclosed(this.closed);
240 if (!s) {
241 return;
242 }
243 if (typeof s !== 'string') {
244 s = s.toString();
245 }
246 spos = this.pos;
247 slen = this.length;
248 if (spos === slen) {
249 this.buflist.push(s);
250 this.length = this.pos = spos + s.length;
251 return;
252 }
253 if (spos > slen) {
254 null_bytes = (Array(spos - slen + 1)).join('\x00');
255 this.buflist.push(null_bytes);
256 slen = spos;
257 }
258 newpos = spos + s.length;
259 if (spos < slen) {
260 if (this.buflist.length) {
261 this._flush_buflist();
262 }
263 this.buflist.push(this.buf.slice(0, spos), s, this.buf.slice(newpos));
264 this.buf = '';
265 if (newpos > slen) {
266 slen = newpos;
267 }
268 } else {
269 this.buflist.push(s);
270 slen = newpos;
271 }
272 this.length = slen;
273 this.pos = newpos;
274 };
275
276
277 /* Write a sequence of strings to the file. The sequence can be any
278 iterable object producing strings, typically a list of strings. There
279 is no return value.
280 (The name is intended to match readlines(); writelines() does not add
281 line separators.)
282 */
283
284 StringIO.prototype.writelines = function(array) {
285 var j, len, line;
286 for (j = 0, len = array.length; j < len; j++) {
287 line = array[j];
288 this.write(line);
289 }
290 };
291
292
293 /* Flush the internal buffer */
294
295 StringIO.prototype.flush = function() {
296 _complain_ifclosed(this.closed);
297 };
298
299
300 /* Retrieve the entire contents of the "file" at any time
301 before the StringIO object's close() method is called.
302 */
303
304 StringIO.prototype.getvalue = function() {
305 if (this.buflist.length) {
306 this._flush_buflist();
307 }
308 return this.buf;
309 };
310
311 return StringIO;
312
313})();
314
315module_root = typeof exports !== "undefined" && exports !== null ? exports : typeof window !== "undefined" && window !== null ? window : this;
316
317module_root.StringIO = StringIO;
318
319_test = function() {
320 var f, j, len, length, line, line2, lines, list, print, ref;
321 print = function() {
322 return console.log.apply(console, arguments);
323 };
324 lines = ['This is a test,\n', 'Blah blah blah,\n', 'Wow does this work?\n', 'Okay, here are some lines\n', 'of text.\n'];
325 f = new StringIO;
326 ref = lines.slice(0, -2);
327 for (j = 0, len = ref.length; j < len; j++) {
328 line = ref[j];
329 f.write(line);
330 }
331 f.writelines(lines.slice(-2));
332 if (f.getvalue() !== lines.join('')) {
333 throw new Error('write failed');
334 }
335 length = f.tell();
336 print('File length =', length);
337 f.seek(lines[0].length);
338 f.write(lines[1]);
339 f.seek(0);
340 print("First line = " + (f.readline()));
341 print("Position = " + (f.tell()));
342 line = f.readline();
343 print("Second line = " + line);
344 f.seek(-line.length, 1);
345 line2 = f.read(line.length);
346 if (line !== line2) {
347 throw new Error('bad result after seek back');
348 }
349 f.seek(-line2.length, 1);
350 list = f.readlines();
351 line = list[list.length - 1];
352 f.seek(f.tell() - line.length);
353 line2 = f.read();
354 if (line !== line2) {
355 throw new Error('bad result after seek back from EOF');
356 }
357 print("Read " + list.length + " more lines");
358 print("File length = " + (f.tell()));
359 if (f.tell() !== length) {
360 throw new Error('bad length');
361 }
362 f.truncate((length / 2) | 0);
363 f.seek(0, 2);
364 print("Truncated length = " + (f.tell()));
365 if (f.tell() !== ((length / 2) | 0)) {
366 throw new Error('truncate did not adjust length');
367 }
368 return f.close();
369};
\No newline at end of file