Text encoding, binary data conversion, and URI handling utilities providing comprehensive data transformation capabilities with .NET compatibility for encoding operations and binary serialization.
Text encoding and decoding utilities for converting between strings and byte arrays with UTF-8 support.
// Get UTF-8 encoder instance
function get_UTF8(): any;
// Convert string to UTF-8 bytes
function toBytes(str: string): Uint8Array;
// Convert UTF-8 bytes to string
function toString(bytes: Uint8Array): string;
// Usage
import { get_UTF8, toBytes, toString } from "fable-library/Encoding.js";
// Get UTF-8 encoder (for compatibility with .NET Encoding.UTF8)
const utf8Encoder = get_UTF8();
// String to bytes conversion
const text = "Hello, 世界! 🌍";
const bytes = toBytes(text);
console.log(bytes); // Uint8Array with UTF-8 encoded bytes
// Bytes to string conversion
const decoded = toString(bytes);
console.log(decoded); // "Hello, 世界! 🌍"
console.log(text === decoded); // true
// Handle different character sets
const asciiText = "Hello World";
const asciiBytes = toBytes(asciiText);
console.log(asciiBytes.length); // 11 bytes (1 byte per ASCII character)
const unicodeText = "Héllo Wörld";
const unicodeBytes = toBytes(unicodeText);
console.log(unicodeBytes.length); // 13 bytes (accented characters need 2 bytes)
const emojiText = "Hello 😀";
const emojiBytes = toBytes(emojiText);
console.log(emojiBytes.length); // 10 bytes (emoji needs 4 bytes)import { toBytes, toString } from "fable-library/Encoding.js";
// Safe text roundtrip function
function safeTextRoundtrip(text: string): boolean {
try {
const encoded = toBytes(text);
const decoded = toString(encoded);
return text === decoded;
} catch (error) {
console.error("Encoding error:", error);
return false;
}
}
// Test various text types
const testTexts = [
"ASCII text",
"Latin characters: café, naïve",
"Cyrillic: Привет мир",
"Chinese: 你好世界",
"Japanese: こんにちは世界",
"Arabic: مرحبا بالعالم",
"Emojis: 🎉🌟🚀💻",
"Mixed: Hello世界🌍"
];
testTexts.forEach(text => {
const isValid = safeTextRoundtrip(text);
console.log(`"${text}" - Valid roundtrip: ${isValid}`);
});import { toBytes, toString } from "fable-library/Encoding.js";
// Prepare text for binary transmission
function prepareForTransmission(data: any): Uint8Array {
const jsonString = JSON.stringify(data);
return toBytes(jsonString);
}
// Restore data from binary transmission
function restoreFromTransmission(bytes: Uint8Array): any {
const jsonString = toString(bytes);
return JSON.parse(jsonString);
}
// Example usage
const originalData = {
id: 123,
name: "José María",
location: "São Paulo",
tags: ["développement", "软件", "🚀"],
timestamp: new Date().toISOString()
};
// Prepare for transmission
const transmissionBytes = prepareForTransmission(originalData);
console.log(`Data size: ${transmissionBytes.length} bytes`);
// Simulate transmission (e.g., over network, file storage)
// ... bytes would be transmitted/stored ...
// Restore data
const restoredData = restoreFromTransmission(transmissionBytes);
console.log("Original:", originalData);
console.log("Restored:", restoredData);
console.log("Equal:", JSON.stringify(originalData) === JSON.stringify(restoredData));Binary data conversion utilities matching .NET BitConverter for converting between primitive types and byte arrays.
// System endianness flag
const isLittleEndian: boolean;
// Usage
import { isLittleEndian } from "fable-library/BitConverter.js";
console.log(`System is ${isLittleEndian ? 'little' : 'big'} endian`);
// Most modern systems are little endian// Convert boolean to byte array
function getBytesBoolean(value: boolean): number[];
// Convert byte array to boolean
function toBoolean(bytes: ArrayLike<number>, startIndex: number): boolean;
// Usage
import { getBytesBoolean, toBoolean } from "fable-library/BitConverter.js";
// Boolean to bytes
const trueBytes = getBytesBoolean(true); // [1]
const falseBytes = getBytesBoolean(false); // [0]
// Bytes to boolean
const value1 = toBoolean([1], 0); // true
const value2 = toBoolean([0], 0); // false
const value3 = toBoolean([42], 0); // true (non-zero is true)
console.log(trueBytes); // [1]
console.log(falseBytes); // [0]
console.log(value1, value2, value3); // true false true// Convert character to byte array
function getBytesChar(value: string): number[];
// Convert byte array to character
function toChar(bytes: ArrayLike<number>, startIndex: number): string;
// Usage
import { getBytesChar, toChar } from "fable-library/BitConverter.js";
// Character to bytes (UTF-16 encoding)
const charBytes = getBytesChar('A'); // [65, 0] (little endian)
const unicodeBytes = getBytesChar('€'); // [172, 32] (Euro symbol)
// Bytes to character
const char1 = toChar([65, 0], 0); // 'A'
const char2 = toChar([172, 32], 0); // '€'
console.log(charBytes); // [65, 0]
console.log(unicodeBytes); // [172, 32]
console.log(char1, char2); // A €// 16-bit integers
function getBytesInt16(value: number): number[];
function getBytesUInt16(value: number): number[];
function toInt16(bytes: ArrayLike<number>, startIndex: number): number;
function toUInt16(bytes: ArrayLike<number>, startIndex: number): number;
// 32-bit integers
function getBytesInt32(value: number): number[];
function getBytesUInt32(value: number): number[];
function toInt32(bytes: ArrayLike<number>, startIndex: number): number;
function toUInt32(bytes: ArrayLike<number>, startIndex: number): number;
// Usage
import {
getBytesInt16, getBytesInt32,
toInt16, toInt32
} from "fable-library/BitConverter.js";
// 16-bit conversions
const int16Value = 12345;
const int16Bytes = getBytesInt16(int16Value); // [57, 48] (little endian)
const restoredInt16 = toInt16(int16Bytes, 0); // 12345
// 32-bit conversions
const int32Value = 123456789;
const int32Bytes = getBytesInt32(int32Value); // [21, 205, 91, 7]
const restoredInt32 = toInt32(int32Bytes, 0); // 123456789
// Negative numbers
const negativeValue = -12345;
const negativeBytes = getBytesInt16(negativeValue);
const restoredNegative = toInt16(negativeBytes, 0); // -12345
console.log(`${int16Value} -> [${int16Bytes}] -> ${restoredInt16}`);
console.log(`${int32Value} -> [${int32Bytes}] -> ${restoredInt32}`);
console.log(`${negativeValue} -> [${negativeBytes}] -> ${restoredNegative}`);// 64-bit integers (using Long type)
function getBytesInt64(value: Long): number[];
function getBytesUInt64(value: Long): number[];
function toInt64(bytes: ArrayLike<number>, startIndex: number): Long;
function toUInt64(bytes: ArrayLike<number>, startIndex: number): Long;
// Usage
import { getBytesInt64, toInt64 } from "fable-library/BitConverter.js";
import { fromString, toString } from "fable-library/Long.js";
// 64-bit conversion
const longValue = fromString("9223372036854775807"); // Max int64
const longBytes = getBytesInt64(longValue);
const restoredLong = toInt64(longBytes, 0);
console.log(`Original: ${toString(longValue)}`);
console.log(`Bytes: [${longBytes.join(', ')}]`);
console.log(`Restored: ${toString(restoredLong)}`);
console.log(`Equal: ${toString(longValue) === toString(restoredLong)}`);// Single precision (32-bit float)
function getBytesSingle(value: number): number[];
function toSingle(bytes: ArrayLike<number>, startIndex: number): number;
// Double precision (64-bit float)
function getBytesDouble(value: number): number[];
function toDouble(bytes: ArrayLike<number>, startIndex: number): number;
// Usage
import {
getBytesSingle, getBytesDouble,
toSingle, toDouble
} from "fable-library/BitConverter.js";
// Single precision
const floatValue = 3.14159;
const floatBytes = getBytesSingle(floatValue);
const restoredFloat = toSingle(floatBytes, 0);
// Double precision
const doubleValue = Math.PI;
const doubleBytes = getBytesDouble(doubleValue);
const restoredDouble = toDouble(doubleBytes, 0);
console.log(`Float: ${floatValue} -> [${floatBytes}] -> ${restoredFloat}`);
console.log(`Double: ${doubleValue} -> [${doubleBytes}] -> ${restoredDouble}`);
// Special values
const specialValues = [0, -0, Infinity, -Infinity, NaN];
specialValues.forEach(value => {
const bytes = getBytesDouble(value);
const restored = toDouble(bytes, 0);
console.log(`${value} -> ${restored} (${Object.is(value, restored) ? 'exact' : 'different'})`);
});// Convert byte array to hex string representation
function toString(value: ArrayLike<number>, startIndex?: number, length?: number): string;
// Usage
import { toString, getBytesInt32, getBytesDouble } from "fable-library/BitConverter.js";
// Convert various data to hex string
const intBytes = getBytesInt32(123456789);
const floatBytes = getBytesDouble(3.14159);
console.log(`Int32 hex: ${toString(intBytes)}`);
console.log(`Double hex: ${toString(floatBytes)}`);
// Partial conversion
const largeArray = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
console.log(`Full: ${toString(largeArray)}`);
console.log(`Partial: ${toString(largeArray, 2, 4)}`); // Start at index 2, take 4 bytes
// Format with separators for readability
function toHexString(bytes: ArrayLike<number>, separator: string = "-"): string {
const hex = toString(bytes);
return hex.match(/.{2}/g)?.join(separator) || hex;
}
console.log(`Formatted: ${toHexString(intBytes)}`); // "15-CD-5B-07"
console.log(`Formatted: ${toHexString(floatBytes, " ")}`); // "6F 12 83 C0 CA 21 09 40"import {
getBytesInt32, getBytesDouble, getBytesBoolean,
toInt32, toDouble, toBoolean
} from "fable-library/BitConverter.js";
import { toBytes, toString } from "fable-library/Encoding.js";
// Serialize structured data to binary format
interface PersonData {
id: number;
age: number;
height: number;
isActive: boolean;
name: string;
}
function serializePerson(person: PersonData): Uint8Array {
const parts: number[] = [];
// Serialize fields in order
parts.push(...getBytesInt32(person.id));
parts.push(...getBytesInt32(person.age));
parts.push(...getBytesDouble(person.height));
parts.push(...getBytesBoolean(person.isActive));
// String needs length prefix
const nameBytes = toBytes(person.name);
parts.push(...getBytesInt32(nameBytes.length));
parts.push(...Array.from(nameBytes));
return new Uint8Array(parts);
}
function deserializePerson(data: Uint8Array): PersonData {
const bytes = Array.from(data);
let offset = 0;
// Deserialize fields in same order
const id = toInt32(bytes, offset); offset += 4;
const age = toInt32(bytes, offset); offset += 4;
const height = toDouble(bytes, offset); offset += 8;
const isActive = toBoolean(bytes, offset); offset += 1;
// String with length prefix
const nameLength = toInt32(bytes, offset); offset += 4;
const nameBytes = new Uint8Array(bytes.slice(offset, offset + nameLength));
const name = toString(nameBytes);
return { id, age, height, isActive, name };
}
// Usage
const originalPerson: PersonData = {
id: 12345,
age: 30,
height: 175.5,
isActive: true,
name: "José María"
};
const serialized = serializePerson(originalPerson);
console.log(`Serialized size: ${serialized.length} bytes`);
const deserialized = deserializePerson(serialized);
console.log("Original:", originalPerson);
console.log("Deserialized:", deserialized);import {
getBytesInt16, getBytesInt32,
toInt16, toInt32
} from "fable-library/BitConverter.js";
// Simple network message protocol
interface NetworkMessage {
messageType: number;
messageId: number;
payload: Uint8Array;
}
function encodeNetworkMessage(message: NetworkMessage): Uint8Array {
const header: number[] = [];
// Protocol header: type(2) + id(4) + length(4)
header.push(...getBytesInt16(message.messageType));
header.push(...getBytesInt32(message.messageId));
header.push(...getBytesInt32(message.payload.length));
// Combine header and payload
const result = new Uint8Array(header.length + message.payload.length);
result.set(header, 0);
result.set(message.payload, header.length);
return result;
}
function decodeNetworkMessage(data: Uint8Array): NetworkMessage {
const bytes = Array.from(data);
// Parse header
const messageType = toInt16(bytes, 0);
const messageId = toInt32(bytes, 2);
const payloadLength = toInt32(bytes, 6);
// Extract payload
const payload = data.slice(10, 10 + payloadLength);
return { messageType, messageId, payload };
}
// Usage
const testMessage: NetworkMessage = {
messageType: 100,
messageId: 12345,
payload: new Uint8Array([1, 2, 3, 4, 5])
};
const encoded = encodeNetworkMessage(testMessage);
console.log(`Encoded message size: ${encoded.length} bytes`);
const decoded = decodeNetworkMessage(encoded);
console.log("Original message type:", testMessage.messageType);
console.log("Decoded message type:", decoded.messageType);
console.log("Payloads equal:",
Array.from(testMessage.payload).join(',') ===
Array.from(decoded.payload).join(',')
);URI parsing, validation, and manipulation utilities with support for different URI schemes and formats.
// Standard URI schemes
const uriSchemeFile: string; // "file"
const uriSchemeHttp: string; // "http"
const uriSchemeHttps: string; // "https"
// Usage
import { uriSchemeFile, uriSchemeHttp, uriSchemeHttps } from "fable-library/Uri.js";
console.log("File scheme:", uriSchemeFile); // "file"
console.log("HTTP scheme:", uriSchemeHttp); // "http"
console.log("HTTPS scheme:", uriSchemeHttps); // "https"
// Build URIs with correct schemes
const fileUri = `${uriSchemeFile}:///C:/temp/data.txt`;
const httpUri = `${uriSchemeHttp}://example.com/api/data`;
const httpsUri = `${uriSchemeHttps}://secure.example.com/api/data`;enum UriKind {
RelativeOrAbsolute, // Can be either relative or absolute
Absolute, // Must be absolute (with scheme)
Relative // Must be relative (without scheme)
}
// Usage
import { UriKind } from "fable-library/Uri.js";
// Examples of different URI kinds
const absoluteUri = "https://example.com/path"; // UriKind.Absolute
const relativeUri = "/path/to/resource"; // UriKind.Relative
const ambiguousUri = "example.com/path"; // UriKind.RelativeOrAbsolute// Check if path is UNC (Universal Naming Convention)
function isUnc(path: string): boolean;
// Validate URI scheme name
function checkSchemeName(schemeName: string): boolean;
// Validate URI format
function isWellFormedUriString(uriString: string, uriKind: UriKind): boolean;
// Usage
import { isUnc, checkSchemeName, isWellFormedUriString, UriKind } from "fable-library/Uri.js";
// UNC path checking
console.log(isUnc("\\\\server\\share\\file.txt")); // true
console.log(isUnc("C:\\local\\file.txt")); // false
console.log(isUnc("/unix/path")); // false
// Scheme validation
console.log(checkSchemeName("http")); // true
console.log(checkSchemeName("https")); // true
console.log(checkSchemeName("ftp")); // true
console.log(checkSchemeName("custom+")); // true (allows some special chars)
console.log(checkSchemeName("123invalid")); // false (can't start with digit)
console.log(checkSchemeName("")); // false (empty)
// URI format validation
const testUris = [
"https://example.com/path",
"http://localhost:8080",
"file:///C:/temp/file.txt",
"/relative/path",
"example.com",
"not a uri at all",
"ftp://user:pass@server.com:21/path"
];
testUris.forEach(uri => {
const isAbsolute = isWellFormedUriString(uri, UriKind.Absolute);
const isRelative = isWellFormedUriString(uri, UriKind.Relative);
const isEither = isWellFormedUriString(uri, UriKind.RelativeOrAbsolute);
console.log(`"${uri}"`);
console.log(` Absolute: ${isAbsolute}, Relative: ${isRelative}, Either: ${isEither}`);
});import { checkSchemeName, isWellFormedUriString, UriKind } from "fable-library/Uri.js";
// Comprehensive URI validator
function validateAndParseUri(uriString: string): {
isValid: boolean;
kind: 'absolute' | 'relative' | 'invalid';
scheme?: string;
parts?: URL | null;
error?: string;
} {
try {
// Check if it's a well-formed absolute URI
if (isWellFormedUriString(uriString, UriKind.Absolute)) {
const url = new URL(uriString);
const schemeValid = checkSchemeName(url.protocol.slice(0, -1)); // Remove ':'
return {
isValid: schemeValid,
kind: 'absolute',
scheme: url.protocol.slice(0, -1),
parts: url,
error: schemeValid ? undefined : 'Invalid scheme name'
};
}
// Check if it's a valid relative URI
if (isWellFormedUriString(uriString, UriKind.Relative)) {
return {
isValid: true,
kind: 'relative',
parts: null
};
}
return {
isValid: false,
kind: 'invalid',
error: 'Not a well-formed URI'
};
} catch (error) {
return {
isValid: false,
kind: 'invalid',
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
// Test various URIs
const testUris = [
"https://www.example.com:443/path?query=value#fragment",
"http://localhost:8080/api/v1/users",
"file:///C:/Windows/System32/notepad.exe",
"ftp://anonymous@ftp.example.com/pub/",
"/api/users/123",
"../relative/path",
"mailto:user@example.com",
"tel:+1-555-123-4567",
"invalid://bad-scheme-123/path",
"not-a-uri-at-all"
];
testUris.forEach(uri => {
const result = validateAndParseUri(uri);
console.log(`\n"${uri}"`);
console.log(` Valid: ${result.isValid}`);
console.log(` Kind: ${result.kind}`);
if (result.scheme) console.log(` Scheme: ${result.scheme}`);
if (result.parts) {
console.log(` Host: ${result.parts.hostname}`);
console.log(` Port: ${result.parts.port || 'default'}`);
console.log(` Path: ${result.parts.pathname}`);
}
if (result.error) console.log(` Error: ${result.error}`);
});import { uriSchemeHttp, uriSchemeHttps, checkSchemeName } from "fable-library/Uri.js";
// URI builder class
class UriBuilder {
private scheme: string;
private host: string;
private port?: number;
private path: string;
private query: Map<string, string>;
private fragment?: string;
constructor(baseUri?: string) {
this.scheme = uriSchemeHttp;
this.host = 'localhost';
this.path = '/';
this.query = new Map();
if (baseUri) {
this.parseUri(baseUri);
}
}
private parseUri(uri: string): void {
try {
const url = new URL(uri);
this.scheme = url.protocol.slice(0, -1);
this.host = url.hostname;
if (url.port) this.port = parseInt(url.port);
this.path = url.pathname;
this.fragment = url.hash.slice(1) || undefined;
// Parse query parameters
url.searchParams.forEach((value, key) => {
this.query.set(key, value);
});
} catch (error) {
throw new Error(`Invalid URI: ${uri}`);
}
}
setScheme(scheme: string): UriBuilder {
if (!checkSchemeName(scheme)) {
throw new Error(`Invalid scheme: ${scheme}`);
}
this.scheme = scheme;
return this;
}
setHost(host: string): UriBuilder {
this.host = host;
return this;
}
setPort(port: number): UriBuilder {
this.port = port;
return this;
}
setPath(path: string): UriBuilder {
this.path = path.startsWith('/') ? path : '/' + path;
return this;
}
addQuery(key: string, value: string): UriBuilder {
this.query.set(key, value);
return this;
}
removeQuery(key: string): UriBuilder {
this.query.delete(key);
return this;
}
setFragment(fragment: string): UriBuilder {
this.fragment = fragment;
return this;
}
build(): string {
let uri = `${this.scheme}://${this.host}`;
if (this.port) {
uri += `:${this.port}`;
}
uri += this.path;
if (this.query.size > 0) {
const queryString = Array.from(this.query.entries())
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&');
uri += `?${queryString}`;
}
if (this.fragment) {
uri += `#${encodeURIComponent(this.fragment)}`;
}
return uri;
}
}
// Usage
const apiUri = new UriBuilder()
.setScheme(uriSchemeHttps)
.setHost('api.example.com')
.setPort(443)
.setPath('/v2/users')
.addQuery('page', '1')
.addQuery('limit', '50')
.addQuery('filter', 'active')
.setFragment('results')
.build();
console.log("Built URI:", apiUri);
// "https://api.example.com:443/v2/users?page=1&limit=50&filter=active#results"
// Modify existing URI
const modifiedUri = new UriBuilder(apiUri)
.addQuery('sort', 'name')
.removeQuery('filter')
.setPath('/v2/users/search')
.build();
console.log("Modified URI:", modifiedUri);
// "https://api.example.com:443/v2/users/search?page=1&limit=50&sort=name#results"import { uriSchemeFile, isUnc } from "fable-library/Uri.js";
// Convert file paths to file URIs
function pathToFileUri(filePath: string): string {
// Handle different path formats
if (isUnc(filePath)) {
// UNC path: \\server\share\path -> file://server/share/path
const uncPath = filePath.replace(/\\/g, '/').substring(2);
return `${uriSchemeFile}://${uncPath}`;
} else if (filePath.match(/^[A-Za-z]:/)) {
// Windows absolute path: C:\path -> file:///C:/path
const windowsPath = filePath.replace(/\\/g, '/');
return `${uriSchemeFile}:///${windowsPath}`;
} else if (filePath.startsWith('/')) {
// Unix absolute path: /path -> file:///path
return `${uriSchemeFile}://${filePath}`;
} else {
// Relative path - convert to absolute first
throw new Error('Relative paths must be converted to absolute paths first');
}
}
// Convert file URI back to path
function fileUriToPath(fileUri: string): string {
if (!fileUri.startsWith(`${uriSchemeFile}:`)) {
throw new Error('Not a file URI');
}
const url = new URL(fileUri);
if (url.hostname) {
// UNC path: file://server/share/path -> \\server\share\path
return `\\\\${url.hostname}${url.pathname.replace(/\//g, '\\')}`;
} else {
// Local path
let path = decodeURIComponent(url.pathname);
if (path.match(/^\/[A-Za-z]:/)) {
// Windows: file:///C:/path -> C:\path
return path.substring(1).replace(/\//g, '\\');
} else {
// Unix: file:///path -> /path
return path;
}
}
}
// Test file path conversions
const testPaths = [
"C:\\Users\\John\\Documents\\file.txt",
"/home/user/documents/file.txt",
"\\\\server\\share\\documents\\file.txt"
];
testPaths.forEach(path => {
try {
const uri = pathToFileUri(path);
const backToPath = fileUriToPath(uri);
console.log(`Original: ${path}`);
console.log(`URI: ${uri}`);
console.log(`Back to path: ${backToPath}`);
console.log(`Roundtrip OK: ${path === backToPath}\n`);
} catch (error) {
console.log(`Error with "${path}": ${error}\n`);
}
});