Skip to content

Commit a4174d1

Browse files
committed
Properly encode strings to UTF8 bytes, see #4
1 parent 407d01b commit a4174d1

File tree

7 files changed

+344
-61
lines changed

7 files changed

+344
-61
lines changed

bcrypt.js

Lines changed: 146 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,141 @@
157157
}
158158
return res;
159159
};
160+
// ref: http://mths.be/fromcodepoint v0.1.0 by @mathias
161+
/* if (!String.fromCodePoint) {
162+
(function () {
163+
var defineProperty = (function () {
164+
// IE 8 only supports `Object.defineProperty` on DOM elements
165+
try {
166+
var object = {};
167+
var $defineProperty = Object.defineProperty;
168+
var result = $defineProperty(object, object, object) && $defineProperty;
169+
} catch (error) {
170+
}
171+
return result;
172+
}());
173+
var stringFromCharCode = String.fromCharCode;
174+
var floor = Math.floor;
175+
var fromCodePoint = function () {
176+
var MAX_SIZE = 0x4000;
177+
var codeUnits = [];
178+
var highSurrogate;
179+
var lowSurrogate;
180+
var index = -1;
181+
var length = arguments.length;
182+
if (!length)
183+
return '';
184+
var result = '';
185+
while (++index < length) {
186+
var codePoint = Number(arguments[index]);
187+
if (
188+
!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
189+
codePoint < 0 || // not a valid Unicode code point
190+
codePoint > 0x10FFFF || // not a valid Unicode code point
191+
floor(codePoint) != codePoint // not an integer
192+
) {
193+
throw RangeError('Invalid code point: ' + codePoint);
194+
}
195+
if (codePoint <= 0xFFFF) { // BMP code point
196+
codeUnits.push(codePoint);
197+
} else { // Astral code point; split in surrogate halves
198+
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
199+
codePoint -= 0x10000;
200+
highSurrogate = (codePoint >> 10) + 0xD800;
201+
lowSurrogate = (codePoint % 0x400) + 0xDC00;
202+
codeUnits.push(highSurrogate, lowSurrogate);
203+
}
204+
if (index + 1 == length || codeUnits.length > MAX_SIZE) {
205+
result += stringFromCharCode.apply(null, codeUnits);
206+
codeUnits.length = 0;
207+
}
208+
}
209+
return result;
210+
};
211+
if (defineProperty) {
212+
defineProperty(String, 'fromCodePoint', {
213+
'value': fromCodePoint,
214+
'configurable': true,
215+
'writable': true
216+
});
217+
} else {
218+
String["fromCodePoint"] = fromCodePoint;
219+
}
220+
}());
221+
} */
222+
223+
// ref: http://mths.be/codepointat v0.1.0 by @mathias
224+
if (!String.prototype.codePointAt) {
225+
(function() {
226+
'use strict'; // needed to support `apply`/`call` with `undefined`/`null`
227+
var codePointAt = function(position) {
228+
if (this == null) {
229+
throw TypeError();
230+
}
231+
var string = String(this);
232+
var size = string.length;
233+
// `ToInteger`
234+
var index = position ? Number(position) : 0;
235+
if (index != index) { // better `isNaN`
236+
index = 0;
237+
}
238+
// Account for out-of-bounds indices:
239+
if (index < 0 || index >= size) {
240+
return undefined;
241+
}
242+
// Get the first code unit
243+
var first = string.charCodeAt(index);
244+
var second;
245+
if ( // check if it’s the start of a surrogate pair
246+
first >= 0xD800 && first <= 0xDBFF && // high surrogate
247+
size > index + 1 // there is a next code unit
248+
) {
249+
second = string.charCodeAt(index + 1);
250+
if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate
251+
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
252+
return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
253+
}
254+
}
255+
return first;
256+
};
257+
if (Object.defineProperty) {
258+
Object.defineProperty(String.prototype, 'codePointAt', {
259+
'value': codePointAt,
260+
'configurable': true,
261+
'writable': true
262+
});
263+
} else {
264+
String.prototype["codePointAt"] = codePointAt;
265+
}
266+
}());
267+
}
268+
269+
/**
270+
* Encodes a unicode code point to bytes.
271+
* @param {number} codePoint Code point to encode
272+
* @param {Array.<number>} out Output array
273+
*/
274+
function utf8_encode_char(codePoint, out) {
275+
if (codePoint < 0)
276+
throw RangeError("Illegal code point: "+codePoint);
277+
if (codePoint < 0x80) {
278+
out.push( codePoint &0x7F);
279+
} else if (codePoint < 0x800) {
280+
out.push(((codePoint>>6 )&0x1F)|0xC0);
281+
out.push(( codePoint &0x3F)|0x80);
282+
} else if (codePoint < 0x10000) {
283+
out.push(((codePoint>>12)&0x0F)|0xE0);
284+
out.push(((codePoint>>6 )&0x3F)|0x80);
285+
out.push(( codePoint &0x3F)|0x80);
286+
} else if (codePoint < 0x110000) {
287+
out.push(((codePoint>>18)&0x07)|0xF0);
288+
out.push(((codePoint>>12)&0x3F)|0x80);
289+
out.push(((codePoint>>6 )&0x3F)|0x80);
290+
out.push(( codePoint &0x3F)|0x80);
291+
} else
292+
throw RangeError("Illegal code point: "+codePoint);
293+
}
294+
160295
/**
161296
* bcrypt namespace.
162297
* @type {Object.<string,*>}
@@ -656,18 +791,17 @@
656791
}
657792
}
658793

659-
function _stringToBytes ( str ) {
660-
var ch, st, re = [];
661-
for (var i = 0; i < str.length; i++ ) {
662-
ch = str.charCodeAt(i);
663-
st = [];
664-
do {
665-
st.push( ch & 0xFF );
666-
ch = ch >> 8;
667-
} while (ch);
668-
re = re.concat( st.reverse() );
669-
}
670-
return re;
794+
function _stringToBytes(str) {
795+
var cp, out = [];
796+
for (var i=0; i<str.length; i++) {
797+
cp = str.charCodeAt(i);
798+
if (cp >= 0xD800 && cp <= 0xDFFF) {
799+
cp = str.codePointAt(i);
800+
if (cp > 0xFFFF) i++;
801+
}
802+
utf8_encode_char(cp, out);
803+
}
804+
return out;
671805
}
672806

673807
/**

0 commit comments

Comments
 (0)