Merge remote-tracking branch 'origin/v0.10'
[platform/upstream/nodejs.git] / src / node_buffer.cc
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22
23 #include "node.h"
24 #include "node_buffer.h"
25
26 #include "env.h"
27 #include "env-inl.h"
28 #include "smalloc.h"
29 #include "string_bytes.h"
30 #include "v8-profiler.h"
31 #include "v8.h"
32
33 #include <assert.h>
34 #include <string.h>
35 #include <limits.h>
36
37 #define MIN(a, b) ((a) < (b) ? (a) : (b))
38
39 #define CHECK_NOT_OOB(r)                                                    \
40   do { if (!(r)) return ThrowRangeError("out of range index"); } while (0)
41
42 #define ARGS_THIS(argT)                                                     \
43   Local<Object> obj = argT;                                                 \
44   size_t obj_length = obj->GetIndexedPropertiesExternalArrayDataLength();   \
45   char* obj_data = static_cast<char*>(                                      \
46     obj->GetIndexedPropertiesExternalArrayData());                          \
47   if (obj_length > 0)                                                       \
48     assert(obj_data != NULL);
49
50 #define SLICE_START_END(start_arg, end_arg, end_max)                        \
51   size_t start;                                                             \
52   size_t end;                                                               \
53   CHECK_NOT_OOB(ParseArrayIndex(start_arg, 0, &start));                     \
54   CHECK_NOT_OOB(ParseArrayIndex(end_arg, end_max, &end));                   \
55   if (end < start) end = start;                                             \
56   CHECK_NOT_OOB(end <= end_max);                                            \
57   size_t length = end - start;
58
59 namespace node {
60 namespace Buffer {
61
62 using v8::ArrayBuffer;
63 using v8::Context;
64 using v8::Function;
65 using v8::FunctionCallbackInfo;
66 using v8::FunctionTemplate;
67 using v8::Handle;
68 using v8::HandleScope;
69 using v8::Local;
70 using v8::Number;
71 using v8::Object;
72 using v8::String;
73 using v8::Uint32;
74 using v8::Value;
75
76
77 bool HasInstance(Handle<Value> val) {
78   return val->IsObject() && HasInstance(val.As<Object>());
79 }
80
81
82 bool HasInstance(Handle<Object> obj) {
83   if (!obj->HasIndexedPropertiesInExternalArrayData())
84     return false;
85   v8::ExternalArrayType type = obj->GetIndexedPropertiesExternalArrayDataType();
86   return type == v8::kExternalUnsignedByteArray;
87 }
88
89
90 char* Data(Handle<Value> val) {
91   assert(val->IsObject());
92   // Use a fully qualified name here to work around a bug in gcc 4.2.
93   // It mistakes an unadorned call to Data() for the v8::String::Data type.
94   return node::Buffer::Data(val.As<Object>());
95 }
96
97
98 char* Data(Handle<Object> obj) {
99   assert(obj->HasIndexedPropertiesInExternalArrayData());
100   return static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData());
101 }
102
103
104 size_t Length(Handle<Value> val) {
105   assert(val->IsObject());
106   return Length(val.As<Object>());
107 }
108
109
110 size_t Length(Handle<Object> obj) {
111   assert(obj->HasIndexedPropertiesInExternalArrayData());
112   return obj->GetIndexedPropertiesExternalArrayDataLength();
113 }
114
115
116 Local<Object> New(Handle<String> string, enum encoding enc) {
117   HandleScope scope(node_isolate);
118
119   size_t length = StringBytes::Size(string, enc);
120
121   Local<Object> buf = New(length);
122   char* data = Buffer::Data(buf);
123   StringBytes::Write(data, length, string, enc);
124
125   return scope.Close(buf);
126 }
127
128
129 Local<Object> New(size_t length) {
130   HandleScope handle_scope(node_isolate);
131   Environment* env = Environment::GetCurrent(node_isolate);
132   Local<Object> obj = Buffer::New(env, length);
133   return handle_scope.Close(obj);
134 }
135
136
137 // TODO(trevnorris): these have a flaw by needing to call the Buffer inst then
138 // Alloc. continue to look for a better architecture.
139 Local<Object> New(Environment* env, size_t length) {
140   HandleScope scope(node_isolate);
141
142   assert(length <= kMaxLength);
143
144   Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate);
145   Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg);
146
147   // TODO(trevnorris): done like this to handle HasInstance since only checks
148   // if external array data has been set, but would like to use a better
149   // approach if v8 provided one.
150   char* data;
151   if (length > 0) {
152     data = static_cast<char*>(malloc(length));
153     if (data == NULL)
154       FatalError("node::Buffer::New(size_t)", "Out Of Memory");
155   } else {
156     data = NULL;
157   }
158   smalloc::Alloc(obj, data, length);
159
160   return scope.Close(obj);
161 }
162
163
164 Local<Object> New(const char* data, size_t length) {
165   HandleScope handle_scope(node_isolate);
166   Environment* env = Environment::GetCurrent(node_isolate);
167   Local<Object> obj = Buffer::New(env, data, length);
168   return handle_scope.Close(obj);
169 }
170
171
172 // TODO(trevnorris): for backwards compatibility this is left to copy the data,
173 // but for consistency w/ the other should use data. And a copy version renamed
174 // to something else.
175 Local<Object> New(Environment* env, const char* data, size_t length) {
176   HandleScope scope(node_isolate);
177
178   assert(length <= kMaxLength);
179
180   Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate);
181   Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg);
182
183   // TODO(trevnorris): done like this to handle HasInstance since only checks
184   // if external array data has been set, but would like to use a better
185   // approach if v8 provided one.
186   char* new_data;
187   if (length > 0) {
188     new_data = static_cast<char*>(malloc(length));
189     if (new_data == NULL)
190       FatalError("node::Buffer::New(const char*, size_t)", "Out Of Memory");
191     memcpy(new_data, data, length);
192   } else {
193     new_data = NULL;
194   }
195
196   smalloc::Alloc(obj, new_data, length);
197
198   return scope.Close(obj);
199 }
200
201
202 Local<Object> New(char* data,
203                   size_t length,
204                   smalloc::FreeCallback callback,
205                   void* hint) {
206   HandleScope handle_scope(node_isolate);
207   Environment* env = Environment::GetCurrent(node_isolate);
208   Local<Object> obj = Buffer::New(env, data, length, callback, hint);
209   return handle_scope.Close(obj);
210 }
211
212
213 Local<Object> New(Environment* env,
214                   char* data,
215                   size_t length,
216                   smalloc::FreeCallback callback,
217                   void* hint) {
218   HandleScope scope(node_isolate);
219
220   assert(length <= kMaxLength);
221
222   Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate);
223   Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg);
224
225   smalloc::Alloc(obj, data, length, callback, hint);
226
227   return scope.Close(obj);
228 }
229
230
231 Local<Object> Use(char* data, uint32_t length) {
232   HandleScope handle_scope(node_isolate);
233   Environment* env = Environment::GetCurrent(node_isolate);
234   Local<Object> obj = Buffer::Use(env, data, length);
235   return handle_scope.Close(obj);
236 }
237
238
239 Local<Object> Use(Environment* env, char* data, uint32_t length) {
240   HandleScope scope(node_isolate);
241
242   assert(length <= kMaxLength);
243
244   Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate);
245   Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg);
246
247   smalloc::Alloc(obj, data, length);
248
249   return scope.Close(obj);
250 }
251
252
253 template <encoding encoding>
254 void StringSlice(const FunctionCallbackInfo<Value>& args) {
255   HandleScope scope(node_isolate);
256
257   ARGS_THIS(args.This())
258   SLICE_START_END(args[0], args[1], obj_length)
259
260   args.GetReturnValue().Set(
261       StringBytes::Encode(obj_data + start, length, encoding));
262 }
263
264
265 void BinarySlice(const FunctionCallbackInfo<Value>& args) {
266   StringSlice<BINARY>(args);
267 }
268
269
270 void AsciiSlice(const FunctionCallbackInfo<Value>& args) {
271   StringSlice<ASCII>(args);
272 }
273
274
275 void Utf8Slice(const FunctionCallbackInfo<Value>& args) {
276   StringSlice<UTF8>(args);
277 }
278
279
280 void Ucs2Slice(const FunctionCallbackInfo<Value>& args) {
281   StringSlice<UCS2>(args);
282 }
283
284
285 void HexSlice(const FunctionCallbackInfo<Value>& args) {
286   StringSlice<HEX>(args);
287 }
288
289
290 void Base64Slice(const FunctionCallbackInfo<Value>& args) {
291   StringSlice<BASE64>(args);
292 }
293
294
295 // bytesCopied = buffer.copy(target[, targetStart][, sourceStart][, sourceEnd]);
296 void Copy(const FunctionCallbackInfo<Value> &args) {
297   HandleScope scope(node_isolate);
298
299   Local<Object> target = args[0]->ToObject();
300
301   if (!HasInstance(target))
302     return ThrowTypeError("first arg should be a Buffer");
303
304   ARGS_THIS(args.This())
305   size_t target_length = target->GetIndexedPropertiesExternalArrayDataLength();
306   char* target_data = static_cast<char*>(
307       target->GetIndexedPropertiesExternalArrayData());
308   size_t target_start;
309   size_t source_start;
310   size_t source_end;
311
312   CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &target_start));
313   CHECK_NOT_OOB(ParseArrayIndex(args[2], 0, &source_start));
314   CHECK_NOT_OOB(ParseArrayIndex(args[3], obj_length, &source_end));
315
316   // Copy 0 bytes; we're done
317   if (target_start >= target_length || source_start >= source_end)
318     return args.GetReturnValue().Set(0);
319
320   if (source_start > obj_length)
321     return ThrowRangeError("out of range index");
322
323   if (source_end - source_start > target_length - target_start)
324     source_end = source_start + target_length - target_start;
325
326   uint32_t to_copy = MIN(MIN(source_end - source_start,
327                              target_length - target_start),
328                              obj_length - source_start);
329
330   memmove(target_data + target_start, obj_data + source_start, to_copy);
331   args.GetReturnValue().Set(to_copy);
332 }
333
334
335 // buffer.fill(value[, start][, end]);
336 void Fill(const FunctionCallbackInfo<Value>& args) {
337   HandleScope scope(node_isolate);
338
339   ARGS_THIS(args.This())
340   SLICE_START_END(args[1], args[2], obj_length)
341   args.GetReturnValue().Set(args.This());
342
343   if (args[0]->IsNumber()) {
344     int value = args[0]->Uint32Value() & 255;
345     memset(obj_data + start, value, length);
346     return;
347   }
348
349   String::Utf8Value at(args[0]);
350   size_t at_length = at.length();
351
352   // optimize single ascii character case
353   if (at_length == 1) {
354     int value = static_cast<int>((*at)[0]);
355     memset(obj_data + start, value, length);
356     return;
357   }
358
359   size_t in_there = at_length;
360   char* ptr = obj_data + start + at_length;
361
362   memcpy(obj_data + start, *at, MIN(at_length, length));
363
364   if (at_length >= length)
365     return;
366
367   while (in_there < length - in_there) {
368     memcpy(ptr, obj_data + start, in_there);
369     ptr += in_there;
370     in_there *= 2;
371   }
372
373   if (in_there < length) {
374     memcpy(ptr, obj_data + start, length - in_there);
375     in_there = length;
376   }
377 }
378
379
380 template <encoding encoding>
381 void StringWrite(const FunctionCallbackInfo<Value>& args) {
382   HandleScope scope(node_isolate);
383
384   ARGS_THIS(args.This())
385
386   if (!args[0]->IsString())
387     return ThrowTypeError("Argument must be a string");
388
389   Local<String> str = args[0]->ToString();
390
391   if (encoding == HEX && str->Length() % 2 != 0)
392     return ThrowTypeError("Invalid hex string");
393
394   size_t offset;
395   size_t max_length;
396
397   CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &offset));
398   CHECK_NOT_OOB(ParseArrayIndex(args[2], obj_length - offset, &max_length));
399
400   max_length = MIN(obj_length - offset, max_length);
401
402   if (max_length == 0)
403     return args.GetReturnValue().Set(0);
404
405   if (encoding == UCS2)
406     max_length = max_length / 2;
407
408   if (offset >= obj_length)
409     return ThrowRangeError("Offset is out of bounds");
410
411   uint32_t written = StringBytes::Write(obj_data + offset,
412                                         max_length,
413                                         str,
414                                         encoding,
415                                         NULL);
416   args.GetReturnValue().Set(written);
417 }
418
419
420 void Base64Write(const FunctionCallbackInfo<Value>& args) {
421   StringWrite<BASE64>(args);
422 }
423
424
425 void BinaryWrite(const FunctionCallbackInfo<Value>& args) {
426   StringWrite<BINARY>(args);
427 }
428
429
430 void Utf8Write(const FunctionCallbackInfo<Value>& args) {
431   StringWrite<UTF8>(args);
432 }
433
434
435 void Ucs2Write(const FunctionCallbackInfo<Value>& args) {
436   StringWrite<UCS2>(args);
437 }
438
439
440 void HexWrite(const FunctionCallbackInfo<Value>& args) {
441   StringWrite<HEX>(args);
442 }
443
444
445 void AsciiWrite(const FunctionCallbackInfo<Value>& args) {
446   StringWrite<ASCII>(args);
447 }
448
449
450 static inline void Swizzle(char* start, unsigned int len) {
451   char* end = start + len - 1;
452   while (start < end) {
453     char tmp = *start;
454     *start++ = *end;
455     *end-- = tmp;
456   }
457 }
458
459
460 template <typename T, enum Endianness endianness>
461 void ReadFloatGeneric(const FunctionCallbackInfo<Value>& args) {
462   bool doAssert = !args[1]->BooleanValue();
463   size_t offset;
464
465   CHECK_NOT_OOB(ParseArrayIndex(args[0], 0, &offset));
466
467   if (doAssert) {
468     size_t len = Length(args.This());
469     if (offset + sizeof(T) > len || offset + sizeof(T) < offset)
470       return ThrowRangeError("Trying to read beyond buffer length");
471   }
472
473   union NoAlias {
474     T val;
475     char bytes[sizeof(T)];
476   };
477
478   union NoAlias na;
479   const void* data = args.This()->GetIndexedPropertiesExternalArrayData();
480   const char* ptr = static_cast<const char*>(data) + offset;
481   memcpy(na.bytes, ptr, sizeof(na.bytes));
482   if (endianness != GetEndianness())
483     Swizzle(na.bytes, sizeof(na.bytes));
484
485   args.GetReturnValue().Set(na.val);
486 }
487
488
489 void ReadFloatLE(const FunctionCallbackInfo<Value>& args) {
490   ReadFloatGeneric<float, kLittleEndian>(args);
491 }
492
493
494 void ReadFloatBE(const FunctionCallbackInfo<Value>& args) {
495   ReadFloatGeneric<float, kBigEndian>(args);
496 }
497
498
499 void ReadDoubleLE(const FunctionCallbackInfo<Value>& args) {
500   ReadFloatGeneric<double, kLittleEndian>(args);
501 }
502
503
504 void ReadDoubleBE(const FunctionCallbackInfo<Value>& args) {
505   ReadFloatGeneric<double, kBigEndian>(args);
506 }
507
508
509 template <typename T, enum Endianness endianness>
510 uint32_t WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) {
511   bool doAssert = !args[2]->BooleanValue();
512
513   T val = static_cast<T>(args[0]->NumberValue());
514   size_t offset;
515
516   if (!ParseArrayIndex(args[1], 0, &offset)) {
517     ThrowRangeError("out of range index");
518     return 0;
519   }
520
521   if (doAssert) {
522     size_t len = Length(args.This());
523     if (offset + sizeof(T) > len || offset + sizeof(T) < offset) {
524       ThrowRangeError("Trying to write beyond buffer length");
525       return 0;
526     }
527   }
528
529   union NoAlias {
530     T val;
531     char bytes[sizeof(T)];
532   };
533
534   union NoAlias na = { val };
535   void* data = args.This()->GetIndexedPropertiesExternalArrayData();
536   char* ptr = static_cast<char*>(data) + offset;
537   if (endianness != GetEndianness())
538     Swizzle(na.bytes, sizeof(na.bytes));
539   memcpy(ptr, na.bytes, sizeof(na.bytes));
540   return offset + sizeof(na.bytes);
541 }
542
543
544 void WriteFloatLE(const FunctionCallbackInfo<Value>& args) {
545   args.GetReturnValue().Set(WriteFloatGeneric<float, kLittleEndian>(args));
546 }
547
548
549 void WriteFloatBE(const FunctionCallbackInfo<Value>& args) {
550   args.GetReturnValue().Set(WriteFloatGeneric<float, kBigEndian>(args));
551 }
552
553
554 void WriteDoubleLE(const FunctionCallbackInfo<Value>& args) {
555   args.GetReturnValue().Set(WriteFloatGeneric<double, kLittleEndian>(args));
556 }
557
558
559 void WriteDoubleBE(const FunctionCallbackInfo<Value>& args) {
560   args.GetReturnValue().Set(WriteFloatGeneric<double, kBigEndian>(args));
561 }
562
563
564 void ToArrayBuffer(const FunctionCallbackInfo<Value>& args) {
565   HandleScope scope(node_isolate);
566
567   ARGS_THIS(args.This());
568   void* adata = malloc(obj_length);
569
570   if (adata == NULL) {
571     FatalError("node::Buffer::ToArrayBuffer("
572         "const FunctionCallbackInfo<v8::Value>&)",
573         "Out Of Memory");
574   }
575
576   memcpy(adata, obj_data, obj_length);
577
578   Local<ArrayBuffer> abuf = ArrayBuffer::New(adata, obj_length);
579   args.GetReturnValue().Set(abuf);
580 }
581
582
583 void ByteLength(const FunctionCallbackInfo<Value> &args) {
584   HandleScope scope(node_isolate);
585
586   if (!args[0]->IsString())
587     return ThrowTypeError("Argument must be a string");
588
589   Local<String> s = args[0]->ToString();
590   enum encoding e = ParseEncoding(args[1], UTF8);
591
592   uint32_t size = StringBytes::Size(s, e);
593   args.GetReturnValue().Set(size);
594 }
595
596
597 // pass Buffer object to load prototype methods
598 void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
599   HandleScope handle_scope(args.GetIsolate());
600   Environment* env = Environment::GetCurrent(args.GetIsolate());
601
602   assert(args[0]->IsFunction());
603
604   Local<Function> bv = args[0].As<Function>();
605   env->set_buffer_constructor_function(bv);
606   Local<Value> proto_v =
607       bv->Get(FIXED_ONE_BYTE_STRING(node_isolate, "prototype"));
608
609   assert(proto_v->IsObject());
610
611   Local<Object> proto = proto_v.As<Object>();
612
613   NODE_SET_METHOD(proto, "asciiSlice", AsciiSlice);
614   NODE_SET_METHOD(proto, "base64Slice", Base64Slice);
615   NODE_SET_METHOD(proto, "binarySlice", BinarySlice);
616   NODE_SET_METHOD(proto, "hexSlice", HexSlice);
617   NODE_SET_METHOD(proto, "ucs2Slice", Ucs2Slice);
618   NODE_SET_METHOD(proto, "utf8Slice", Utf8Slice);
619
620   NODE_SET_METHOD(proto, "asciiWrite", AsciiWrite);
621   NODE_SET_METHOD(proto, "base64Write", Base64Write);
622   NODE_SET_METHOD(proto, "binaryWrite", BinaryWrite);
623   NODE_SET_METHOD(proto, "hexWrite", HexWrite);
624   NODE_SET_METHOD(proto, "ucs2Write", Ucs2Write);
625   NODE_SET_METHOD(proto, "utf8Write", Utf8Write);
626
627   NODE_SET_METHOD(proto, "readDoubleBE", ReadDoubleBE);
628   NODE_SET_METHOD(proto, "readDoubleLE", ReadDoubleLE);
629   NODE_SET_METHOD(proto, "readFloatBE", ReadFloatBE);
630   NODE_SET_METHOD(proto, "readFloatLE", ReadFloatLE);
631
632   NODE_SET_METHOD(proto, "writeDoubleBE", WriteDoubleBE);
633   NODE_SET_METHOD(proto, "writeDoubleLE", WriteDoubleLE);
634   NODE_SET_METHOD(proto, "writeFloatBE", WriteFloatBE);
635   NODE_SET_METHOD(proto, "writeFloatLE", WriteFloatLE);
636
637   NODE_SET_METHOD(proto, "toArrayBuffer", ToArrayBuffer);
638
639   NODE_SET_METHOD(proto, "copy", Copy);
640   NODE_SET_METHOD(proto, "fill", Fill);
641
642   // for backwards compatibility
643   proto->Set(FIXED_ONE_BYTE_STRING(node_isolate, "offset"),
644              Uint32::New(0, node_isolate),
645              v8::ReadOnly);
646
647   assert(args[1]->IsObject());
648
649   Local<Object> internal = args[1].As<Object>();
650
651   internal->Set(FIXED_ONE_BYTE_STRING(node_isolate, "byteLength"),
652                 FunctionTemplate::New(ByteLength)->GetFunction());
653 }
654
655
656 void Initialize(Handle<Object> target,
657                 Handle<Value> unused,
658                 Handle<Context> context) {
659   Environment* env = Environment::GetCurrent(context);
660   target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "setupBufferJS"),
661               FunctionTemplate::New(SetupBufferJS)->GetFunction());
662 }
663
664
665 }  // namespace Buffer
666 }  // namespace node
667
668 NODE_MODULE_CONTEXT_AWARE(node_buffer, node::Buffer::Initialize)