/* text.cm - text conversion and formatting utilities */ var blob = use('blob') var utf8 = use('utf8') var that = this // Convert number to string with given radix function to_radix(num, radix) { if (radix < 2 || radix > 36) return null; var digits = "0123456789abcdefghijklmnopqrstuvwxyz"; var result = ""; var n = Math.trunc(num); var negative = n < 0; n = Math.abs(n); if (n == 0) return "0"; while (n > 0) { result = digits[n % radix] + result; n = Math.floor(n / radix); } return negative ? "-" + result : result; } // Insert separator every n digits from right function add_separator(str, sep, n) { if (!n || n == 0) return str; var negative = str[0] == '-'; if (negative) str = str.substring(1); var parts = str.split('.'); var integer = parts[0]; var decimal = parts[1] || ''; // Add separators to integer part var result = ""; for (var i = integer.length - 1, count = 0; i >= 0; i--) { if (count == n && i != integer.length - 1) { result = sep + result; count = 0; } result = integer[i] + result; count++; } if (decimal) result += '.' + decimal; return negative ? '-' + result : result; } // Format number with separator from left function add_separator_left(str, sep, n) { if (!n || n == 0) return str; var negative = str[0] == '-'; if (negative) str = str.substring(1); var result = ""; for (var i = 0, count = 0; i < str.length; i++) { if (count == n && i != 0) { result += sep; count = 0; } result += str[i]; count++; } return negative ? '-' + result : result; } /* -------- main text function --------------------------------------- */ function text(...arguments) { var arg = arguments[0]; // Handle blob conversion if (arg instanceof blob) { if (!stone.p(arg)) throw new Error("text: blob must be stone for reading"); var format = arguments[1]; var bit_length = arg.length; var result = ""; if (typeof format == 'string') { // Extract style from format var style = ''; for (var i = 0; i < format.length; i++) { if ((format[i] >= 'a' && format[i] <= 'z') || (format[i] >= 'A' && format[i] <= 'Z')) { style = format[i]; break; } } // Handle blob encoding styles switch (style) { case 'h': // hexadecimal return that.blob_to_hex(arg); case 't': // base32 return that.blob_to_base32(arg); case 'b': // binary for (var i = 0; i < bit_length; i++) { result += arg.read_logical(i) ? '1' : '0'; } return result; case 'o': // octal var bits = 0; var value = 0; for (var i = 0; i < bit_length; i++) { var bit = arg.read_logical(i); value = (value << 1) | (bit ? 1 : 0); bits++; if (bits == 3) { result += value.toString(); bits = 0; value = 0; } } // Handle remaining bits if (bits > 0) { value = value << (3 - bits); result += value.toString(); } return result; } } // Default: interpret as UTF-8 text // Use the utf8 module to decode the blob return utf8.decode(arg); } // Handle array conversion if (Array.isArray(arg)) { var separator = arguments[1] || ""; // Check if all items are valid codepoints var all_codepoints = true; for (var i = 0; i < arg.length; i++) { var item = arg[i]; if (!(typeof item == 'number' && item >= 0 && item <= 0x10FFFF && item == Math.floor(item))) { all_codepoints = false; break; } } if (all_codepoints && separator == "") { // Use utf8 module to convert codepoints to string return utf8.from_codepoints(arg); } else { // General array to string conversion var result = ""; for (var i = 0; i < arg.length; i++) { if (i > 0) result += separator; var item = arg[i]; if (typeof item == 'number' && item >= 0 && item <= 0x10FFFF && item == Math.floor(item)) { // Single codepoint - use utf8 module result += utf8.from_codepoints([item]); } else { result += String(item); } } return result; } } // Handle number conversion if (typeof arg == 'number') { var format = arguments[1]; // Simple radix conversion if (typeof format == 'number') { return to_radix(arg, format); } // Format string conversion if (typeof format == 'string') { return format_number(arg, format); } // Default conversion return String(arg); } // Handle text operations if (typeof arg == 'string') { if (arguments.length == 1) return arg; var from = arguments[1]; var to = arguments[2]; if (typeof from != 'number' || typeof to != 'number') return arg; var len = arg.length; // Adjust negative indices if (from < 0) from += len; if (to < 0) to += len; // Default values if (from == null) from = 0; if (to == null) to = len; // Validate range if (from < 0 || from > to || to > len) return null; return arg.substring(from, to); } return null; } /* -------- number formatting ---------------------------------------- */ function format_number(num, format) { // Parse format string var separation = 0; var style = ''; var places = 0; var i = 0; // Parse separation digit if (i < format.length && format[i] >= '0' && format[i] <= '9') { separation = parseInt(format[i]); i++; } // Parse style letter if (i < format.length) { style = format[i]; i++; } else { return null; } // Parse places digits if (i < format.length && format[i] >= '0' && format[i] <= '9') { places = parseInt(format[i]); i++; if (i < format.length && format[i] >= '0' && format[i] <= '9') { places = places * 10 + parseInt(format[i]); i++; } } // Invalid format if there's more if (i < format.length) return null; // Real number styles if (style == 'e' || style == 'n' || style == 's' || style == 'u' || style == 'd' || style == 'v' || style == 'l') { var decimal_point = '.'; var separator = ''; var default_separation = 0; var default_places = 0; switch (style) { case 'e': // exponential decimal_point = '.'; separator = ''; default_separation = 0; default_places = 0; break; case 'n': // number decimal_point = '.'; separator = ''; default_separation = 0; default_places = 0; break; case 's': // space decimal_point = '.'; separator = ' '; default_separation = 3; default_places = 0; break; case 'u': // underbar decimal_point = '.'; separator = '_'; default_separation = 0; default_places = 0; break; case 'd': // decimal decimal_point = '.'; separator = ','; default_separation = 3; default_places = 2; break; case 'v': // comma (European style) decimal_point = ','; separator = '.'; default_separation = 0; default_places = 0; break; case 'l': // locale (default to 'd' style for now) decimal_point = '.'; separator = ','; default_separation = 3; default_places = 2; break; } if (separation == 0) separation = default_separation; if (places == 0 && style != 'e' && style != 'n') places = default_places; // Format the number if (style == 'e') { // Scientific notation var str = places > 0 ? num.toExponential(places) : num.toExponential(); return str; } else if (style == 'n' && (Math.abs(num) >= 1e21 || (Math.abs(num) < 1e-6 && num != 0))) { // Use scientific notation for extreme values return num.toExponential(); } else { // Regular decimal formatting var str; if (places > 0) { str = num.toFixed(places); } else { str = num.toString(); } // Replace decimal point if needed if (decimal_point != '.') { str = str.replace('.', decimal_point); } // Add separators if (separation > 0 && separator) { str = add_separator(str, separator, separation); } return str; } } // Integer styles if (style == 'i' || style == 'b' || style == 'o' || style == 'h' || style == 't') { var radix = 10; var default_separation = 0; var default_places = 1; switch (style) { case 'i': // integer radix = 10; default_separation = 0; default_places = 1; break; case 'b': // binary radix = 2; default_separation = 0; default_places = 1; break; case 'o': // octal radix = 8; default_separation = 0; default_places = 1; break; case 'h': // hexadecimal radix = 16; default_separation = 0; default_places = 1; break; case 't': // base32 radix = 32; default_separation = 0; default_places = 1; break; } if (separation == 0) separation = default_separation; if (places == 0) places = default_places; // Convert to integer var n = Math.trunc(num); var str = to_radix(n, radix).toUpperCase(); // Pad with zeros if needed var negative = str[0] == '-'; if (negative) str = str.substring(1); while (str.length < places) { str = '0' + str; } // Add separators if (separation > 0) { str = add_separator_left(str, '_', separation); } return negative ? '-' + str : str; } return null; } text.base32_to_blob = that.base32_to_blob text.base64_to_blob = that.base64_to_blob text.base64url_to_blob = that.base64url_to_blob text.blob_to_base64 = that.blob_to_base64 text.blob_to_base64url = that.blob_to_base64url return text;