f524778f464b2186c97e6247d9db0c7493d7725a
[platform/upstream/nodejs.git] / src / node_buffer.cc
1 #include <node_buffer.h>
2
3 #include <assert.h>
4 #include <stdlib.h> // malloc, free
5 #include <v8.h>
6
7 #include <string.h> // memcpy
8
9 #include <arpa/inet.h>  // htons, htonl
10
11 #include <node.h>
12
13 #define MIN(a,b) ((a) < (b) ? (a) : (b))
14
15 namespace node {
16
17 using namespace v8;
18
19 #define SLICE_ARGS(start_arg, end_arg)                               \
20   if (!start_arg->IsInt32() || !end_arg->IsInt32()) {                \
21     return ThrowException(Exception::TypeError(                      \
22           String::New("Bad argument.")));                            \
23   }                                                                  \
24   int32_t start = start_arg->Int32Value();                           \
25   int32_t end = end_arg->Int32Value();                               \
26   if (start < 0 || end < 0) {                                        \
27     return ThrowException(Exception::TypeError(                      \
28           String::New("Bad argument.")));                            \
29   }                                                                  \
30   if (!(start <= end)) {                                             \
31     return ThrowException(Exception::Error(                          \
32           String::New("Must have start <= end")));                   \
33   }                                                                  \
34   if ((size_t)end > parent->length_) {                               \
35     return ThrowException(Exception::Error(                          \
36           String::New("end cannot be longer than parent.length")));  \
37   }
38
39
40 static Persistent<String> length_symbol;
41 Persistent<FunctionTemplate> Buffer::constructor_template;
42
43
44 // Each javascript Buffer object is backed by a Blob object.
45 // the Blob is just a C-level chunk of bytes.
46 // It has a reference count.
47 struct Blob_ {
48   unsigned int refs;
49   size_t length;
50   char *data;
51 };
52 typedef struct Blob_ Blob;
53
54
55 static inline Blob * blob_new(size_t length) {
56   Blob * blob  = (Blob*) malloc(sizeof(Blob));
57   if (!blob) return NULL;
58
59   blob->data = (char*) malloc(length);
60   if (!blob->data) {
61     free(blob);
62     return NULL;
63   }
64
65   V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Blob) + length);
66   blob->length = length;
67   blob->refs = 0;
68   return blob;
69 }
70
71
72 static inline void blob_ref(Blob *blob) {
73   blob->refs++;
74 }
75
76
77 static inline void blob_unref(Blob *blob) {
78   assert(blob->refs > 0);
79   if (--blob->refs == 0) {
80     //fprintf(stderr, "free %d bytes\n", blob->length);
81     V8::AdjustAmountOfExternalAllocatedMemory(-(sizeof(Blob) + blob->length));
82     free(blob->data);
83     free(blob);
84   }
85 }
86
87 #if 0
88 // When someone calls buffer.asciiSlice, data is not copied. Instead V8
89 // references in the underlying Blob with this ExternalAsciiStringResource.
90 class AsciiSliceExt: public String::ExternalAsciiStringResource {
91  friend class Buffer;
92  public:
93   AsciiSliceExt(Buffer *parent, size_t start, size_t end) {
94     blob_ = parent->blob();
95     blob_ref(blob_);
96
97     assert(start <= end);
98     length_ = end - start;
99     assert(start + length_ <= parent->length());
100     data_ = parent->data() + start;
101   }
102
103
104   ~AsciiSliceExt() {
105     //fprintf(stderr, "free ascii slice (%d refs left)\n", blob_->refs);
106     blob_unref(blob_);
107   }
108
109
110   const char* data() const { return data_; }
111   size_t length() const { return length_; }
112
113  private:
114   const char *data_;
115   size_t length_;
116   Blob *blob_;
117 };
118 #endif
119
120
121 Handle<Value> Buffer::New(const Arguments &args) {
122   HandleScope scope;
123
124   Buffer *buffer;
125   if (args[0]->IsInt32()) {
126     // var buffer = new Buffer(1024);
127     size_t length = args[0]->Uint32Value();
128     buffer = new Buffer(length);
129
130   } else if (Buffer::HasInstance(args[0]) && args.Length() > 2) {
131     // var slice = new Buffer(buffer, 123, 130);
132     // args: parent, start, end
133     Buffer *parent = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
134     SLICE_ARGS(args[1], args[2])
135     buffer = new Buffer(parent, start, end);
136   } else {
137     return ThrowException(Exception::TypeError(String::New("Bad argument")));
138   }
139
140   buffer->Wrap(args.This());
141   args.This()->SetIndexedPropertiesToExternalArrayData(buffer->data(),
142                                                        kExternalUnsignedByteArray,
143                                                        buffer->length());
144   args.This()->Set(length_symbol, Integer::New(buffer->length_));
145   return args.This();
146 }
147
148
149 Buffer::Buffer(size_t length) : ObjectWrap() {
150   blob_ = blob_new(length);
151   off_ = 0;
152   length_ = length;
153
154   blob_ref(blob_);
155
156   V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer));
157 }
158
159
160 Buffer::Buffer(Buffer *parent, size_t start, size_t end) : ObjectWrap() {
161   blob_ = parent->blob_;
162   assert(blob_->refs > 0);
163   blob_ref(blob_);
164
165   assert(start <= end);
166   off_ = start;
167   length_ = end - start;
168   assert(length_ <= parent->length_);
169
170   V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer));
171 }
172
173
174 Buffer::~Buffer() {
175   assert(blob_->refs > 0);
176   //fprintf(stderr, "free buffer (%d refs left)\n", blob_->refs);
177   blob_unref(blob_);
178   V8::AdjustAmountOfExternalAllocatedMemory(-static_cast<long int>(sizeof(Buffer)));
179 }
180
181
182 char* Buffer::data() {
183   return blob_->data + off_;
184 }
185
186
187 Handle<Value> Buffer::BinarySlice(const Arguments &args) {
188   HandleScope scope;
189   Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
190   SLICE_ARGS(args[0], args[1])
191
192   const char *data = const_cast<char*>(parent->data() + start);
193   //Local<String> string = String::New(data, end - start);
194
195   Local<Value> b =  Encode(data, end - start, BINARY);
196
197   return scope.Close(b);
198 }
199
200
201 Handle<Value> Buffer::AsciiSlice(const Arguments &args) {
202   HandleScope scope;
203   Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
204   SLICE_ARGS(args[0], args[1])
205
206 #if 0
207   AsciiSliceExt *ext = new AsciiSliceExt(parent, start, end);
208   Local<String> string = String::NewExternal(ext);
209   // There should be at least two references to the blob now - the parent
210   // and the slice.
211   assert(parent->blob_->refs >= 2);
212 #endif
213
214   const char *data = const_cast<char*>(parent->data() + start);
215   Local<String> string = String::New(data, end - start);
216
217
218   return scope.Close(string);
219 }
220
221
222 Handle<Value> Buffer::Utf8Slice(const Arguments &args) {
223   HandleScope scope;
224   Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
225   SLICE_ARGS(args[0], args[1])
226   const char *data = const_cast<char*>(parent->data() + start);
227   Local<String> string = String::New(data, end - start);
228   return scope.Close(string);
229 }
230
231
232 Handle<Value> Buffer::Slice(const Arguments &args) {
233   HandleScope scope;
234   Local<Value> argv[3] = { args.This(), args[0], args[1] };
235   Local<Object> slice =
236     constructor_template->GetFunction()->NewInstance(3, argv);
237   return scope.Close(slice);
238 }
239
240
241 // var bytesCopied = buffer.copy(target, targetStart, sourceStart, sourceEnd);
242 Handle<Value> Buffer::Copy(const Arguments &args) {
243   HandleScope scope;
244
245   Buffer *source = ObjectWrap::Unwrap<Buffer>(args.This());
246
247   if (!Buffer::HasInstance(args[0])) {
248     return ThrowException(Exception::TypeError(String::New(
249             "First arg should be a Buffer")));
250   }
251
252   Buffer *target = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
253
254   ssize_t target_start = args[1]->Int32Value();
255   ssize_t source_start = args[2]->Int32Value();
256   ssize_t source_end = args[3]->IsInt32() ? args[3]->Int32Value()
257                                           : source->length();
258
259   if (source_end < source_start) {
260     return ThrowException(Exception::Error(String::New(
261             "sourceEnd < sourceStart")));
262   }
263
264   if (target_start >= target->length()) {
265     return ThrowException(Exception::Error(String::New(
266             "targetStart out of bounds")));
267   }
268
269   if (source_start >= source->length()) {
270     return ThrowException(Exception::Error(String::New(
271             "sourceStart out of bounds")));
272   }
273
274   if (source_end > source->length()) {
275     return ThrowException(Exception::Error(String::New(
276             "sourceEnd out of bounds")));
277   }
278
279   ssize_t to_copy = MIN(source_end - source_start,
280                         target->length() - target_start);
281
282   memcpy((void*)(target->data() + target_start),
283          (const void*)(source->data() + source_start),
284          to_copy);
285
286   return scope.Close(Integer::New(to_copy));
287 }
288
289
290 // var charsWritten = buffer.utf8Write(string, offset);
291 Handle<Value> Buffer::Utf8Write(const Arguments &args) {
292   HandleScope scope;
293   Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
294
295   if (!args[0]->IsString()) {
296     return ThrowException(Exception::TypeError(String::New(
297             "Argument must be a string")));
298   }
299
300   Local<String> s = args[0]->ToString();
301
302   size_t offset = args[1]->Int32Value();
303
304   if (offset >= buffer->length_) {
305     return ThrowException(Exception::TypeError(String::New(
306             "Offset is out of bounds")));
307   }
308
309   const char *p = buffer->data() + offset;
310
311   s->Flatten();
312   int written = s->WriteUtf8((char*)p, buffer->length_ - offset);
313
314   if (written > 0 && p[written-1] == '\0') written--;
315
316   return scope.Close(Integer::New(written));
317 }
318
319
320 // var charsWritten = buffer.asciiWrite(string, offset);
321 Handle<Value> Buffer::AsciiWrite(const Arguments &args) {
322   HandleScope scope;
323
324   Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
325
326   if (!args[0]->IsString()) {
327     return ThrowException(Exception::TypeError(String::New(
328             "Argument must be a string")));
329   }
330
331   Local<String> s = args[0]->ToString();
332
333   size_t offset = args[1]->Int32Value();
334
335   if (offset >= buffer->length_) {
336     return ThrowException(Exception::TypeError(String::New(
337             "Offset is out of bounds")));
338   }
339
340   const char *p = buffer->data() + offset;
341
342   size_t towrite = MIN((unsigned long) s->Length(), buffer->length_ - offset);
343
344   s->Flatten();
345   int written = s->WriteAscii((char*)p, 0, towrite);
346   return scope.Close(Integer::New(written));
347 }
348
349
350 Handle<Value> Buffer::BinaryWrite(const Arguments &args) {
351   HandleScope scope;
352
353   Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
354
355   if (!args[0]->IsString()) {
356     return ThrowException(Exception::TypeError(String::New(
357             "Argument must be a string")));
358   }
359
360   Local<String> s = args[0]->ToString();
361
362   size_t offset = args[1]->Int32Value();
363
364   if (offset >= buffer->length_) {
365     return ThrowException(Exception::TypeError(String::New(
366             "Offset is out of bounds")));
367   }
368
369   char *p = (char*)buffer->data() + offset;
370
371   size_t towrite = MIN((unsigned long) s->Length(), buffer->length_ - offset);
372
373   int written = DecodeWrite(p, towrite, s, BINARY);
374   return scope.Close(Integer::New(written));
375 }
376
377
378 // buffer.unpack(format, index);
379 // Starting at 'index', unpacks binary from the buffer into an array.
380 // 'format' is a string
381 //
382 //  FORMAT  RETURNS
383 //    N     uint32_t   a 32bit unsigned integer in network byte order
384 //    n     uint16_t   a 16bit unsigned integer in network byte order
385 //    o     uint8_t    a 8bit unsigned integer
386 Handle<Value> Buffer::Unpack(const Arguments &args) {
387   HandleScope scope;
388   Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());
389
390   if (!args[0]->IsString()) {
391     return ThrowException(Exception::TypeError(String::New(
392             "Argument must be a string")));
393   }
394
395   String::AsciiValue format(args[0]->ToString());
396   uint32_t index = args[1]->Uint32Value();
397
398 #define OUT_OF_BOUNDS ThrowException(Exception::Error(String::New("Out of bounds")))
399
400   Local<Array> array = Array::New(format.length());
401
402   uint8_t  uint8;
403   uint16_t uint16;
404   uint32_t uint32;
405
406   for (int i = 0; i < format.length(); i++) {
407     switch ((*format)[i]) {
408       // 32bit unsigned integer in network byte order
409       case 'N':
410         if (index + 3 >= buffer->length_) return OUT_OF_BOUNDS;
411         uint32 = htonl(*(uint32_t*)(buffer->data() + index));
412         array->Set(Integer::New(i), Integer::NewFromUnsigned(uint32));
413         index += 4;
414         break;
415
416       // 16bit unsigned integer in network byte order
417       case 'n':
418         if (index + 1 >= buffer->length_) return OUT_OF_BOUNDS;
419         uint16 = htons(*(uint16_t*)(buffer->data() + index));
420         array->Set(Integer::New(i), Integer::NewFromUnsigned(uint16));
421         index += 2;
422         break;
423
424       // a single octet, unsigned.
425       case 'o':
426         if (index >= buffer->length_) return OUT_OF_BOUNDS;
427         uint8 = (uint8_t)buffer->data()[index];
428         array->Set(Integer::New(i), Integer::NewFromUnsigned(uint8));
429         index += 1;
430         break;
431
432       default:
433         return ThrowException(Exception::Error(
434               String::New("Unknown format character")));
435     }
436   }
437
438   return scope.Close(array);
439 }
440
441
442 // var nbytes = Buffer.byteLength("string", "utf8")
443 Handle<Value> Buffer::ByteLength(const Arguments &args) {
444   HandleScope scope;
445
446   if (!args[0]->IsString()) {
447     return ThrowException(Exception::TypeError(String::New(
448             "Argument must be a string")));
449   }
450
451   Local<String> s = args[0]->ToString();
452   enum encoding e = ParseEncoding(args[1], UTF8);
453
454   Local<Integer> length =
455     Integer::New(e == UTF8 ? s->Utf8Length() : s->Length()); 
456   
457   return scope.Close(length);
458 }
459
460
461 void Buffer::Initialize(Handle<Object> target) {
462   HandleScope scope;
463
464   length_symbol = Persistent<String>::New(String::NewSymbol("length"));
465
466   Local<FunctionTemplate> t = FunctionTemplate::New(Buffer::New);
467   constructor_template = Persistent<FunctionTemplate>::New(t);
468   constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
469   constructor_template->SetClassName(String::NewSymbol("Buffer"));
470
471   // copy free
472   NODE_SET_PROTOTYPE_METHOD(constructor_template, "binarySlice", Buffer::BinarySlice);
473   NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", Buffer::AsciiSlice);
474   NODE_SET_PROTOTYPE_METHOD(constructor_template, "slice", Buffer::Slice);
475   // TODO NODE_SET_PROTOTYPE_METHOD(t, "utf16Slice", Utf16Slice);
476   // copy
477   NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Buffer::Utf8Slice);
478
479   NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Write", Buffer::Utf8Write);
480   NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", Buffer::AsciiWrite);
481   NODE_SET_PROTOTYPE_METHOD(constructor_template, "binaryWrite", Buffer::BinaryWrite);
482   NODE_SET_PROTOTYPE_METHOD(constructor_template, "unpack", Buffer::Unpack);
483   NODE_SET_PROTOTYPE_METHOD(constructor_template, "copy", Buffer::Copy);
484
485   NODE_SET_METHOD(constructor_template->GetFunction(),
486                   "byteLength",
487                   Buffer::ByteLength);
488
489   target->Set(String::NewSymbol("Buffer"), constructor_template->GetFunction());
490 }
491
492
493 }  // namespace node