The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.
Please select the desired protocol below to get the URL.
This URL has Read-Only access.
main_repo / src / string_bytes.cc @ f230a1cf
History | View | Annotate | Download (19.3 KB)
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 |
#include "string_bytes.h" |
23 |
|
24 |
#include "node.h" |
25 |
#include "node_buffer.h" |
26 |
#include "v8.h" |
27 |
|
28 |
#include <assert.h> |
29 |
#include <limits.h> |
30 |
#include <string.h> // memcpy |
31 |
|
32 |
// When creating strings >= this length v8's gc spins up and consumes
|
33 |
// most of the execution time. For these cases it's more performant to
|
34 |
// use external string resources.
|
35 |
#define EXTERN_APEX 0xFBEE9 |
36 |
|
37 |
namespace node {
|
38 |
|
39 |
using v8::Handle;
|
40 |
using v8::HandleScope;
|
41 |
using v8::Local;
|
42 |
using v8::String;
|
43 |
using v8::Value;
|
44 |
|
45 |
|
46 |
template <typename ResourceType, typename TypeName> |
47 |
class ExternString: public ResourceType { |
48 |
public:
|
49 |
~ExternString() { |
50 |
delete[] data_;
|
51 |
node_isolate->AdjustAmountOfExternalAllocatedMemory(-length_); |
52 |
} |
53 |
|
54 |
const TypeName* data() const { |
55 |
return data_;
|
56 |
} |
57 |
|
58 |
size_t length() const {
|
59 |
return length_;
|
60 |
} |
61 |
|
62 |
static Local<String> NewFromCopy(const TypeName* data, size_t length) { |
63 |
HandleScope scope(node_isolate); |
64 |
|
65 |
if (length == 0) |
66 |
return scope.Close(String::Empty(node_isolate));
|
67 |
|
68 |
TypeName* new_data = new TypeName[length];
|
69 |
memcpy(new_data, data, length * sizeof(*new_data));
|
70 |
|
71 |
return scope.Close(ExternString<ResourceType, TypeName>::New(new_data,
|
72 |
length)); |
73 |
} |
74 |
|
75 |
// uses "data" for external resource, and will be free'd on gc
|
76 |
static Local<String> New(const TypeName* data, size_t length) { |
77 |
HandleScope scope(node_isolate); |
78 |
|
79 |
if (length == 0) |
80 |
return scope.Close(String::Empty(node_isolate));
|
81 |
|
82 |
ExternString* h_str = new ExternString<ResourceType, TypeName>(data,
|
83 |
length); |
84 |
Local<String> str = String::NewExternal(h_str); |
85 |
node_isolate->AdjustAmountOfExternalAllocatedMemory(length); |
86 |
|
87 |
return scope.Close(str);
|
88 |
} |
89 |
|
90 |
private:
|
91 |
ExternString(const TypeName* data, size_t length)
|
92 |
: data_(data), length_(length) { } |
93 |
const TypeName* data_;
|
94 |
size_t length_; |
95 |
}; |
96 |
|
97 |
|
98 |
typedef ExternString<String::ExternalAsciiStringResource,
|
99 |
char> ExternOneByteString;
|
100 |
typedef ExternString<String::ExternalStringResource,
|
101 |
uint16_t> ExternTwoByteString; |
102 |
|
103 |
|
104 |
//// Base 64 ////
|
105 |
|
106 |
#define base64_encoded_size(size) ((size + 2 - ((size + 2) % 3)) / 3 * 4) |
107 |
|
108 |
|
109 |
// Doesn't check for padding at the end. Can be 1-2 bytes over.
|
110 |
static inline size_t base64_decoded_size_fast(size_t size) { |
111 |
size_t remainder = size % 4;
|
112 |
|
113 |
size = (size / 4) * 3; |
114 |
if (remainder) {
|
115 |
if (size == 0 && remainder == 1) { |
116 |
// special case: 1-byte input cannot be decoded
|
117 |
size = 0;
|
118 |
} else {
|
119 |
// non-padded input, add 1 or 2 extra bytes
|
120 |
size += 1 + (remainder == 3); |
121 |
} |
122 |
} |
123 |
|
124 |
return size;
|
125 |
} |
126 |
|
127 |
template <typename TypeName> |
128 |
size_t base64_decoded_size(const TypeName* src, size_t size) {
|
129 |
if (size == 0) |
130 |
return 0; |
131 |
|
132 |
if (src[size - 1] == '=') |
133 |
size--; |
134 |
if (size > 0 && src[size - 1] == '=') |
135 |
size--; |
136 |
|
137 |
return base64_decoded_size_fast(size);
|
138 |
} |
139 |
|
140 |
|
141 |
// supports regular and URL-safe base64
|
142 |
static const int unbase64_table[] = |
143 |
{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -2, -1, -1, |
144 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
145 |
-2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, |
146 |
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, |
147 |
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, |
148 |
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, |
149 |
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, |
150 |
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, |
151 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
152 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
153 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
154 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
155 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
156 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
157 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
158 |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 |
159 |
}; |
160 |
#define unbase64(x) unbase64_table[(uint8_t)(x)]
|
161 |
|
162 |
|
163 |
template <typename TypeName> |
164 |
size_t base64_decode(char* buf,
|
165 |
size_t len, |
166 |
const TypeName* src,
|
167 |
const size_t srcLen) {
|
168 |
char a, b, c, d;
|
169 |
char* dst = buf;
|
170 |
char* dstEnd = buf + len;
|
171 |
const TypeName* srcEnd = src + srcLen;
|
172 |
|
173 |
while (src < srcEnd && dst < dstEnd) {
|
174 |
int remaining = srcEnd - src;
|
175 |
|
176 |
while (unbase64(*src) < 0 && src < srcEnd) |
177 |
src++, remaining--; |
178 |
if (remaining == 0 || *src == '=') |
179 |
break;
|
180 |
a = unbase64(*src++); |
181 |
|
182 |
while (unbase64(*src) < 0 && src < srcEnd) |
183 |
src++, remaining--; |
184 |
if (remaining <= 1 || *src == '=') |
185 |
break;
|
186 |
b = unbase64(*src++); |
187 |
|
188 |
*dst++ = (a << 2) | ((b & 0x30) >> 4); |
189 |
if (dst == dstEnd)
|
190 |
break;
|
191 |
|
192 |
while (unbase64(*src) < 0 && src < srcEnd) |
193 |
src++, remaining--; |
194 |
if (remaining <= 2 || *src == '=') |
195 |
break;
|
196 |
c = unbase64(*src++); |
197 |
|
198 |
*dst++ = ((b & 0x0F) << 4) | ((c & 0x3C) >> 2); |
199 |
if (dst == dstEnd)
|
200 |
break;
|
201 |
|
202 |
while (unbase64(*src) < 0 && src < srcEnd) |
203 |
src++, remaining--; |
204 |
if (remaining <= 3 || *src == '=') |
205 |
break;
|
206 |
d = unbase64(*src++); |
207 |
|
208 |
*dst++ = ((c & 0x03) << 6) | (d & 0x3F); |
209 |
} |
210 |
|
211 |
return dst - buf;
|
212 |
} |
213 |
|
214 |
|
215 |
//// HEX ////
|
216 |
|
217 |
template <typename TypeName> |
218 |
unsigned hex2bin(TypeName c) {
|
219 |
if (c >= '0' && c <= '9') |
220 |
return c - '0'; |
221 |
if (c >= 'A' && c <= 'F') |
222 |
return 10 + (c - 'A'); |
223 |
if (c >= 'a' && c <= 'f') |
224 |
return 10 + (c - 'a'); |
225 |
return static_cast<unsigned>(-1); |
226 |
} |
227 |
|
228 |
|
229 |
template <typename TypeName> |
230 |
size_t hex_decode(char* buf,
|
231 |
size_t len, |
232 |
const TypeName* src,
|
233 |
const size_t srcLen) {
|
234 |
size_t i; |
235 |
for (i = 0; i < len && i * 2 + 1 < srcLen; ++i) { |
236 |
unsigned a = hex2bin(src[i * 2 + 0]); |
237 |
unsigned b = hex2bin(src[i * 2 + 1]); |
238 |
if (!~a || !~b)
|
239 |
return i;
|
240 |
buf[i] = a * 16 + b;
|
241 |
} |
242 |
|
243 |
return i;
|
244 |
} |
245 |
|
246 |
|
247 |
bool StringBytes::GetExternalParts(Handle<Value> val,
|
248 |
const char** data, |
249 |
size_t* len) { |
250 |
if (Buffer::HasInstance(val)) {
|
251 |
*data = Buffer::Data(val); |
252 |
*len = Buffer::Length(val); |
253 |
return true; |
254 |
} |
255 |
|
256 |
if (!val->IsString())
|
257 |
return false; |
258 |
|
259 |
Local<String> str = val.As<String>(); |
260 |
|
261 |
if (str->IsExternalAscii()) {
|
262 |
const String::ExternalAsciiStringResource* ext;
|
263 |
ext = str->GetExternalAsciiStringResource(); |
264 |
*data = ext->data(); |
265 |
*len = ext->length(); |
266 |
return true; |
267 |
|
268 |
} else if (str->IsExternal()) { |
269 |
const String::ExternalStringResource* ext;
|
270 |
ext = str->GetExternalStringResource(); |
271 |
*data = reinterpret_cast<const char*>(ext->data()); |
272 |
*len = ext->length(); |
273 |
return true; |
274 |
} |
275 |
|
276 |
return false; |
277 |
} |
278 |
|
279 |
|
280 |
size_t StringBytes::Write(char* buf,
|
281 |
size_t buflen, |
282 |
Handle<Value> val, |
283 |
enum encoding encoding,
|
284 |
int* chars_written) {
|
285 |
HandleScope scope(node_isolate); |
286 |
const char* data; |
287 |
size_t len = 0;
|
288 |
bool is_extern = GetExternalParts(val, &data, &len);
|
289 |
|
290 |
Local<String> str = val.As<String>(); |
291 |
len = len < buflen ? len : buflen; |
292 |
|
293 |
int flags = String::NO_NULL_TERMINATION |
|
294 |
String::HINT_MANY_WRITES_EXPECTED; |
295 |
|
296 |
switch (encoding) {
|
297 |
case ASCII:
|
298 |
case BINARY:
|
299 |
case BUFFER:
|
300 |
if (is_extern)
|
301 |
memcpy(buf, data, len); |
302 |
else
|
303 |
len = str->WriteOneByte(reinterpret_cast<uint8_t*>(buf),
|
304 |
0,
|
305 |
buflen, |
306 |
flags); |
307 |
if (chars_written != NULL) |
308 |
*chars_written = len; |
309 |
break;
|
310 |
|
311 |
case UTF8:
|
312 |
if (is_extern)
|
313 |
memcpy(buf, data, len); |
314 |
else
|
315 |
len = str->WriteUtf8(buf, buflen, chars_written, flags); |
316 |
break;
|
317 |
|
318 |
case UCS2:
|
319 |
if (is_extern)
|
320 |
memcpy(buf, data, len * 2);
|
321 |
else
|
322 |
len = str->Write(reinterpret_cast<uint16_t*>(buf), 0, buflen, flags); |
323 |
if (chars_written != NULL) |
324 |
*chars_written = len; |
325 |
len = len * sizeof(uint16_t);
|
326 |
break;
|
327 |
|
328 |
case BASE64:
|
329 |
if (is_extern) {
|
330 |
base64_decode(buf, buflen, data, len); |
331 |
} else {
|
332 |
String::Value value(str); |
333 |
len = base64_decode(buf, buflen, *value, value.length()); |
334 |
} |
335 |
if (chars_written != NULL) { |
336 |
*chars_written = len; |
337 |
} |
338 |
break;
|
339 |
|
340 |
case HEX:
|
341 |
if (is_extern) {
|
342 |
hex_decode(buf, buflen, data, len); |
343 |
} else {
|
344 |
String::Value value(str); |
345 |
len = hex_decode(buf, buflen, *value, value.length()); |
346 |
} |
347 |
if (chars_written != NULL) { |
348 |
*chars_written = len * 2;
|
349 |
} |
350 |
break;
|
351 |
|
352 |
default:
|
353 |
assert(0 && "unknown encoding"); |
354 |
break;
|
355 |
} |
356 |
|
357 |
return len;
|
358 |
} |
359 |
|
360 |
|
361 |
bool StringBytes::IsValidString(Handle<String> string, enum encoding enc) { |
362 |
if (enc == HEX && string->Length() % 2 != 0) |
363 |
return false; |
364 |
// TODO(bnoordhuis) Add BASE64 check?
|
365 |
return true; |
366 |
} |
367 |
|
368 |
|
369 |
// Quick and dirty size calculation
|
370 |
// Will always be at least big enough, but may have some extra
|
371 |
// UTF8 can be as much as 3x the size, Base64 can have 1-2 extra bytes
|
372 |
size_t StringBytes::StorageSize(Handle<Value> val, enum encoding encoding) {
|
373 |
HandleScope scope(node_isolate); |
374 |
size_t data_size = 0;
|
375 |
bool is_buffer = Buffer::HasInstance(val);
|
376 |
|
377 |
if (is_buffer && (encoding == BUFFER || encoding == BINARY)) {
|
378 |
return Buffer::Length(val);
|
379 |
} |
380 |
|
381 |
Local<String> str = val->ToString(); |
382 |
|
383 |
switch (encoding) {
|
384 |
case BINARY:
|
385 |
case BUFFER:
|
386 |
case ASCII:
|
387 |
data_size = str->Length(); |
388 |
break;
|
389 |
|
390 |
case UTF8:
|
391 |
// A single UCS2 codepoint never takes up more than 3 utf8 bytes.
|
392 |
// It is an exercise for the caller to decide when a string is
|
393 |
// long enough to justify calling Size() instead of StorageSize()
|
394 |
data_size = 3 * str->Length();
|
395 |
break;
|
396 |
|
397 |
case UCS2:
|
398 |
data_size = str->Length() * sizeof(uint16_t);
|
399 |
break;
|
400 |
|
401 |
case BASE64:
|
402 |
data_size = base64_decoded_size_fast(str->Length()); |
403 |
break;
|
404 |
|
405 |
case HEX:
|
406 |
assert(str->Length() % 2 == 0 && "invalid hex string length"); |
407 |
data_size = str->Length() / 2;
|
408 |
break;
|
409 |
|
410 |
default:
|
411 |
assert(0 && "unknown encoding"); |
412 |
break;
|
413 |
} |
414 |
|
415 |
return data_size;
|
416 |
} |
417 |
|
418 |
|
419 |
size_t StringBytes::Size(Handle<Value> val, enum encoding encoding) {
|
420 |
HandleScope scope(node_isolate); |
421 |
size_t data_size = 0;
|
422 |
bool is_buffer = Buffer::HasInstance(val);
|
423 |
|
424 |
if (is_buffer && (encoding == BUFFER || encoding == BINARY))
|
425 |
return Buffer::Length(val);
|
426 |
|
427 |
const char* data; |
428 |
if (GetExternalParts(val, &data, &data_size))
|
429 |
return data_size;
|
430 |
|
431 |
Local<String> str = val->ToString(); |
432 |
|
433 |
switch (encoding) {
|
434 |
case BINARY:
|
435 |
case BUFFER:
|
436 |
case ASCII:
|
437 |
data_size = str->Length(); |
438 |
break;
|
439 |
|
440 |
case UTF8:
|
441 |
data_size = str->Utf8Length(); |
442 |
break;
|
443 |
|
444 |
case UCS2:
|
445 |
data_size = str->Length() * sizeof(uint16_t);
|
446 |
break;
|
447 |
|
448 |
case BASE64: {
|
449 |
String::Value value(str); |
450 |
data_size = base64_decoded_size(*value, value.length()); |
451 |
break;
|
452 |
} |
453 |
|
454 |
case HEX:
|
455 |
data_size = str->Length() / 2;
|
456 |
break;
|
457 |
|
458 |
default:
|
459 |
assert(0 && "unknown encoding"); |
460 |
break;
|
461 |
} |
462 |
|
463 |
return data_size;
|
464 |
} |
465 |
|
466 |
|
467 |
|
468 |
|
469 |
static bool contains_non_ascii_slow(const char* buf, size_t len) { |
470 |
for (size_t i = 0; i < len; ++i) { |
471 |
if (buf[i] & 0x80) |
472 |
return true; |
473 |
} |
474 |
return false; |
475 |
} |
476 |
|
477 |
|
478 |
static bool contains_non_ascii(const char* src, size_t len) { |
479 |
if (len < 16) { |
480 |
return contains_non_ascii_slow(src, len);
|
481 |
} |
482 |
|
483 |
const unsigned bytes_per_word = sizeof(uintptr_t); |
484 |
const unsigned align_mask = bytes_per_word - 1; |
485 |
const unsigned unaligned = reinterpret_cast<uintptr_t>(src) & align_mask; |
486 |
|
487 |
if (unaligned > 0) { |
488 |
const unsigned n = bytes_per_word - unaligned; |
489 |
if (contains_non_ascii_slow(src, n))
|
490 |
return true; |
491 |
src += n; |
492 |
len -= n; |
493 |
} |
494 |
|
495 |
|
496 |
#if defined(__x86_64__) || defined(_WIN64)
|
497 |
const uintptr_t mask = 0x8080808080808080ll; |
498 |
#else
|
499 |
const uintptr_t mask = 0x80808080l; |
500 |
#endif
|
501 |
|
502 |
const uintptr_t* srcw = reinterpret_cast<const uintptr_t*>(src); |
503 |
|
504 |
for (size_t i = 0, n = len / bytes_per_word; i < n; ++i) { |
505 |
if (srcw[i] & mask)
|
506 |
return true; |
507 |
} |
508 |
|
509 |
const unsigned remainder = len & align_mask; |
510 |
if (remainder > 0) { |
511 |
const size_t offset = len - remainder;
|
512 |
if (contains_non_ascii_slow(src + offset, remainder))
|
513 |
return true; |
514 |
} |
515 |
|
516 |
return false; |
517 |
} |
518 |
|
519 |
|
520 |
static void force_ascii_slow(const char* src, char* dst, size_t len) { |
521 |
for (size_t i = 0; i < len; ++i) { |
522 |
dst[i] = src[i] & 0x7f;
|
523 |
} |
524 |
} |
525 |
|
526 |
|
527 |
static void force_ascii(const char* src, char* dst, size_t len) { |
528 |
if (len < 16) { |
529 |
force_ascii_slow(src, dst, len); |
530 |
return;
|
531 |
} |
532 |
|
533 |
const unsigned bytes_per_word = sizeof(uintptr_t); |
534 |
const unsigned align_mask = bytes_per_word - 1; |
535 |
const unsigned src_unalign = reinterpret_cast<uintptr_t>(src) & align_mask; |
536 |
const unsigned dst_unalign = reinterpret_cast<uintptr_t>(dst) & align_mask; |
537 |
|
538 |
if (src_unalign > 0) { |
539 |
if (src_unalign == dst_unalign) {
|
540 |
const unsigned unalign = bytes_per_word - src_unalign; |
541 |
force_ascii_slow(src, dst, unalign); |
542 |
src += unalign; |
543 |
dst += unalign; |
544 |
len -= src_unalign; |
545 |
} else {
|
546 |
force_ascii_slow(src, dst, len); |
547 |
return;
|
548 |
} |
549 |
} |
550 |
|
551 |
#if defined(__x86_64__) || defined(_WIN64)
|
552 |
const uintptr_t mask = ~0x8080808080808080ll; |
553 |
#else
|
554 |
const uintptr_t mask = ~0x80808080l; |
555 |
#endif
|
556 |
|
557 |
const uintptr_t* srcw = reinterpret_cast<const uintptr_t*>(src); |
558 |
uintptr_t* dstw = reinterpret_cast<uintptr_t*>(dst);
|
559 |
|
560 |
for (size_t i = 0, n = len / bytes_per_word; i < n; ++i) { |
561 |
dstw[i] = srcw[i] & mask; |
562 |
} |
563 |
|
564 |
const unsigned remainder = len & align_mask; |
565 |
if (remainder > 0) { |
566 |
const size_t offset = len - remainder;
|
567 |
force_ascii_slow(src + offset, dst + offset, remainder); |
568 |
} |
569 |
} |
570 |
|
571 |
|
572 |
static size_t base64_encode(const char* src, |
573 |
size_t slen, |
574 |
char* dst,
|
575 |
size_t dlen) { |
576 |
// We know how much we'll write, just make sure that there's space.
|
577 |
assert(dlen >= base64_encoded_size(slen) && |
578 |
"not enough space provided for base64 encode");
|
579 |
|
580 |
dlen = base64_encoded_size(slen); |
581 |
|
582 |
unsigned a;
|
583 |
unsigned b;
|
584 |
unsigned c;
|
585 |
unsigned i;
|
586 |
unsigned k;
|
587 |
unsigned n;
|
588 |
|
589 |
static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
590 |
"abcdefghijklmnopqrstuvwxyz"
|
591 |
"0123456789+/";
|
592 |
|
593 |
i = 0;
|
594 |
k = 0;
|
595 |
n = slen / 3 * 3; |
596 |
|
597 |
while (i < n) {
|
598 |
a = src[i + 0] & 0xff; |
599 |
b = src[i + 1] & 0xff; |
600 |
c = src[i + 2] & 0xff; |
601 |
|
602 |
dst[k + 0] = table[a >> 2]; |
603 |
dst[k + 1] = table[((a & 3) << 4) | (b >> 4)]; |
604 |
dst[k + 2] = table[((b & 0x0f) << 2) | (c >> 6)]; |
605 |
dst[k + 3] = table[c & 0x3f]; |
606 |
|
607 |
i += 3;
|
608 |
k += 4;
|
609 |
} |
610 |
|
611 |
if (n != slen) {
|
612 |
switch (slen - n) {
|
613 |
case 1: |
614 |
a = src[i + 0] & 0xff; |
615 |
dst[k + 0] = table[a >> 2]; |
616 |
dst[k + 1] = table[(a & 3) << 4]; |
617 |
dst[k + 2] = '='; |
618 |
dst[k + 3] = '='; |
619 |
break;
|
620 |
|
621 |
case 2: |
622 |
a = src[i + 0] & 0xff; |
623 |
b = src[i + 1] & 0xff; |
624 |
dst[k + 0] = table[a >> 2]; |
625 |
dst[k + 1] = table[((a & 3) << 4) | (b >> 4)]; |
626 |
dst[k + 2] = table[(b & 0x0f) << 2]; |
627 |
dst[k + 3] = '='; |
628 |
break;
|
629 |
} |
630 |
} |
631 |
|
632 |
return dlen;
|
633 |
} |
634 |
|
635 |
|
636 |
static size_t hex_encode(const char* src, size_t slen, char* dst, size_t dlen) { |
637 |
// We know how much we'll write, just make sure that there's space.
|
638 |
assert(dlen >= slen * 2 &&
|
639 |
"not enough space provided for hex encode");
|
640 |
|
641 |
dlen = slen * 2;
|
642 |
for (uint32_t i = 0, k = 0; k < dlen; i += 1, k += 2) { |
643 |
static const char hex[] = "0123456789abcdef"; |
644 |
uint8_t val = static_cast<uint8_t>(src[i]);
|
645 |
dst[k + 0] = hex[val >> 4]; |
646 |
dst[k + 1] = hex[val & 15]; |
647 |
} |
648 |
|
649 |
return dlen;
|
650 |
} |
651 |
|
652 |
|
653 |
|
654 |
Local<Value> StringBytes::Encode(const char* buf, |
655 |
size_t buflen, |
656 |
enum encoding encoding) {
|
657 |
HandleScope scope(node_isolate); |
658 |
|
659 |
assert(buflen <= Buffer::kMaxLength); |
660 |
if (!buflen && encoding != BUFFER)
|
661 |
return scope.Close(String::Empty(node_isolate));
|
662 |
|
663 |
Local<String> val; |
664 |
switch (encoding) {
|
665 |
case BUFFER:
|
666 |
return scope.Close(Buffer::New(buf, buflen));
|
667 |
|
668 |
case ASCII:
|
669 |
if (contains_non_ascii(buf, buflen)) {
|
670 |
char* out = new char[buflen]; |
671 |
force_ascii(buf, out, buflen); |
672 |
if (buflen < EXTERN_APEX) {
|
673 |
val = OneByteString(node_isolate, out, buflen); |
674 |
delete[] out;
|
675 |
} else {
|
676 |
val = ExternOneByteString::New(out, buflen); |
677 |
} |
678 |
} else {
|
679 |
if (buflen < EXTERN_APEX)
|
680 |
val = OneByteString(node_isolate, buf, buflen); |
681 |
else
|
682 |
val = ExternOneByteString::NewFromCopy(buf, buflen); |
683 |
} |
684 |
break;
|
685 |
|
686 |
case UTF8:
|
687 |
val = String::NewFromUtf8(node_isolate, |
688 |
buf, |
689 |
String::kNormalString, |
690 |
buflen); |
691 |
break;
|
692 |
|
693 |
case BINARY:
|
694 |
if (buflen < EXTERN_APEX)
|
695 |
val = OneByteString(node_isolate, buf, buflen); |
696 |
else
|
697 |
val = ExternOneByteString::NewFromCopy(buf, buflen); |
698 |
break;
|
699 |
|
700 |
case BASE64: {
|
701 |
size_t dlen = base64_encoded_size(buflen); |
702 |
char* dst = new char[dlen]; |
703 |
|
704 |
size_t written = base64_encode(buf, buflen, dst, dlen); |
705 |
assert(written == dlen); |
706 |
|
707 |
if (dlen < EXTERN_APEX) {
|
708 |
val = OneByteString(node_isolate, dst, dlen); |
709 |
delete[] dst;
|
710 |
} else {
|
711 |
val = ExternOneByteString::New(dst, dlen); |
712 |
} |
713 |
break;
|
714 |
} |
715 |
|
716 |
case UCS2: {
|
717 |
const uint16_t* out = reinterpret_cast<const uint16_t*>(buf); |
718 |
if (buflen < EXTERN_APEX)
|
719 |
val = String::NewFromTwoByte(node_isolate, |
720 |
out, |
721 |
String::kNormalString, |
722 |
buflen / 2);
|
723 |
else
|
724 |
val = ExternTwoByteString::NewFromCopy(out, buflen / 2);
|
725 |
break;
|
726 |
} |
727 |
|
728 |
case HEX: {
|
729 |
size_t dlen = buflen * 2;
|
730 |
char* dst = new char[dlen]; |
731 |
size_t written = hex_encode(buf, buflen, dst, dlen); |
732 |
assert(written == dlen); |
733 |
|
734 |
if (dlen < EXTERN_APEX) {
|
735 |
val = OneByteString(node_isolate, dst, dlen); |
736 |
delete[] dst;
|
737 |
} else {
|
738 |
val = ExternOneByteString::New(dst, dlen); |
739 |
} |
740 |
break;
|
741 |
} |
742 |
|
743 |
default:
|
744 |
assert(0 && "unknown encoding"); |
745 |
break;
|
746 |
} |
747 |
|
748 |
return scope.Close(val);
|
749 |
} |
750 |
|
751 |
} // namespace node
|