/** * @license Apache-2.0 * * Copyright (c) 2018 The Stdlib Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict'; // MODULES // var isNonNegativeInteger = require( '@stdlib/assert/is-nonnegative-integer' ).isPrimitive; var isCollection = require( '@stdlib/assert/is-collection' ); var format = require( './../../format' ); var UNICODE_MAX = require( '@stdlib/constants/unicode/max' ); var UNICODE_MAX_BMP = require( '@stdlib/constants/unicode/max-bmp' ); // VARIABLES // var fromCharCode = String.fromCharCode; // Factor to rescale a code point from a supplementary plane: var Ox10000 = 0x10000|0; // 65536 // Factor added to obtain a high surrogate: var OxD800 = 0xD800|0; // 55296 // Factor added to obtain a low surrogate: var OxDC00 = 0xDC00|0; // 56320 // 10-bit mask: 2^10-1 = 1023 => 0x3ff => 00000000 00000000 00000011 11111111 var Ox3FF = 1023|0; // MAIN // /** * Creates a string from a sequence of Unicode code points. * * ## Notes * * - UTF-16 encoding uses one 16-bit unit for non-surrogates (U+0000 to U+D7FF and U+E000 to U+FFFF). * - UTF-16 encoding uses two 16-bit units (surrogate pairs) for U+10000 to U+10FFFF and encodes U+10000-U+10FFFF by subtracting 0x10000 from the code point, expressing the result as a 20-bit binary, and splitting the 20 bits of 0x0-0xFFFFF as upper and lower 10-bits. The respective 10-bits are stored in two 16-bit words: a high and a low surrogate. * * * @param {...NonNegativeInteger} args - sequence of code points * @throws {Error} must provide either an array-like object of code points or one or more code points as separate arguments * @throws {TypeError} a code point must be a nonnegative integer * @throws {RangeError} must provide a valid Unicode code point * @returns {string} created string * * @example * var str = fromCodePoint( 9731 ); * // returns '☃' */ function fromCodePoint( args ) { var len; var str; var arr; var low; var hi; var pt; var i; len = arguments.length; if ( len === 1 && isCollection( args ) ) { arr = arguments[ 0 ]; len = arr.length; } else { arr = []; for ( i = 0; i < len; i++ ) { arr.push( arguments[ i ] ); } } if ( len === 0 ) { throw new Error( 'insufficient arguments. Must provide either an array of code points or one or more code points as separate arguments.' ); } str = ''; for ( i = 0; i < len; i++ ) { pt = arr[ i ]; if ( !isNonNegativeInteger( pt ) ) { throw new TypeError( format( 'invalid argument. Must provide valid code points (i.e., nonnegative integers). Value: `%s`.', pt ) ); } if ( pt > UNICODE_MAX ) { throw new RangeError( format( 'invalid argument. Must provide a valid code point (i.e., cannot exceed %u). Value: `%s`.', UNICODE_MAX, pt ) ); } if ( pt <= UNICODE_MAX_BMP ) { str += fromCharCode( pt ); } else { // Code point from a supplementary plane. Split into two 16-bit code units (surrogate pair). pt -= Ox10000; hi = (pt >> 10) + OxD800; low = (pt & Ox3FF) + OxDC00; str += fromCharCode( hi, low ); } } return str; } // EXPORTS // module.exports = fromCodePoint;