| … | … | @@ -0,0 +1,668 @@ |
| 1 | #!/usr/bin/env node | |
| 2 | ||
| 3 | import { openDB, listApps, getCorestoreStats, formatBytes } from '../lib/rocksdb-inspector.js' | |
| 4 | import { findCorestores, getDirSize, getLastModified, isLocked, getLockHolder } from '../lib/scanner.js' | |
| 5 | import { openDrive, getDriveStats, discoverDrives } from '../lib/drive-inspector.js' | |
| 6 | import { enumerateCores } from '../lib/core-enumerator.js' | |
| 7 | import { analyzeCoreDiskUsage, formatAnalysis } from '../lib/deep-analyzer.js' | |
| 8 | import { walkHyperbeeBlocks, decodeNode, decodeDriveEntry, stripSubEncoder, decodeHeader } from '../lib/block-reader.js' | |
| 9 | import { listDriveFromDB, buildTree, formatTree, formatFlat } from '../lib/drive-ls.js' | |
| 10 | import { corestoreDU, formatDU } from '../lib/du.js' | |
| 11 | import b4a from 'b4a' | |
| 12 | import { existsSync } from 'fs' | |
| 13 | import { join, resolve } from 'path' | |
| 14 | import { homedir } from 'os' | |
| 15 | ||
| 16 | const args = process.argv.slice(2) | |
| 17 | const command = args[0] | |
| 18 | ||
| 19 | const HELP = ` | |
| 20 | hyper-inspector - Inspect, audit, and manage Hypercore corestores | |
| 21 | ||
| 22 | Commands: | |
| 23 | scan [dir] Scan a directory for all corestores (default: ~/) | |
| 24 | audit [dir] Full audit with sizes, lock status, and staleness | |
| 25 | du [dir] Full Hyperdrive disk usage across all corestores | |
| 26 | inspect <db-path> Inspect a corestore's RocksDB (path to db/ dir) | |
| 27 | apps <db-path> List pear:// apps in a platform corestore | |
| 28 | cores <storage-path> Enumerate all cores in a corestore (path with CORESTORE file) | |
| 29 | drives <storage-path> Discover all Hyperdrives in a corestore | |
| 30 | drive <storage-path> Open and traverse a Hyperdrive, list all files | |
| 31 | drive <storage-path> --key <hex> Open a specific drive by key | |
| 32 | ls <db-path> <dataPtr> List all files in a Hyperdrive (by RocksDB dataPointer) | |
| 33 | usage <db-path> Deep analysis: which cores consume how many bytes on disk | |
| 34 | blocks <storage-path> --key <hex> Read raw Hyperbee blocks, decode file entries | |
| 35 | ||
| 36 | Options: | |
| 37 | --readonly Open databases in read-only mode (for locked DBs) | |
| 38 | --limit <n> Max keys to scan (default: 100000) | |
| 39 | --key <hex> Specify a Hypercore key | |
| 40 | --help Show this help | |
| 41 | ||
| 42 | Examples: | |
| 43 | hyper-inspector scan | |
| 44 | hyper-inspector audit | |
| 45 | hyper-inspector cores ~/.config/hypernote/server-data | |
| 46 | hyper-inspector drive ~/.config/hypernote/server-data | |
| 47 | hyper-inspector inspect ~/.config/pear/corestores/platform/db --readonly | |
| 48 | hyper-inspector apps ~/.config/pear/corestores/platform/db --readonly | |
| 49 | ` | |
| 50 | ||
| 51 | if (!command || command === '--help' || command === 'help') { | |
| 52 | console.log(HELP) | |
| 53 | process.exit(0) | |
| 54 | } | |
| 55 | ||
| 56 | if (command === 'scan') { | |
| 57 | const dir = args[1] || homedir() | |
| 58 | console.log(`\nScanning ${dir} for Hypercore storage...\n`) | |
| 59 | ||
| 60 | const stores = findCorestores(dir) | |
| 61 | let totalSize = 0 | |
| 62 | ||
| 63 | for (const store of stores) { | |
| 64 | totalSize += store.size | |
| 65 | const modified = getLastModified(store.path) | |
| 66 | const modStr = modified ? modified.toISOString().slice(0, 10) : 'unknown' | |
| 67 | console.log(` ${formatBytes(store.size).padStart(10)} ${modStr} ${store.path}`) | |
| 68 | } | |
| 69 | ||
| 70 | console.log(`\n Total: ${formatBytes(totalSize)} across ${stores.length} locations\n`) | |
| 71 | } | |
| 72 | ||
| 73 | if (command === 'inspect') { | |
| 74 | const dbPath = resolve(args[1] || '') | |
| 75 | const readOnly = args.includes('--readonly') | |
| 76 | const limit = parseInt(args[args.indexOf('--limit') + 1]) || 100000 | |
| 77 | ||
| 78 | if (!dbPath || !existsSync(dbPath)) { | |
| 79 | console.error('Error: provide a valid path to a db/ directory') | |
| 80 | process.exit(1) | |
| 81 | } | |
| 82 | ||
| 83 | const locked = isLocked(dbPath) | |
| 84 | if (locked && !readOnly) { | |
| 85 | const holder = getLockHolder(dbPath) | |
| 86 | console.error(`Database is locked by ${holder?.command} (PID ${holder?.pid})`) | |
| 87 | console.error('Use --readonly to open in read-only mode') | |
| 88 | process.exit(1) | |
| 89 | } | |
| 90 | ||
| 91 | console.log(`\nInspecting: ${dbPath}`) | |
| 92 | console.log(`Mode: ${readOnly ? 'read-only' : 'read-write'}`) | |
| 93 | ||
| 94 | const db = await openDB(dbPath, { readOnly }) | |
| 95 | const stats = await getCorestoreStats(db) | |
| 96 | ||
| 97 | console.log(`\nCorestore column family:`) | |
| 98 | console.log(` Keys scanned: ${stats.totalKeys}`) | |
| 99 | console.log(` Value bytes: ${formatBytes(stats.totalBytes)}`) | |
| 100 | ||
| 101 | console.log(`\nKey type breakdown:`) | |
| 102 | for (const t of stats.typeBreakdown) { | |
| 103 | console.log(` ${t.type.padEnd(16)} ${t.count.toString().padStart(8)} keys ${t.mb.padStart(10)} MB`) | |
| 104 | } | |
| 105 | ||
| 106 | if (stats.readable.length > 0) { | |
| 107 | console.log(`\nSample readable values (${stats.readable.length}):`) | |
| 108 | for (const r of stats.readable.slice(0, 30)) { | |
| 109 | const label = KEY_TYPES_LABEL[r.type] || `0x${r.type.toString(16)}` | |
| 110 | try { | |
| 111 | const obj = JSON.parse(r.value) | |
| 112 | if (obj.slug || obj.name || obj.title || obj.id) { | |
| 113 | console.log(` [${label}] ${obj.title || obj.name || obj.slug || obj.id}`) | |
| 114 | continue | |
| 115 | } | |
| 116 | } catch {} | |
| 117 | console.log(` [${label}] ${r.value.slice(0, 100)}`) | |
| 118 | } | |
| 119 | } | |
| 120 | ||
| 121 | const parentPath = dbPath.replace(/\/db\/?$/, '') | |
| 122 | const diskSize = getDirSize(parentPath) | |
| 123 | console.log(`\nDisk usage: ${formatBytes(diskSize)}`) | |
| 124 | ||
| 125 | await db.close() | |
| 126 | } | |
| 127 | ||
| 128 | if (command === 'apps') { | |
| 129 | const dbPath = resolve(args[1] || '') | |
| 130 | const readOnly = args.includes('--readonly') | |
| 131 | ||
| 132 | if (!dbPath || !existsSync(dbPath)) { | |
| 133 | console.error('Error: provide a valid path to a platform db/ directory') | |
| 134 | process.exit(1) | |
| 135 | } | |
| 136 | ||
| 137 | const db = await openDB(dbPath, { readOnly }) | |
| 138 | const { pearApps, fileSources } = await listApps(db) | |
| 139 | ||
| 140 | const appRegistrations = pearApps.filter(a => a.prefix === 0x03) | |
| 141 | const appVersions = pearApps.filter(a => a.prefix === 0x06) | |
| 142 | ||
| 143 | console.log(`\n=== Pear Apps (${appRegistrations.length} registered) ===\n`) | |
| 144 | for (const app of appRegistrations) { | |
| 145 | const versionEntry = appVersions.find(v => v.url === app.url) | |
| 146 | const hasVersion = versionEntry ? 'active' : 'no-version' | |
| 147 | const dkeyMatch = app.storage.match(/by-dkey\/([a-f0-9]+)/) | |
| 148 | const randomMatch = app.storage.match(/by-random\/([a-f0-9]+)/) | |
| 149 | const storageId = dkeyMatch ? `dkey:${dkeyMatch[1].slice(0, 12)}...` : randomMatch ? `random:${randomMatch[1].slice(0, 12)}...` : '' | |
| 150 | ||
| 151 | const storagePath = app.storage.match(/\/home\/[^\s|]+/)?.[0] | |
| 152 | let sizeStr = '' | |
| 153 | if (storagePath && existsSync(storagePath)) { | |
| 154 | sizeStr = formatBytes(getDirSize(storagePath)) | |
| 155 | } | |
| 156 | ||
| 157 | console.log(` ${app.url}`) | |
| 158 | console.log(` status: ${hasVersion} storage: ${storageId} size: ${sizeStr || 'n/a'}`) | |
| 159 | } | |
| 160 | ||
| 161 | console.log(`\n=== Staging Sources (${fileSources.length}) ===\n`) | |
| 162 | for (const f of fileSources) { | |
| 163 | console.log(` ${f.path}`) | |
| 164 | } | |
| 165 | ||
| 166 | await db.close() | |
| 167 | } | |
| 168 | ||
| 169 | if (command === 'cores') { | |
| 170 | const storagePath = resolve(args[1] || '') | |
| 171 | const readOnly = args.includes('--readonly') | |
| 172 | ||
| 173 | if (!storagePath || !existsSync(storagePath)) { | |
| 174 | console.error('Error: provide a valid corestore path (directory with CORESTORE file)') | |
| 175 | process.exit(1) | |
| 176 | } | |
| 177 | ||
| 178 | const dbPath = join(storagePath, 'db') | |
| 179 | const locked = isLocked(dbPath) | |
| 180 | ||
| 181 | if (locked && !readOnly) { | |
| 182 | const holder = getLockHolder(dbPath) | |
| 183 | console.error(`Database is locked by ${holder?.command} (PID ${holder?.pid})`) | |
| 184 | console.error('Use --readonly to open in read-only mode') | |
| 185 | process.exit(1) | |
| 186 | } | |
| 187 | ||
| 188 | console.log(`\nEnumerating cores in: ${storagePath}`) | |
| 189 | console.log(`Mode: ${readOnly ? 'read-only' : 'read-write'}\n`) | |
| 190 | ||
| 191 | const cores = await enumerateCores(dbPath, { readOnly }) | |
| 192 | ||
| 193 | console.log(`Found ${cores.length} cores:\n`) | |
| 194 | for (const core of cores) { | |
| 195 | const alias = core.alias | |
| 196 | ? `${core.alias.name} (ns: ${core.alias.namespace.slice(0, 8)}...)` | |
| 197 | : '(unnamed)' | |
| 198 | const key = core.publicKey ? `key: ${core.publicKey.slice(0, 16)}...` : '' | |
| 199 | const len = core.length != null ? `len: ${core.length}` : '' | |
| 200 | console.log(` #${String(core.corePointer).padEnd(4)} ${alias.padEnd(30)} ${len.padEnd(12)} ${key}`) | |
| 201 | console.log(` dkey: ${core.discoveryKey.slice(0, 32)}...`) | |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 | if (command === 'drives') { | |
| 206 | const storagePath = resolve(args[1] || '') | |
| 207 | const readOnly = args.includes('--readonly') | |
| 208 | ||
| 209 | if (!storagePath || !existsSync(storagePath)) { | |
| 210 | console.error('Error: provide a valid corestore path') | |
| 211 | process.exit(1) | |
| 212 | } | |
| 213 | ||
| 214 | console.log(`\nDiscovering Hyperdrives in: ${storagePath}\n`) | |
| 215 | ||
| 216 | const drives = await discoverDrives(storagePath, { readOnly }) | |
| 217 | ||
| 218 | if (drives.length === 0) { | |
| 219 | console.log('No Hyperdrives found (data may be Autobase/Hyperbee, not Hyperdrive)') | |
| 220 | } else { | |
| 221 | for (const d of drives) { | |
| 222 | const alias = d.alias ? d.alias.name : '(unnamed)' | |
| 223 | const rw = d.writable ? 'writable' : 'read-only' | |
| 224 | console.log(` Hyperdrive: ${alias}`) | |
| 225 | console.log(` key: ${d.key}`) | |
| 226 | console.log(` content: ${d.contentKey || 'n/a'}`) | |
| 227 | console.log(` version: ${d.version} bytes: ${formatBytes(d.byteLength)} ${rw}`) | |
| 228 | ||
| 229 | if (d.filePaths.length > 0) { | |
| 230 | console.log(` files (${d.filePaths.length} found):`) | |
| 231 | for (const f of d.filePaths.slice(0, 15)) { | |
| 232 | console.log(` ${f}`) | |
| 233 | } | |
| 234 | if (d.filePaths.length > 15) { | |
| 235 | console.log(` ... and ${d.filePaths.length - 15} more`) | |
| 236 | } | |
| 237 | } | |
| 238 | console.log() | |
| 239 | } | |
| 240 | } | |
| 241 | } | |
| 242 | ||
| 243 | if (command === 'drive') { | |
| 244 | const storagePath = resolve(args[1] || '') | |
| 245 | const keyIdx = args.indexOf('--key') | |
| 246 | const key = keyIdx >= 0 ? args[keyIdx + 1] : null | |
| 247 | ||
| 248 | if (!storagePath || !existsSync(storagePath)) { | |
| 249 | console.error('Error: provide a valid corestore path') | |
| 250 | process.exit(1) | |
| 251 | } | |
| 252 | ||
| 253 | const dbPath = join(storagePath, 'db') | |
| 254 | const locked = isLocked(dbPath) | |
| 255 | if (locked) { | |
| 256 | const holder = getLockHolder(dbPath) | |
| 257 | console.error(`Corestore is locked by ${holder?.command} (PID ${holder?.pid})`) | |
| 258 | console.error('Cannot open a Hyperdrive while another process holds the lock.') | |
| 259 | console.error('Stop the process first, or use "cores" / "inspect" with --readonly.') | |
| 260 | process.exit(1) | |
| 261 | } | |
| 262 | ||
| 263 | console.log(`\nOpening Hyperdrive from: ${storagePath}`) | |
| 264 | if (key) console.log(`Key: ${key}`) | |
| 265 | ||
| 266 | const { store, drive } = await openDrive(storagePath, { key }) | |
| 267 | ||
| 268 | console.log(`\nDrive key: ${drive.key.toString('hex')}`) | |
| 269 | console.log(`Version: ${drive.version}`) | |
| 270 | console.log(`Writable: ${drive.writable}`) | |
| 271 | ||
| 272 | const stats = await getDriveStats(drive) | |
| 273 | ||
| 274 | console.log(`\nFiles: ${stats.fileCount}`) | |
| 275 | console.log(`Total content: ${formatBytes(stats.totalBytes)}`) | |
| 276 | console.log(`Blob blocks: ${stats.blobBlocks}`) | |
| 277 | ||
| 278 | if (stats.topDirs.length > 0) { | |
| 279 | console.log(`\n--- By directory ---`) | |
| 280 | for (const d of stats.topDirs.slice(0, 15)) { | |
| 281 | console.log(` ${formatBytes(d.bytes).padStart(10)} ${d.count.toString().padStart(5)} files ${d.dir}`) | |
| 282 | } | |
| 283 | } | |
| 284 | ||
| 285 | if (stats.extensions.length > 0) { | |
| 286 | console.log(`\n--- By extension ---`) | |
| 287 | for (const e of stats.extensions.slice(0, 15)) { | |
| 288 | console.log(` ${formatBytes(e.bytes).padStart(10)} ${e.count.toString().padStart(5)} files .${e.ext}`) | |
| 289 | } | |
| 290 | } | |
| 291 | ||
| 292 | // Show first 50 files | |
| 293 | console.log(`\n--- Files (first 50 of ${stats.files.length}) ---`) | |
| 294 | for (const f of stats.files.slice(0, 50)) { | |
| 295 | const size = f.byteLength ? formatBytes(f.byteLength) : '0 B' | |
| 296 | const link = f.linkname ? ` -> ${f.linkname}` : '' | |
| 297 | const exec = f.executable ? ' *' : '' | |
| 298 | console.log(` ${size.padStart(10)} ${f.path}${exec}${link}`) | |
| 299 | } | |
| 300 | ||
| 301 | if (stats.files.length > 50) { | |
| 302 | console.log(` ... and ${stats.files.length - 50} more files`) | |
| 303 | } | |
| 304 | ||
| 305 | await drive.close() | |
| 306 | await store.close() | |
| 307 | } | |
| 308 | ||
| 309 | if (command === 'ls') { | |
| 310 | const dbPath = resolve(args[1] || '') | |
| 311 | const dataPtr = parseInt(args[2]) | |
| 312 | const readOnly = args.includes('--readonly') | |
| 313 | const flat = args.includes('--flat') | |
| 314 | const filterIdx = args.indexOf('--filter') | |
| 315 | const filter = filterIdx >= 0 ? args[filterIdx + 1] : null | |
| 316 | const depthIdx = args.indexOf('--depth') | |
| 317 | const maxDepth = depthIdx >= 0 ? parseInt(args[depthIdx + 1]) : 3 | |
| 318 | const minIdx = args.indexOf('--min') | |
| 319 | const minBytes = minIdx >= 0 ? parseSize(args[minIdx + 1]) : 0 | |
| 320 | ||
| 321 | if (!dbPath || isNaN(dataPtr)) { | |
| 322 | console.error('Usage: hyper-inspector ls <db-path> <dataPointer> [--readonly] [--flat] [--filter <path>] [--depth <n>] [--min <size>]') | |
| 323 | console.error(' dataPointer: the data pointer number from "cores" output') | |
| 324 | console.error(' --flat: flat file list sorted by size') | |
| 325 | console.error(' --filter: only show paths containing this string') | |
| 326 | console.error(' --depth: max tree depth (default: 3)') | |
| 327 | console.error(' --min: minimum file size to show (e.g., 1M, 100K)') | |
| 328 | process.exit(1) | |
| 329 | } | |
| 330 | ||
| 331 | console.log(`\nListing Hyperdrive at dataPointer ${dataPtr} in ${dbPath}\n`) | |
| 332 | ||
| 333 | const { header, files, blocksRead } = await listDriveFromDB(dbPath, dataPtr, { readOnly }) | |
| 334 | ||
| 335 | if (header) { | |
| 336 | console.log(`Hyperbee: protocol="${header.protocol}"`) | |
| 337 | if (header.contentFeed) { | |
| 338 | console.log(`Content core: ${b4a.toString(header.contentFeed, 'hex')}`) | |
| 339 | } | |
| 340 | } | |
| 341 | ||
| 342 | console.log(`Scanned ${blocksRead} blocks, found ${files.length} files`) | |
| 343 | ||
| 344 | let totalBytes = 0 | |
| 345 | for (const f of files) totalBytes += f.bytes | |
| 346 | console.log(`Total content: ${formatBytes(totalBytes)}\n`) | |
| 347 | ||
| 348 | if (files.length === 0) { | |
| 349 | console.log('No Hyperdrive file entries found.') | |
| 350 | } else if (flat) { | |
| 351 | console.log(formatFlat(files, { filter, limit: 200 })) | |
| 352 | } else { | |
| 353 | const tree = buildTree(files) | |
| 354 | console.log(formatTree(tree, { maxDepth, minBytes, showFiles: maxDepth <= 2 })) | |
| 355 | } | |
| 356 | } | |
| 357 | ||
| 358 | function parseSize (str) { | |
| 359 | if (!str) return 0 | |
| 360 | const num = parseFloat(str) | |
| 361 | if (str.endsWith('G') || str.endsWith('g')) return num * 1024 * 1024 * 1024 | |
| 362 | if (str.endsWith('M') || str.endsWith('m')) return num * 1024 * 1024 | |
| 363 | if (str.endsWith('K') || str.endsWith('k')) return num * 1024 | |
| 364 | return num | |
| 365 | } | |
| 366 | ||
| 367 | if (command === 'usage') { | |
| 368 | const dbPath = resolve(args[1] || '') | |
| 369 | const readOnly = args.includes('--readonly') | |
| 370 | const sampleIdx = args.indexOf('--sample') | |
| 371 | const sampleSize = sampleIdx >= 0 ? parseInt(args[sampleIdx + 1]) : 500000 | |
| 372 | ||
| 373 | if (!dbPath || !existsSync(dbPath)) { | |
| 374 | console.error('Error: provide a valid path to a db/ directory') | |
| 375 | process.exit(1) | |
| 376 | } | |
| 377 | ||
| 378 | console.log(`\nDeep disk usage analysis: ${dbPath}`) | |
| 379 | console.log(`Sampling up to ${sampleSize} entries...\n`) | |
| 380 | ||
| 381 | const analysis = await analyzeCoreDiskUsage(dbPath, { readOnly, sampleSize }) | |
| 382 | console.log(formatAnalysis(analysis)) | |
| 383 | } | |
| 384 | ||
| 385 | if (command === 'blocks') { | |
| 386 | const storagePath = resolve(args[1] || '') | |
| 387 | const keyIdx = args.indexOf('--key') | |
| 388 | const keyHex = keyIdx >= 0 ? args[keyIdx + 1] : null | |
| 389 | const maxIdx = args.indexOf('--max') | |
| 390 | const maxBlocks = maxIdx >= 0 ? parseInt(args[maxIdx + 1]) : 500 | |
| 391 | ||
| 392 | if (!storagePath || !keyHex) { | |
| 393 | console.error('Usage: hyper-inspector blocks <storage-path> --key <hex> [--max <n>]') | |
| 394 | process.exit(1) | |
| 395 | } | |
| 396 | ||
| 397 | const dbPath = join(storagePath, 'db') | |
| 398 | const locked = isLocked(dbPath) | |
| 399 | if (locked) { | |
| 400 | const holder = getLockHolder(dbPath) | |
| 401 | console.error(`Locked by ${holder?.command} (PID ${holder?.pid}). Cannot open core.`) | |
| 402 | process.exit(1) | |
| 403 | } | |
| 404 | ||
| 405 | const Corestore = (await import('corestore')).default | |
| 406 | const store = new Corestore(storagePath) | |
| 407 | await store.ready() | |
| 408 | ||
| 409 | const core = store.get({ key: Buffer.from(keyHex, 'hex') }) | |
| 410 | await core.ready() | |
| 411 | ||
| 412 | console.log(`\nCore: len=${core.length} bytes=${formatBytes(core.byteLength)} writable=${core.writable}`) | |
| 413 | ||
| 414 | const { header, entries } = await walkHyperbeeBlocks(core, { maxBlocks }) | |
| 415 | ||
| 416 | if (header) { | |
| 417 | console.log(`\nHyperbee header: protocol="${header.protocol}"`) | |
| 418 | if (header.contentFeed) { | |
| 419 | console.log(`Content feed: ${b4a.toString(header.contentFeed, 'hex')}`) | |
| 420 | } | |
| 421 | } | |
| 422 | ||
| 423 | console.log(`\nDecoded ${entries.length} Hyperbee entries from ${Math.min(core.length, maxBlocks)} blocks\n`) | |
| 424 | ||
| 425 | // Try to decode as Hyperdrive file entries | |
| 426 | let driveEntries = 0 | |
| 427 | let totalBlobBytes = 0 | |
| 428 | const filesByExt = new Map() | |
| 429 | const filesByDir = new Map() | |
| 430 | const allFiles = [] | |
| 431 | ||
| 432 | for (const entry of entries) { | |
| 433 | const rawKey = stripSubEncoder(entry.key) | |
| 434 | let filePath = '' | |
| 435 | try { | |
| 436 | filePath = b4a.toString(rawKey, 'utf8') | |
| 437 | } catch { | |
| 438 | continue | |
| 439 | } | |
| 440 | ||
| 441 | // Skip non-path entries | |
| 442 | if (!filePath.startsWith('/')) continue | |
| 443 | ||
| 444 | let driveEntry = null | |
| 445 | try { | |
| 446 | driveEntry = decodeDriveEntry(entry.value) | |
| 447 | } catch {} | |
| 448 | ||
| 449 | if (driveEntry) { | |
| 450 | driveEntries++ | |
| 451 | const blob = driveEntry.blob | |
| 452 | const bytes = blob?.byteLength || 0 | |
| 453 | totalBlobBytes += bytes | |
| 454 | ||
| 455 | // By extension | |
| 456 | const ext = filePath.includes('.') ? filePath.split('.').pop() : '(none)' | |
| 457 | const extInfo = filesByExt.get(ext) || { count: 0, bytes: 0 } | |
| 458 | extInfo.count++ | |
| 459 | extInfo.bytes += bytes | |
| 460 | filesByExt.set(ext, extInfo) | |
| 461 | ||
| 462 | // By top-level directory | |
| 463 | const parts = filePath.split('/') | |
| 464 | const topDir = parts.length > 2 ? '/' + parts[1] : '/' | |
| 465 | const dirInfo = filesByDir.get(topDir) || { count: 0, bytes: 0 } | |
| 466 | dirInfo.count++ | |
| 467 | dirInfo.bytes += bytes | |
| 468 | filesByDir.set(topDir, dirInfo) | |
| 469 | ||
| 470 | allFiles.push({ | |
| 471 | path: filePath, | |
| 472 | blob, | |
| 473 | executable: driveEntry.executable, | |
| 474 | linkname: driveEntry.linkname, | |
| 475 | bytes | |
| 476 | }) | |
| 477 | } | |
| 478 | } | |
| 479 | ||
| 480 | if (driveEntries > 0) { | |
| 481 | console.log(`=== Hyperdrive File Analysis ===`) | |
| 482 | console.log(`Files: ${driveEntries}`) | |
| 483 | console.log(`Total blob content: ${formatBytes(totalBlobBytes)}\n`) | |
| 484 | ||
| 485 | // By directory | |
| 486 | const sortedDirs = [...filesByDir.entries()].sort((a, b) => b[1].bytes - a[1].bytes) | |
| 487 | console.log('--- By directory ---') | |
| 488 | for (const [dir, info] of sortedDirs.slice(0, 20)) { | |
| 489 | console.log(` ${formatBytes(info.bytes).padStart(10)} ${info.count.toString().padStart(5)} files ${dir}`) | |
| 490 | } | |
| 491 | ||
| 492 | // By extension | |
| 493 | const sortedExts = [...filesByExt.entries()].sort((a, b) => b[1].bytes - a[1].bytes) | |
| 494 | console.log('\n--- By extension ---') | |
| 495 | for (const [ext, info] of sortedExts.slice(0, 20)) { | |
| 496 | console.log(` ${formatBytes(info.bytes).padStart(10)} ${info.count.toString().padStart(5)} files .${ext}`) | |
| 497 | } | |
| 498 | ||
| 499 | // Largest files | |
| 500 | allFiles.sort((a, b) => b.bytes - a.bytes) | |
| 501 | console.log('\n--- Largest files ---') | |
| 502 | for (const f of allFiles.slice(0, 20)) { | |
| 503 | const blockInfo = f.blob ? `blocks[${f.blob.blockOffset}..${f.blob.blockOffset + f.blob.blockLength}]` : '' | |
| 504 | console.log(` ${formatBytes(f.bytes).padStart(10)} ${f.path} ${blockInfo}`) | |
| 505 | } | |
| 506 | ||
| 507 | // Show blob block range summary | |
| 508 | if (allFiles.length > 0 && allFiles[0].blob) { | |
| 509 | let minBlock = Infinity | |
| 510 | let maxBlock = 0 | |
| 511 | for (const f of allFiles) { | |
| 512 | if (f.blob) { | |
| 513 | minBlock = Math.min(minBlock, f.blob.blockOffset) | |
| 514 | maxBlock = Math.max(maxBlock, f.blob.blockOffset + f.blob.blockLength) | |
| 515 | } | |
| 516 | } | |
| 517 | console.log(`\nBlob block range: ${minBlock} - ${maxBlock} (${maxBlock - minBlock} blocks in content core)`) | |
| 518 | } | |
| 519 | } else { | |
| 520 | // Not a Hyperdrive - show raw entries | |
| 521 | console.log('Not a Hyperdrive (no file path entries found).') | |
| 522 | console.log('Raw Hyperbee entries:') | |
| 523 | for (const entry of entries.slice(0, 30)) { | |
| 524 | let keyStr = '' | |
| 525 | try { | |
| 526 | keyStr = b4a.toString(entry.key, 'utf8') | |
| 527 | if (!/^[\x20-\x7e\n\r\t]+$/.test(keyStr)) { | |
| 528 | keyStr = `[hex: ${b4a.toString(entry.key, 'hex').slice(0, 40)}]` | |
| 529 | } | |
| 530 | } catch { | |
| 531 | keyStr = `[hex: ${b4a.toString(entry.key, 'hex').slice(0, 40)}]` | |
| 532 | } | |
| 533 | const valLen = entry.value ? entry.value.length : 0 | |
| 534 | console.log(` seq=${entry.seq} ${keyStr} (val: ${valLen}B)`) | |
| 535 | } | |
| 536 | } | |
| 537 | ||
| 538 | await core.close() | |
| 539 | await store.close() | |
| 540 | } | |
| 541 | ||
| 542 | if (command === 'du') { | |
| 543 | const dir = args[1] || homedir() | |
| 544 | const depthIdx = args.indexOf('--depth') | |
| 545 | const dirDepth = depthIdx >= 0 ? parseInt(args[depthIdx + 1]) : 2 | |
| 546 | const minIdx = args.indexOf('--min') | |
| 547 | const minSize = minIdx >= 0 ? parseSize(args[minIdx + 1]) : 1024 * 1024 | |
| 548 | ||
| 549 | console.log(`\n=== Hyperdrive Disk Usage: ${dir} ===\n`) | |
| 550 | ||
| 551 | const stores = findCorestores(dir) | |
| 552 | ||
| 553 | for (const store of stores) { | |
| 554 | const dbPath = join(store.path, 'db') | |
| 555 | if (!existsSync(dbPath)) { | |
| 556 | // Try nested corestore/db | |
| 557 | const nested = join(store.path, 'corestore', 'db') | |
| 558 | if (!existsSync(nested)) continue | |
| 559 | } | |
| 560 | ||
| 561 | const actualDbPath = existsSync(join(store.path, 'db')) | |
| 562 | ? join(store.path, 'db') | |
| 563 | : join(store.path, 'corestore', 'db') | |
| 564 | ||
| 565 | const diskSize = getDirSize(store.path) | |
| 566 | const locked = isLocked(actualDbPath) | |
| 567 | const lockInfo = locked ? getLockHolder(actualDbPath) : null | |
| 568 | ||
| 569 | console.log(`--- ${store.path} (${formatBytes(diskSize)} on disk)${locked ? ` [LOCKED by ${lockInfo?.command}]` : ''} ---\n`) | |
| 570 | ||
| 571 | try { | |
| 572 | const result = await corestoreDU(actualDbPath, { readOnly: true, dirDepth }) | |
| 573 | console.log(formatDU(result, { dirDepth, minSize })) | |
| 574 | } catch (err) { | |
| 575 | if (err.message.includes('Column families not opened')) { | |
| 576 | console.log(' (not a corestore RocksDB — legacy or different format)') | |
| 577 | } else { | |
| 578 | console.log(` Error: ${err.message.slice(0, 100)}`) | |
| 579 | } | |
| 580 | } | |
| 581 | ||
| 582 | console.log('') | |
| 583 | } | |
| 584 | } | |
| 585 | ||
| 586 | if (command === 'audit') { | |
| 587 | const dir = args[1] || homedir() | |
| 588 | console.log(`\n=== Hypercore Storage Audit: ${dir} ===\n`) | |
| 589 | ||
| 590 | const stores = findCorestores(dir) | |
| 591 | let totalSize = 0 | |
| 592 | const categories = { active: [], stale: [], junk: [], unknown: [] } | |
| 593 | ||
| 594 | for (const store of stores) { | |
| 595 | totalSize += store.size | |
| 596 | const modified = getLastModified(store.path) | |
| 597 | const now = new Date() | |
| 598 | const ageMs = modified ? now - modified : Infinity | |
| 599 | const ageDays = Math.floor(ageMs / (1000 * 60 * 60 * 24)) | |
| 600 | ||
| 601 | const dbPath = join(store.path, 'db') | |
| 602 | const parentDb = existsSync(dbPath) ? dbPath : join(store.path, 'corestore', 'db') | |
| 603 | const locked = existsSync(parentDb) ? isLocked(parentDb) : false | |
| 604 | const holder = locked ? getLockHolder(parentDb) : null | |
| 605 | ||
| 606 | const entry = { | |
| 607 | ...store, | |
| 608 | modified, | |
| 609 | ageDays, | |
| 610 | locked, | |
| 611 | holder, | |
| 612 | sizeFormatted: formatBytes(store.size) | |
| 613 | } | |
| 614 | ||
| 615 | if (locked) { | |
| 616 | categories.active.push(entry) | |
| 617 | } else if (ageDays > 180) { | |
| 618 | categories.stale.push(entry) | |
| 619 | } else if (store.path.includes('backup') || store.path.includes('broken') || store.path.includes('test') || store.path.includes('Videos')) { | |
| 620 | categories.junk.push(entry) | |
| 621 | } else { | |
| 622 | categories.unknown.push(entry) | |
| 623 | } | |
| 624 | } | |
| 625 | ||
| 626 | const printEntry = (e) => { | |
| 627 | const lockStr = e.locked ? ` [LOCKED by ${e.holder?.command} PID ${e.holder?.pid}]` : '' | |
| 628 | const ageStr = e.ageDays < 365 ? `${e.ageDays}d ago` : `${Math.floor(e.ageDays / 365)}y ago` | |
| 629 | console.log(` ${e.sizeFormatted.padStart(10)} ${ageStr.padStart(8)} ${e.path}${lockStr}`) | |
| 630 | } | |
| 631 | ||
| 632 | if (categories.active.length > 0) { | |
| 633 | console.log('ACTIVE (locked by running processes):') | |
| 634 | categories.active.forEach(printEntry) | |
| 635 | console.log() | |
| 636 | } | |
| 637 | ||
| 638 | if (categories.unknown.length > 0) { | |
| 639 | console.log('NEEDS REVIEW (unlocked, relatively recent):') | |
| 640 | categories.unknown.forEach(printEntry) | |
| 641 | console.log() | |
| 642 | } | |
| 643 | ||
| 644 | if (categories.stale.length > 0) { | |
| 645 | console.log('STALE (>6 months old, unlocked):') | |
| 646 | categories.stale.forEach(printEntry) | |
| 647 | console.log() | |
| 648 | } | |
| 649 | ||
| 650 | if (categories.junk.length > 0) { | |
| 651 | console.log('LIKELY JUNK (backups, test data, broken):') | |
| 652 | categories.junk.forEach(printEntry) | |
| 653 | console.log() | |
| 654 | } | |
| 655 | ||
| 656 | console.log(`Total: ${formatBytes(totalSize)} across ${stores.length} locations`) | |
| 657 | const staleSize = categories.stale.reduce((s, e) => s + e.size, 0) | |
| 658 | const junkSize = categories.junk.reduce((s, e) => s + e.size, 0) | |
| 659 | console.log(`Potentially reclaimable: ${formatBytes(staleSize + junkSize)} (stale + junk)`) | |
| 660 | } | |
| 661 | ||
| 662 | const KEY_TYPES_LABEL = { | |
| 663 | 0x00: 'seed', | |
| 664 | 0x01: 'core-key', | |
| 665 | 0x02: 'dk', | |
| 666 | 0x03: 'info', | |
| 667 | 0x04: 'data' | |
| 668 | } |
| … | … | @@ -0,0 +1,2 @@ |
| 1 | export { openDB, listApps, getCorestoreStats, scanColumnFamily, formatBytes } from './lib/rocksdb-inspector.js' | |
| 2 | export { findCorestores, getDirSize, getLastModified, isLocked, getLockHolder, hasRocksDB } from './lib/scanner.js' |
| … | … | @@ -0,0 +1,295 @@ |
| 1 | // Deep block reader - parse Hyperbee nodes from raw Hypercore blocks | |
| 2 | // and decode Hyperdrive file entries with full blob mapping | |
| 3 | import b4a from 'b4a' | |
| 4 | ||
| 5 | // Protobuf varint decoder | |
| 6 | function pbVarint (buf, offset) { | |
| 7 | let result = 0 | |
| 8 | let shift = 0 | |
| 9 | let pos = offset | |
| 10 | while (pos < buf.length) { | |
| 11 | const byte = buf[pos] | |
| 12 | result |= (byte & 0x7f) << shift | |
| 13 | pos++ | |
| 14 | if ((byte & 0x80) === 0) break | |
| 15 | shift += 7 | |
| 16 | } | |
| 17 | return { value: result, end: pos } | |
| 18 | } | |
| 19 | ||
| 20 | // Protobuf bytes decoder (length-delimited) | |
| 21 | function pbBytes (buf, offset) { | |
| 22 | const { value: len, end: lenEnd } = pbVarint(buf, offset) | |
| 23 | return { value: buf.subarray(lenEnd, lenEnd + len), end: lenEnd + len } | |
| 24 | } | |
| 25 | ||
| 26 | // Decode a Hyperbee Node protobuf { index, key, value } | |
| 27 | export function decodeNode (buf) { | |
| 28 | let offset = 0 | |
| 29 | const node = { index: null, key: null, value: null } | |
| 30 | ||
| 31 | while (offset < buf.length) { | |
| 32 | const { value: prefix, end: tagEnd } = pbVarint(buf, offset) | |
| 33 | offset = tagEnd | |
| 34 | const tag = prefix >> 3 | |
| 35 | const wireType = prefix & 7 | |
| 36 | ||
| 37 | if (tag === 1 && wireType === 2) { | |
| 38 | const { value, end } = pbBytes(buf, offset) | |
| 39 | node.index = decodeYoloIndex(value) | |
| 40 | offset = end | |
| 41 | } else if (tag === 2 && wireType === 2) { | |
| 42 | const { value, end } = pbBytes(buf, offset) | |
| 43 | node.key = value | |
| 44 | offset = end | |
| 45 | } else if (tag === 3 && wireType === 2) { | |
| 46 | const { value, end } = pbBytes(buf, offset) | |
| 47 | node.value = value | |
| 48 | offset = end | |
| 49 | } else { | |
| 50 | // Skip unknown fields | |
| 51 | if (wireType === 0) { pbVarint(buf, offset); offset = pbVarint(buf, offset).end } | |
| 52 | else if (wireType === 2) { const { end } = pbBytes(buf, offset); offset = end } | |
| 53 | else break | |
| 54 | } | |
| 55 | } | |
| 56 | ||
| 57 | return node | |
| 58 | } | |
| 59 | ||
| 60 | // Decode YoloIndex { levels: [{ keys: [seq], children: [seq] }] } | |
| 61 | function decodeYoloIndex (buf) { | |
| 62 | const levels = [] | |
| 63 | let offset = 0 | |
| 64 | ||
| 65 | while (offset < buf.length) { | |
| 66 | const { value: prefix, end: tagEnd } = pbVarint(buf, offset) | |
| 67 | offset = tagEnd | |
| 68 | const tag = prefix >> 3 | |
| 69 | ||
| 70 | if (tag === 1) { | |
| 71 | const { value: levelBuf, end } = pbBytes(buf, offset) | |
| 72 | offset = end | |
| 73 | levels.push(decodeLevel(levelBuf)) | |
| 74 | } else { | |
| 75 | break | |
| 76 | } | |
| 77 | } | |
| 78 | ||
| 79 | return { levels } | |
| 80 | } | |
| 81 | ||
| 82 | function decodeLevel (buf) { | |
| 83 | const keys = [] | |
| 84 | const children = [] | |
| 85 | let offset = 0 | |
| 86 | ||
| 87 | while (offset < buf.length) { | |
| 88 | const { value: prefix, end: tagEnd } = pbVarint(buf, offset) | |
| 89 | offset = tagEnd | |
| 90 | const tag = prefix >> 3 | |
| 91 | ||
| 92 | if (tag === 1) { | |
| 93 | // packed varints for keys | |
| 94 | const { value: packedLen, end: lenEnd } = pbVarint(buf, offset) | |
| 95 | offset = lenEnd | |
| 96 | const packedEnd = offset + packedLen | |
| 97 | while (offset < packedEnd) { | |
| 98 | const { value, end } = pbVarint(buf, offset) | |
| 99 | keys.push(value) | |
| 100 | offset = end | |
| 101 | } | |
| 102 | } else if (tag === 2) { | |
| 103 | // packed varints for children | |
| 104 | const { value: packedLen, end: lenEnd } = pbVarint(buf, offset) | |
| 105 | offset = lenEnd | |
| 106 | const packedEnd = offset + packedLen | |
| 107 | while (offset < packedEnd) { | |
| 108 | const { value, end } = pbVarint(buf, offset) | |
| 109 | children.push(value) | |
| 110 | offset = end | |
| 111 | } | |
| 112 | } else { | |
| 113 | break | |
| 114 | } | |
| 115 | } | |
| 116 | ||
| 117 | return { keys, children } | |
| 118 | } | |
| 119 | ||
| 120 | // Decode Hyperbee Header protobuf { protocol: string, metadata?: { contentFeed? } } | |
| 121 | export function decodeHeader (buf) { | |
| 122 | let offset = 0 | |
| 123 | const header = { protocol: '', contentFeed: null } | |
| 124 | ||
| 125 | while (offset < buf.length) { | |
| 126 | const { value: prefix, end: tagEnd } = pbVarint(buf, offset) | |
| 127 | offset = tagEnd | |
| 128 | const tag = prefix >> 3 | |
| 129 | ||
| 130 | if (tag === 1) { | |
| 131 | // protocol string | |
| 132 | const { value, end } = pbBytes(buf, offset) | |
| 133 | header.protocol = b4a.toString(value, 'utf8') | |
| 134 | offset = end | |
| 135 | } else if (tag === 2) { | |
| 136 | // metadata submessage | |
| 137 | const { value: metaBuf, end } = pbBytes(buf, offset) | |
| 138 | offset = end | |
| 139 | // Parse metadata: field 1 = contentFeed (bytes) | |
| 140 | let mOff = 0 | |
| 141 | while (mOff < metaBuf.length) { | |
| 142 | const { value: mPrefix, end: mTagEnd } = pbVarint(metaBuf, mOff) | |
| 143 | mOff = mTagEnd | |
| 144 | const mTag = mPrefix >> 3 | |
| 145 | if (mTag === 1) { | |
| 146 | const { value, end } = pbBytes(metaBuf, mOff) | |
| 147 | header.contentFeed = value | |
| 148 | mOff = end | |
| 149 | } else { | |
| 150 | break | |
| 151 | } | |
| 152 | } | |
| 153 | } else { | |
| 154 | break | |
| 155 | } | |
| 156 | } | |
| 157 | ||
| 158 | return header | |
| 159 | } | |
| 160 | ||
| 161 | // Decode a Hyperdrive file entry value | |
| 162 | // Supports both JSON (compat/legacy) and compact-encoding formats | |
| 163 | export function decodeDriveEntry (buf) { | |
| 164 | if (!buf || buf.length === 0) return null | |
| 165 | ||
| 166 | // JSON format starts with { (0x7b) | |
| 167 | if (buf[0] === 0x7b) { | |
| 168 | try { | |
| 169 | const obj = JSON.parse(b4a.toString(buf, 'utf8')) | |
| 170 | return { | |
| 171 | executable: obj.executable || false, | |
| 172 | linkname: obj.linkname || null, | |
| 173 | blob: obj.blob || null, | |
| 174 | metadata: obj.metadata || null | |
| 175 | } | |
| 176 | } catch { | |
| 177 | return null | |
| 178 | } | |
| 179 | } | |
| 180 | ||
| 181 | // Compact-encoding format | |
| 182 | let pos = 0 | |
| 183 | const flags = buf[pos++] | |
| 184 | ||
| 185 | const entry = { | |
| 186 | executable: !!(flags & 1), | |
| 187 | linkname: null, | |
| 188 | blob: null, | |
| 189 | metadata: null | |
| 190 | } | |
| 191 | ||
| 192 | if (flags & 2) { | |
| 193 | const { value: len, end: lenEnd } = readCEVarint(buf, pos) | |
| 194 | pos = lenEnd | |
| 195 | entry.linkname = b4a.toString(buf.subarray(pos, pos + len), 'utf8') | |
| 196 | pos += len | |
| 197 | } | |
| 198 | ||
| 199 | if (flags & 4) { | |
| 200 | const { value: blockOffset, end: e1 } = readCEVarint(buf, pos) | |
| 201 | pos = e1 | |
| 202 | const { value: blockLength, end: e2 } = readCEVarint(buf, pos) | |
| 203 | pos = e2 | |
| 204 | const { value: byteOffset, end: e3 } = readCEVarint(buf, pos) | |
| 205 | pos = e3 | |
| 206 | const { value: byteLength, end: e4 } = readCEVarint(buf, pos) | |
| 207 | pos = e4 | |
| 208 | entry.blob = { blockOffset, blockLength, byteOffset, byteLength } | |
| 209 | } | |
| 210 | ||
| 211 | if (flags & 8) { | |
| 212 | const { value: len, end: lenEnd } = readCEVarint(buf, pos) | |
| 213 | pos = lenEnd | |
| 214 | entry.metadata = buf.subarray(pos, pos + len) | |
| 215 | pos += len | |
| 216 | } | |
| 217 | ||
| 218 | return entry | |
| 219 | } | |
| 220 | ||
| 221 | // Compact-encoding varint (unsigned) | |
| 222 | function readCEVarint (buf, offset) { | |
| 223 | let result = 0 | |
| 224 | let shift = 0 | |
| 225 | let pos = offset | |
| 226 | while (pos < buf.length) { | |
| 227 | const byte = buf[pos] | |
| 228 | result |= (byte & 0x7f) << shift | |
| 229 | pos++ | |
| 230 | if ((byte & 0x80) === 0) break | |
| 231 | shift += 7 | |
| 232 | if (shift > 49) break // safety: max 7 bytes | |
| 233 | } | |
| 234 | return { value: result, end: pos } | |
| 235 | } | |
| 236 | ||
| 237 | // Strip SubEncoder prefix from a Hyperbee key | |
| 238 | // SubEncoder 'files' adds: [prefix_byte, 0x00] at start and [0x00] at end | |
| 239 | export function stripSubEncoder (key) { | |
| 240 | if (!key || key.length < 3) return key | |
| 241 | // SubEncoder format: prefix + SEP(0x00) + actual_key + SEP(0x00) | |
| 242 | // The prefix is the sub-encoder name encoded | |
| 243 | // For 'files': it's the string 'files' length-prefixed, then 0x00 separator | |
| 244 | // Let's find the first 0x00 after position 0 and strip up to there + the trailing 0x00 | |
| 245 | const firstSep = key.indexOf(0x00) | |
| 246 | if (firstSep < 0) return key | |
| 247 | ||
| 248 | const lastByte = key[key.length - 1] | |
| 249 | const end = lastByte === 0x00 ? key.length - 1 : key.length | |
| 250 | ||
| 251 | return key.subarray(firstSep + 1, end) | |
| 252 | } | |
| 253 | ||
| 254 | // Walk all Hyperbee blocks in a core and extract key-value entries | |
| 255 | export async function walkHyperbeeBlocks (core, opts = {}) { | |
| 256 | const { maxBlocks = Infinity, waitForBlocks = false } = opts | |
| 257 | const entries = [] | |
| 258 | let headerBlock = null | |
| 259 | ||
| 260 | const limit = Math.min(core.length, maxBlocks) | |
| 261 | ||
| 262 | for (let i = 0; i < limit; i++) { | |
| 263 | try { | |
| 264 | const block = await core.get(i, { wait: waitForBlocks }) | |
| 265 | if (!block) continue | |
| 266 | ||
| 267 | if (i === 0) { | |
| 268 | // Block 0 is the Hyperbee header | |
| 269 | try { | |
| 270 | headerBlock = decodeHeader(block) | |
| 271 | } catch {} | |
| 272 | continue | |
| 273 | } | |
| 274 | ||
| 275 | // All other blocks are Node protobuf messages | |
| 276 | try { | |
| 277 | const node = decodeNode(block) | |
| 278 | if (node.key && node.key.length > 0) { | |
| 279 | entries.push({ | |
| 280 | seq: i, | |
| 281 | key: node.key, | |
| 282 | value: node.value, | |
| 283 | childCount: node.index?.levels?.[0]?.children?.length || 0, | |
| 284 | keyCount: node.index?.levels?.[0]?.keys?.length || 0 | |
| 285 | }) | |
| 286 | } | |
| 287 | } catch {} | |
| 288 | } catch { | |
| 289 | // Block not available locally | |
| 290 | continue | |
| 291 | } | |
| 292 | } | |
| 293 | ||
| 294 | return { header: headerBlock, entries } | |
| 295 | } |
| … | … | @@ -0,0 +1,251 @@ |
| 1 | import RocksDB from 'rocksdb-native' | |
| 2 | import b4a from 'b4a' | |
| 3 | ||
| 4 | // Mirrors hypercore-storage key encoding | |
| 5 | // TL_CORE_BY_DKEY = 1, keys are: varint(1) + fixed32(discoveryKey) | |
| 6 | // Values are @corestore/core: { version, corePointer, dataPointer, alias? } | |
| 7 | ||
| 8 | const CORESTORE_CF = 'corestore' | |
| 9 | ||
| 10 | export async function enumerateCores (dbPath, opts = {}) { | |
| 11 | const { readOnly = false } = opts | |
| 12 | ||
| 13 | const db = new RocksDB(dbPath, { | |
| 14 | readOnly, | |
| 15 | columnFamilies: [CORESTORE_CF] | |
| 16 | }) | |
| 17 | await db.ready() | |
| 18 | ||
| 19 | const cs = db.columnFamily(CORESTORE_CF) | |
| 20 | const cores = [] | |
| 21 | ||
| 22 | // Key prefix 0x01 = TL_CORE_BY_DKEY (varint-encoded 1) | |
| 23 | // Each entry maps a discovery key to a @corestore/core record | |
| 24 | const start = Buffer.from([0x01]) | |
| 25 | const end = Buffer.from([0x02]) | |
| 26 | ||
| 27 | const iter = cs.iterator({ gte: start, lt: end, limit: 10000 }) | |
| 28 | ||
| 29 | await new Promise((resolve, reject) => { | |
| 30 | iter.on('data', ({ key, value }) => { | |
| 31 | if (!value || key[0] !== 0x01) return | |
| 32 | ||
| 33 | // Discovery key is the 32 bytes after the varint prefix | |
| 34 | const discoveryKey = b4a.toString(key.subarray(1, 33), 'hex') | |
| 35 | ||
| 36 | // Decode @corestore/core value using compact-encoding | |
| 37 | try { | |
| 38 | const core = decodeCoreRecord(value) | |
| 39 | cores.push({ | |
| 40 | discoveryKey, | |
| 41 | ...core | |
| 42 | }) | |
| 43 | } catch (err) { | |
| 44 | cores.push({ | |
| 45 | discoveryKey, | |
| 46 | corePointer: -1, | |
| 47 | dataPointer: -1, | |
| 48 | alias: null, | |
| 49 | error: err.message | |
| 50 | }) | |
| 51 | } | |
| 52 | }) | |
| 53 | iter.on('end', resolve) | |
| 54 | iter.on('error', reject) | |
| 55 | }) | |
| 56 | ||
| 57 | // Read auth records to get public keys and head lengths | |
| 58 | // Auth key format: varint(3) + varint(corePointer) + varint(0) | |
| 59 | // Auth value: fixed32(key) + fixed32(discoveryKey) + varint(flags) + ... | |
| 60 | // Head key format: varint(4) + varint(dataPointer) + varint(0) | |
| 61 | // Head value: { fork, length, rootHash, signature } | |
| 62 | for (const core of cores) { | |
| 63 | // Build auth key | |
| 64 | const authKey = encodeKey(3, core.corePointer, 0) | |
| 65 | try { | |
| 66 | const authIter = cs.iterator({ gte: authKey, lt: incrementKey(authKey), limit: 1 }) | |
| 67 | await new Promise((resolve, reject) => { | |
| 68 | authIter.on('data', ({ value }) => { | |
| 69 | if (value && value.length >= 64) { | |
| 70 | core.publicKey = b4a.toString(value.subarray(0, 32), 'hex') | |
| 71 | } | |
| 72 | }) | |
| 73 | authIter.on('end', resolve) | |
| 74 | authIter.on('error', reject) | |
| 75 | }) | |
| 76 | } catch {} | |
| 77 | ||
| 78 | // Build head key to get core length | |
| 79 | const headKey = encodeKey(4, core.dataPointer, 0) | |
| 80 | try { | |
| 81 | const headIter = cs.iterator({ gte: headKey, lt: incrementKey(headKey), limit: 1 }) | |
| 82 | await new Promise((resolve, reject) => { | |
| 83 | headIter.on('data', ({ value }) => { | |
| 84 | if (value) { | |
| 85 | const head = decodeHeadRecord(value) | |
| 86 | core.length = head.length | |
| 87 | core.fork = head.fork | |
| 88 | } | |
| 89 | }) | |
| 90 | headIter.on('end', resolve) | |
| 91 | headIter.on('error', reject) | |
| 92 | }) | |
| 93 | } catch {} | |
| 94 | } | |
| 95 | ||
| 96 | // Also try to read the corestore head for allocation counts | |
| 97 | let head = null | |
| 98 | try { | |
| 99 | const headKey = Buffer.from([0x00]) | |
| 100 | const headIter = cs.iterator({ gte: headKey, lt: Buffer.from([0x01]), limit: 1 }) | |
| 101 | await new Promise((resolve, reject) => { | |
| 102 | headIter.on('data', ({ value }) => { | |
| 103 | if (value) head = decodeHead(value) | |
| 104 | }) | |
| 105 | headIter.on('end', resolve) | |
| 106 | headIter.on('error', reject) | |
| 107 | }) | |
| 108 | } catch {} | |
| 109 | ||
| 110 | if (head) { | |
| 111 | cores._head = head | |
| 112 | } | |
| 113 | ||
| 114 | await db.close() | |
| 115 | ||
| 116 | // Sort by corePointer | |
| 117 | cores.sort((a, b) => a.corePointer - b.corePointer) | |
| 118 | ||
| 119 | return cores | |
| 120 | } | |
| 121 | ||
| 122 | // Encode a RocksDB key from varint parts | |
| 123 | function encodeKey (...parts) { | |
| 124 | const bufs = parts.map(encodeVarint) | |
| 125 | return Buffer.concat(bufs) | |
| 126 | } | |
| 127 | ||
| 128 | function encodeVarint (n) { | |
| 129 | const bytes = [] | |
| 130 | while (n >= 0x80) { | |
| 131 | bytes.push((n & 0x7f) | 0x80) | |
| 132 | n >>>= 7 | |
| 133 | } | |
| 134 | bytes.push(n & 0x7f) | |
| 135 | return Buffer.from(bytes) | |
| 136 | } | |
| 137 | ||
| 138 | function incrementKey (key) { | |
| 139 | const buf = Buffer.from(key) | |
| 140 | for (let i = buf.length - 1; i >= 0; i--) { | |
| 141 | if (buf[i] < 0xff) { | |
| 142 | buf[i]++ | |
| 143 | return buf | |
| 144 | } | |
| 145 | buf[i] = 0 | |
| 146 | } | |
| 147 | return Buffer.concat([Buffer.from([0x01]), buf]) | |
| 148 | } | |
| 149 | ||
| 150 | // Decode @core/head { fork, length, rootHash, signature } | |
| 151 | function decodeHeadRecord (buf) { | |
| 152 | let pos = 0 | |
| 153 | const { value: fork, bytesRead: fb } = readVarint(buf, pos) | |
| 154 | pos += fb | |
| 155 | const { value: length, bytesRead: lb } = readVarint(buf, pos) | |
| 156 | pos += lb | |
| 157 | return { fork, length } | |
| 158 | } | |
| 159 | ||
| 160 | // Minimal varint decoder | |
| 161 | function readVarint (buf, offset) { | |
| 162 | let result = 0 | |
| 163 | let shift = 0 | |
| 164 | let pos = offset | |
| 165 | while (pos < buf.length) { | |
| 166 | const byte = buf[pos] | |
| 167 | result |= (byte & 0x7f) << shift | |
| 168 | pos++ | |
| 169 | if ((byte & 0x80) === 0) break | |
| 170 | shift += 7 | |
| 171 | } | |
| 172 | return { value: result, bytesRead: pos - offset } | |
| 173 | } | |
| 174 | ||
| 175 | function readFixed32 (buf, offset) { | |
| 176 | return { | |
| 177 | value: b4a.toString(buf.subarray(offset, offset + 32), 'hex'), | |
| 178 | bytesRead: 32 | |
| 179 | } | |
| 180 | } | |
| 181 | ||
| 182 | function readString (buf, offset) { | |
| 183 | const { value: len, bytesRead: lenBytes } = readVarint(buf, offset) | |
| 184 | const str = b4a.toString(buf.subarray(offset + lenBytes, offset + lenBytes + len), 'utf8') | |
| 185 | return { value: str, bytesRead: lenBytes + len } | |
| 186 | } | |
| 187 | ||
| 188 | // Decode @corestore/core { version, corePointer, dataPointer, alias? } | |
| 189 | function decodeCoreRecord (buf) { | |
| 190 | let pos = 0 | |
| 191 | ||
| 192 | const { value: version, bytesRead: vb } = readVarint(buf, pos) | |
| 193 | pos += vb | |
| 194 | ||
| 195 | const { value: corePointer, bytesRead: cpb } = readVarint(buf, pos) | |
| 196 | pos += cpb | |
| 197 | ||
| 198 | const { value: dataPointer, bytesRead: dpb } = readVarint(buf, pos) | |
| 199 | pos += dpb | |
| 200 | ||
| 201 | const { value: flags, bytesRead: fb } = readVarint(buf, pos) | |
| 202 | pos += fb | |
| 203 | ||
| 204 | let alias = null | |
| 205 | if (flags & 1) { | |
| 206 | // @corestore/alias { name, namespace } | |
| 207 | const { value: name, bytesRead: nb } = readString(buf, pos) | |
| 208 | pos += nb | |
| 209 | const { value: namespace, bytesRead: nsb } = readFixed32(buf, pos) | |
| 210 | pos += nsb | |
| 211 | alias = { name, namespace } | |
| 212 | } | |
| 213 | ||
| 214 | return { version, corePointer, dataPointer, alias } | |
| 215 | } | |
| 216 | ||
| 217 | // Decode @corestore/head { version, flags, allocated?, seed?, defaultDiscoveryKey? } | |
| 218 | function decodeHead (buf) { | |
| 219 | let pos = 0 | |
| 220 | ||
| 221 | const { value: version, bytesRead: vb } = readVarint(buf, pos) | |
| 222 | pos += vb | |
| 223 | ||
| 224 | const { value: flags, bytesRead: fb } = readVarint(buf, pos) | |
| 225 | pos += fb | |
| 226 | ||
| 227 | let allocated = null | |
| 228 | if (flags & 1) { | |
| 229 | const { value: cores, bytesRead: cb } = readVarint(buf, pos) | |
| 230 | pos += cb | |
| 231 | const { value: datas, bytesRead: db } = readVarint(buf, pos) | |
| 232 | pos += db | |
| 233 | allocated = { cores, datas } | |
| 234 | } | |
| 235 | ||
| 236 | let seed = null | |
| 237 | if (flags & 2) { | |
| 238 | const { value, bytesRead } = readFixed32(buf, pos) | |
| 239 | pos += bytesRead | |
| 240 | seed = value | |
| 241 | } | |
| 242 | ||
| 243 | let defaultDiscoveryKey = null | |
| 244 | if (flags & 4) { | |
| 245 | const { value, bytesRead } = readFixed32(buf, pos) | |
| 246 | pos += bytesRead | |
| 247 | defaultDiscoveryKey = value | |
| 248 | } | |
| 249 | ||
| 250 | return { version, allocated, seed, defaultDiscoveryKey } | |
| 251 | } |
| … | … | @@ -0,0 +1,196 @@ |
| 1 | // Deep analysis of a corestore - understand WHY it's big | |
| 2 | // Maps cores → apps → files → blobs → disk bytes | |
| 3 | import RocksDB from 'rocksdb-native' | |
| 4 | import b4a from 'b4a' | |
| 5 | ||
| 6 | const CORESTORE_CF = 'corestore' | |
| 7 | ||
| 8 | // Analyze disk usage per core by scanning RocksDB data blocks | |
| 9 | // Key format: varint(4) + varint(dataPointer) + varint(DATA_BLOCK=3) + varint(blockIndex) | |
| 10 | // This is where the actual data bytes live | |
| 11 | export async function analyzeCoreDiskUsage (dbPath, opts = {}) { | |
| 12 | const { readOnly = false, sampleSize = 500000 } = opts | |
| 13 | ||
| 14 | const db = new RocksDB(dbPath, { | |
| 15 | readOnly, | |
| 16 | columnFamilies: [CORESTORE_CF] | |
| 17 | }) | |
| 18 | await db.ready() | |
| 19 | ||
| 20 | const cs = db.columnFamily(CORESTORE_CF) | |
| 21 | ||
| 22 | // Scan data blocks (key prefix 0x04 = varint-encoded TL_DATA=4) | |
| 23 | // Within TL_DATA, the next varint is the dataPointer, then the type: | |
| 24 | // 0 = head, 1 = dependency, 2 = hints, 3 = block, 4 = tree, 5 = bitfield, 6 = userData | |
| 25 | const dataPtrStats = new Map() | |
| 26 | let totalScanned = 0 | |
| 27 | let totalBytes = 0 | |
| 28 | ||
| 29 | const iter = cs.iterator({ gte: Buffer.from([0x04]), lt: Buffer.from([0x05]), limit: sampleSize }) | |
| 30 | ||
| 31 | await new Promise((resolve, reject) => { | |
| 32 | iter.on('data', ({ key, value }) => { | |
| 33 | totalScanned++ | |
| 34 | const valLen = value ? value.length : 0 | |
| 35 | totalBytes += valLen | |
| 36 | ||
| 37 | // Decode dataPointer from key: skip first byte (0x04), then read varint | |
| 38 | let pos = 1 | |
| 39 | let dataPtr = 0 | |
| 40 | let shift = 0 | |
| 41 | while (pos < key.length) { | |
| 42 | const byte = key[pos] | |
| 43 | dataPtr |= (byte & 0x7f) << shift | |
| 44 | pos++ | |
| 45 | if ((byte & 0x80) === 0) break | |
| 46 | shift += 7 | |
| 47 | } | |
| 48 | ||
| 49 | // Decode type | |
| 50 | let type = 0 | |
| 51 | shift = 0 | |
| 52 | while (pos < key.length) { | |
| 53 | const byte = key[pos] | |
| 54 | type |= (byte & 0x7f) << shift | |
| 55 | pos++ | |
| 56 | if ((byte & 0x80) === 0) break | |
| 57 | shift += 7 | |
| 58 | } | |
| 59 | ||
| 60 | const stats = dataPtrStats.get(dataPtr) || { | |
| 61 | dataPointer: dataPtr, | |
| 62 | blocks: 0, | |
| 63 | blockBytes: 0, | |
| 64 | trees: 0, | |
| 65 | treeBytes: 0, | |
| 66 | bitfields: 0, | |
| 67 | bitfieldBytes: 0, | |
| 68 | userData: 0, | |
| 69 | userDataBytes: 0, | |
| 70 | heads: 0, | |
| 71 | other: 0, | |
| 72 | otherBytes: 0 | |
| 73 | } | |
| 74 | ||
| 75 | switch (type) { | |
| 76 | case 3: // DATA_BLOCK | |
| 77 | stats.blocks++ | |
| 78 | stats.blockBytes += valLen | |
| 79 | break | |
| 80 | case 4: // DATA_TREE | |
| 81 | stats.trees++ | |
| 82 | stats.treeBytes += valLen | |
| 83 | break | |
| 84 | case 5: // DATA_BITFIELD | |
| 85 | stats.bitfields++ | |
| 86 | stats.bitfieldBytes += valLen | |
| 87 | break | |
| 88 | case 6: // DATA_USER_DATA | |
| 89 | stats.userData++ | |
| 90 | stats.userDataBytes += valLen | |
| 91 | break | |
| 92 | case 0: // DATA_HEAD | |
| 93 | stats.heads++ | |
| 94 | break | |
| 95 | default: | |
| 96 | stats.other++ | |
| 97 | stats.otherBytes += valLen | |
| 98 | } | |
| 99 | ||
| 100 | dataPtrStats.set(dataPtr, stats) | |
| 101 | }) | |
| 102 | iter.on('end', resolve) | |
| 103 | iter.on('error', reject) | |
| 104 | }) | |
| 105 | ||
| 106 | // Also get core info to map dataPointers to cores | |
| 107 | const coreMap = new Map() // dataPointer -> core info | |
| 108 | const coreIter = cs.iterator({ gte: Buffer.from([0x01]), lt: Buffer.from([0x02]), limit: 10000 }) | |
| 109 | ||
| 110 | await new Promise((resolve, reject) => { | |
| 111 | coreIter.on('data', ({ key, value }) => { | |
| 112 | if (!value || key[0] !== 0x01) return | |
| 113 | const discoveryKey = b4a.toString(key.subarray(1, 33), 'hex') | |
| 114 | ||
| 115 | // Decode @corestore/core { version, corePointer, dataPointer, alias? } | |
| 116 | try { | |
| 117 | let pos = 0 | |
| 118 | const readV = (buf, off) => { | |
| 119 | let r = 0, s = 0, p = off | |
| 120 | while (p < buf.length) { | |
| 121 | const b = buf[p]; r |= (b & 0x7f) << s; p++ | |
| 122 | if ((b & 0x80) === 0) break; s += 7 | |
| 123 | } | |
| 124 | return { value: r, end: p } | |
| 125 | } | |
| 126 | ||
| 127 | const { value: version, end: e1 } = readV(value, pos) | |
| 128 | const { value: corePointer, end: e2 } = readV(value, e1) | |
| 129 | const { value: dataPointer, end: e3 } = readV(value, e2) | |
| 130 | const { value: flags, end: e4 } = readV(value, e3) | |
| 131 | ||
| 132 | let alias = null | |
| 133 | if (flags & 1) { | |
| 134 | const { value: nameLen, end: ne } = readV(value, e4) | |
| 135 | const name = b4a.toString(value.subarray(ne, ne + nameLen), 'utf8') | |
| 136 | alias = name | |
| 137 | } | |
| 138 | ||
| 139 | // Read auth for public key | |
| 140 | let publicKey = null | |
| 141 | const authKey = Buffer.from([0x03]) | |
| 142 | // Will be done separately | |
| 143 | ||
| 144 | coreMap.set(dataPointer, { discoveryKey, corePointer, dataPointer, alias }) | |
| 145 | } catch {} | |
| 146 | }) | |
| 147 | coreIter.on('end', resolve) | |
| 148 | coreIter.on('error', reject) | |
| 149 | }) | |
| 150 | ||
| 151 | await db.close() | |
| 152 | ||
| 153 | // Combine: annotate dataPtrStats with core info | |
| 154 | const results = [] | |
| 155 | for (const [ptr, stats] of dataPtrStats) { | |
| 156 | const core = coreMap.get(ptr) | |
| 157 | results.push({ | |
| 158 | ...stats, | |
| 159 | corePointer: core?.corePointer ?? -1, | |
| 160 | discoveryKey: core?.discoveryKey?.slice(0, 16) ?? 'unknown', | |
| 161 | alias: core?.alias ?? null, | |
| 162 | totalBytes: stats.blockBytes + stats.treeBytes + stats.bitfieldBytes + stats.userDataBytes + stats.otherBytes | |
| 163 | }) | |
| 164 | } | |
| 165 | ||
| 166 | results.sort((a, b) => b.totalBytes - a.totalBytes) | |
| 167 | ||
| 168 | return { | |
| 169 | totalScanned, | |
| 170 | totalBytes, | |
| 171 | sampleComplete: totalScanned < sampleSize, | |
| 172 | coresAnalyzed: results.length, | |
| 173 | topCores: results | |
| 174 | } | |
| 175 | } | |
| 176 | ||
| 177 | export function formatAnalysis (analysis) { | |
| 178 | const lines = [] | |
| 179 | lines.push(`Scanned ${analysis.totalScanned} RocksDB entries (${(analysis.totalBytes / 1024 / 1024).toFixed(1)} MB in sample)`) | |
| 180 | lines.push(`${analysis.sampleComplete ? 'Complete scan' : 'Partial scan (hit limit)'}`) | |
| 181 | lines.push(`${analysis.coresAnalyzed} data pointers found\n`) | |
| 182 | ||
| 183 | lines.push('--- Disk usage by core (top 20) ---\n') | |
| 184 | ||
| 185 | for (const c of analysis.topCores.slice(0, 20)) { | |
| 186 | const totalMB = (c.totalBytes / 1024 / 1024).toFixed(2) | |
| 187 | const blockMB = (c.blockBytes / 1024 / 1024).toFixed(2) | |
| 188 | const treeMB = (c.treeBytes / 1024 / 1024).toFixed(2) | |
| 189 | const alias = c.alias ? c.alias : c.corePointer >= 0 ? `core #${c.corePointer}` : `data #${c.dataPointer}` | |
| 190 | ||
| 191 | lines.push(` ${totalMB.padStart(10)} MB ${alias} (dkey: ${c.discoveryKey}...)`) | |
| 192 | lines.push(` blocks: ${c.blocks} (${blockMB} MB) tree: ${c.trees} (${treeMB} MB) bitfield: ${c.bitfields} userData: ${c.userData}`) | |
| 193 | } | |
| 194 | ||
| 195 | return lines.join('\n') | |
| 196 | } |
| … | … | @@ -0,0 +1,182 @@ |
| 1 | import Corestore from 'corestore' | |
| 2 | import Hyperdrive from 'hyperdrive' | |
| 3 | import b4a from 'b4a' | |
| 4 | import { enumerateCores } from './core-enumerator.js' | |
| 5 | import { join } from 'path' | |
| 6 | ||
| 7 | export async function openDrive (storagePath, opts = {}) { | |
| 8 | const { key = null } = opts | |
| 9 | const store = new Corestore(storagePath) | |
| 10 | await store.ready() | |
| 11 | ||
| 12 | const drive = key | |
| 13 | ? new Hyperdrive(store, b4a.isBuffer(key) ? key : b4a.from(key, 'hex')) | |
| 14 | : new Hyperdrive(store) | |
| 15 | ||
| 16 | await drive.ready() | |
| 17 | return { store, drive } | |
| 18 | } | |
| 19 | ||
| 20 | // Discover all Hyperdrives in a corestore by finding cores with Hyperbee headers | |
| 21 | export async function discoverDrives (storagePath, opts = {}) { | |
| 22 | const dbPath = join(storagePath, 'db') | |
| 23 | const cores = await enumerateCores(dbPath, opts) | |
| 24 | ||
| 25 | const store = new Corestore(storagePath) | |
| 26 | await store.ready() | |
| 27 | ||
| 28 | const drives = [] | |
| 29 | ||
| 30 | for (const c of cores) { | |
| 31 | if (!c.publicKey) continue | |
| 32 | ||
| 33 | const keyBuf = b4a.from(c.publicKey, 'hex') | |
| 34 | const core = store.get({ key: keyBuf }) | |
| 35 | await core.ready() | |
| 36 | ||
| 37 | if (core.length > 0) { | |
| 38 | try { | |
| 39 | const block0 = await core.get(0, { wait: false }) | |
| 40 | if (block0) { | |
| 41 | // Check for Hyperbee header signature: starts with protobuf field for "hyperbee" | |
| 42 | const hexStart = b4a.toString(block0.subarray(0, 12), 'hex') | |
| 43 | if (hexStart.startsWith('0a0868797065726265')) { | |
| 44 | // Extract content key from header | |
| 45 | let contentKey = null | |
| 46 | if (block0.length > 12) { | |
| 47 | try { | |
| 48 | // The content key is at offset 12 (tag 0x12, length 0x22=34, then 0x0a 0x20 + 32 bytes key) | |
| 49 | const contentKeyStart = block0.indexOf(Buffer.from([0x0a, 0x20]), 10) | |
| 50 | if (contentKeyStart >= 0 && contentKeyStart + 34 <= block0.length) { | |
| 51 | contentKey = b4a.toString(block0.subarray(contentKeyStart + 2, contentKeyStart + 34), 'hex') | |
| 52 | } | |
| 53 | } catch {} | |
| 54 | } | |
| 55 | ||
| 56 | // Scan some blocks for embedded file paths | |
| 57 | const filePaths = [] | |
| 58 | const scanLimit = Math.min(core.length, 200) | |
| 59 | for (let i = 0; i < scanLimit; i++) { | |
| 60 | try { | |
| 61 | const block = await core.get(i, { wait: false }) | |
| 62 | if (!block) continue | |
| 63 | const str = b4a.toString(block, 'utf8') | |
| 64 | const paths = str.match(/\/[a-zA-Z0-9._\-/]+\.[a-zA-Z0-9]+/g) | |
| 65 | if (paths) { | |
| 66 | for (const p of paths) { | |
| 67 | if (!filePaths.includes(p) && filePaths.length < 100) filePaths.push(p) | |
| 68 | } | |
| 69 | } | |
| 70 | } catch { break } | |
| 71 | } | |
| 72 | ||
| 73 | drives.push({ | |
| 74 | key: c.publicKey, | |
| 75 | discoveryKey: c.discoveryKey, | |
| 76 | corePointer: c.corePointer, | |
| 77 | alias: c.alias, | |
| 78 | version: core.length, | |
| 79 | byteLength: core.byteLength, | |
| 80 | writable: core.writable, | |
| 81 | contentKey, | |
| 82 | filePaths | |
| 83 | }) | |
| 84 | } | |
| 85 | } | |
| 86 | } catch {} | |
| 87 | } | |
| 88 | ||
| 89 | await core.close() | |
| 90 | } | |
| 91 | ||
| 92 | await store.close() | |
| 93 | return drives | |
| 94 | } | |
| 95 | ||
| 96 | export async function listDriveFiles (drive, folder = '/', opts = {}) { | |
| 97 | const { recursive = true } = opts | |
| 98 | const files = [] | |
| 99 | ||
| 100 | const stream = drive.list(folder, { recursive }) | |
| 101 | ||
| 102 | return new Promise((resolve, reject) => { | |
| 103 | stream.on('data', (entry) => { | |
| 104 | const blob = entry.value?.blob | |
| 105 | files.push({ | |
| 106 | path: entry.key, | |
| 107 | executable: entry.value?.executable || false, | |
| 108 | linkname: entry.value?.linkname || null, | |
| 109 | metadata: entry.value?.metadata || null, | |
| 110 | blobOffset: blob?.blockOffset, | |
| 111 | blobLength: blob?.blockLength, | |
| 112 | byteOffset: blob?.byteOffset, | |
| 113 | byteLength: blob?.byteLength | |
| 114 | }) | |
| 115 | }) | |
| 116 | stream.on('end', () => resolve(files)) | |
| 117 | stream.on('error', reject) | |
| 118 | }) | |
| 119 | } | |
| 120 | ||
| 121 | export async function getDriveStats (drive) { | |
| 122 | const files = await listDriveFiles(drive) | |
| 123 | ||
| 124 | let totalBytes = 0 | |
| 125 | let blobBlocks = 0 | |
| 126 | const extensions = new Map() | |
| 127 | const topDirs = new Map() | |
| 128 | ||
| 129 | for (const f of files) { | |
| 130 | const bytes = f.byteLength || 0 | |
| 131 | totalBytes += bytes | |
| 132 | blobBlocks += f.blobLength || 0 | |
| 133 | ||
| 134 | // Count by extension | |
| 135 | const ext = f.path.includes('.') ? f.path.split('.').pop() : '(none)' | |
| 136 | const extInfo = extensions.get(ext) || { count: 0, bytes: 0 } | |
| 137 | extInfo.count++ | |
| 138 | extInfo.bytes += bytes | |
| 139 | extensions.set(ext, extInfo) | |
| 140 | ||
| 141 | // Count by top-level directory | |
| 142 | const parts = f.path.split('/') | |
| 143 | const topDir = parts.length > 2 ? '/' + parts[1] : '/' | |
| 144 | const dirInfo = topDirs.get(topDir) || { count: 0, bytes: 0 } | |
| 145 | dirInfo.count++ | |
| 146 | dirInfo.bytes += bytes | |
| 147 | topDirs.set(topDir, dirInfo) | |
| 148 | } | |
| 149 | ||
| 150 | return { | |
| 151 | fileCount: files.length, | |
| 152 | totalBytes, | |
| 153 | blobBlocks, | |
| 154 | driveKey: b4a.toString(drive.key, 'hex'), | |
| 155 | driveVersion: drive.version, | |
| 156 | writable: drive.writable, | |
| 157 | extensions: [...extensions.entries()] | |
| 158 | .map(([ext, info]) => ({ ext, ...info })) | |
| 159 | .sort((a, b) => b.bytes - a.bytes), | |
| 160 | topDirs: [...topDirs.entries()] | |
| 161 | .map(([dir, info]) => ({ dir, ...info })) | |
| 162 | .sort((a, b) => b.bytes - a.bytes), | |
| 163 | files | |
| 164 | } | |
| 165 | } | |
| 166 | ||
| 167 | export async function readDriveFile (drive, path) { | |
| 168 | return drive.get(path) | |
| 169 | } | |
| 170 | ||
| 171 | export async function driveSummary (drive) { | |
| 172 | const stats = await getDriveStats(drive) | |
| 173 | return { | |
| 174 | key: stats.driveKey, | |
| 175 | version: stats.driveVersion, | |
| 176 | writable: stats.writable, | |
| 177 | files: stats.fileCount, | |
| 178 | totalBytes: stats.totalBytes, | |
| 179 | topExtensions: stats.extensions.slice(0, 10), | |
| 180 | topDirs: stats.topDirs.slice(0, 10) | |
| 181 | } | |
| 182 | } |
| … | … | @@ -0,0 +1,203 @@ |
| 1 | // List all files in a Hyperdrive stored in RocksDB | |
| 2 | // Works read-only on locked databases — reads raw Hyperbee blocks | |
| 3 | import RocksDB from 'rocksdb-native' | |
| 4 | import b4a from 'b4a' | |
| 5 | import { decodeNode, decodeDriveEntry, stripSubEncoder, decodeHeader } from './block-reader.js' | |
| 6 | ||
| 7 | function encodeVarint (n) { | |
| 8 | const bytes = [] | |
| 9 | while (n >= 0x80) { bytes.push((n & 0x7f) | 0x80); n >>>= 7 } | |
| 10 | bytes.push(n & 0x7f) | |
| 11 | return Buffer.from(bytes) | |
| 12 | } | |
| 13 | ||
| 14 | // Extract all Hyperdrive file entries from a specific dataPointer in a corestore RocksDB | |
| 15 | export async function listDriveFromDB (dbPath, dataPointer, opts = {}) { | |
| 16 | const { readOnly = false, maxBlocks = Infinity } = opts | |
| 17 | ||
| 18 | const db = new RocksDB(dbPath, { readOnly, columnFamilies: ['corestore'] }) | |
| 19 | await db.ready() | |
| 20 | ||
| 21 | const cs = db.columnFamily('corestore') | |
| 22 | ||
| 23 | // Key range for data blocks: varint(4) + varint(dataPointer) + varint(3=DATA_BLOCK) | |
| 24 | const prefix = Buffer.concat([encodeVarint(4), encodeVarint(dataPointer), encodeVarint(3)]) | |
| 25 | const endPrefix = Buffer.concat([encodeVarint(4), encodeVarint(dataPointer), encodeVarint(4)]) | |
| 26 | ||
| 27 | const files = [] | |
| 28 | let header = null | |
| 29 | let blocksRead = 0 | |
| 30 | ||
| 31 | const iter = cs.iterator({ gte: prefix, lt: endPrefix, limit: maxBlocks }) | |
| 32 | ||
| 33 | await new Promise((resolve, reject) => { | |
| 34 | iter.on('data', ({ key, value }) => { | |
| 35 | blocksRead++ | |
| 36 | if (!value) return | |
| 37 | ||
| 38 | // Decode block index from key | |
| 39 | let pos = prefix.length | |
| 40 | let blockIdx = 0 | |
| 41 | let shift = 0 | |
| 42 | while (pos < key.length) { | |
| 43 | const byte = key[pos] | |
| 44 | blockIdx |= (byte & 0x7f) << shift | |
| 45 | pos++ | |
| 46 | if ((byte & 0x80) === 0) break | |
| 47 | shift += 7 | |
| 48 | } | |
| 49 | ||
| 50 | if (blockIdx === 0) { | |
| 51 | try { header = decodeHeader(value) } catch {} | |
| 52 | return | |
| 53 | } | |
| 54 | ||
| 55 | try { | |
| 56 | const node = decodeNode(value) | |
| 57 | if (!node.key || node.key.length === 0) return | |
| 58 | ||
| 59 | const rawKey = stripSubEncoder(node.key) | |
| 60 | let filePath = '' | |
| 61 | try { filePath = b4a.toString(rawKey, 'utf8') } catch { return } | |
| 62 | if (!filePath.startsWith('/')) return | |
| 63 | ||
| 64 | let entry = null | |
| 65 | try { entry = decodeDriveEntry(node.value) } catch {} | |
| 66 | if (!entry) return | |
| 67 | ||
| 68 | files.push({ | |
| 69 | path: filePath, | |
| 70 | blob: entry.blob, | |
| 71 | executable: entry.executable, | |
| 72 | linkname: entry.linkname, | |
| 73 | metadata: entry.metadata, | |
| 74 | bytes: entry.blob?.byteLength || 0, | |
| 75 | seq: blockIdx | |
| 76 | }) | |
| 77 | } catch {} | |
| 78 | }) | |
| 79 | iter.on('end', resolve) | |
| 80 | iter.on('error', reject) | |
| 81 | }) | |
| 82 | ||
| 83 | await db.close() | |
| 84 | ||
| 85 | return { header, files, blocksRead } | |
| 86 | } | |
| 87 | ||
| 88 | // Build a directory tree from flat file list | |
| 89 | export function buildTree (files) { | |
| 90 | const root = { name: '/', children: new Map(), files: [], totalBytes: 0, totalFiles: 0 } | |
| 91 | ||
| 92 | for (const f of files) { | |
| 93 | const parts = f.path.split('/').filter(Boolean) | |
| 94 | let node = root | |
| 95 | ||
| 96 | // Walk/create directory nodes | |
| 97 | for (let i = 0; i < parts.length - 1; i++) { | |
| 98 | if (!node.children.has(parts[i])) { | |
| 99 | node.children.set(parts[i], { | |
| 100 | name: parts[i], | |
| 101 | children: new Map(), | |
| 102 | files: [], | |
| 103 | totalBytes: 0, | |
| 104 | totalFiles: 0 | |
| 105 | }) | |
| 106 | } | |
| 107 | node = node.children.get(parts[i]) | |
| 108 | } | |
| 109 | ||
| 110 | // Add file to its parent directory | |
| 111 | const fileName = parts[parts.length - 1] | |
| 112 | node.files.push({ name: fileName, ...f }) | |
| 113 | node.totalFiles++ | |
| 114 | node.totalBytes += f.bytes | |
| 115 | ||
| 116 | // Propagate totals up | |
| 117 | let parent = root | |
| 118 | for (let i = 0; i < parts.length - 1; i++) { | |
| 119 | parent.totalFiles++ | |
| 120 | parent.totalBytes += f.bytes | |
| 121 | parent = parent.children.get(parts[i]) | |
| 122 | } | |
| 123 | } | |
| 124 | ||
| 125 | return root | |
| 126 | } | |
| 127 | ||
| 128 | // Format tree as a string (like `ls -lR` or `tree`) | |
| 129 | export function formatTree (root, opts = {}) { | |
| 130 | const { maxDepth = 3, minBytes = 0, showFiles = true, indent = '' } = opts | |
| 131 | const lines = [] | |
| 132 | ||
| 133 | function fmt (bytes) { | |
| 134 | if (bytes >= 1024 * 1024 * 1024) return (bytes / 1024 / 1024 / 1024).toFixed(2) + ' GB' | |
| 135 | if (bytes >= 1024 * 1024) return (bytes / 1024 / 1024).toFixed(2) + ' MB' | |
| 136 | if (bytes >= 1024) return (bytes / 1024).toFixed(1) + ' KB' | |
| 137 | return bytes + ' B' | |
| 138 | } | |
| 139 | ||
| 140 | function walk (node, depth, prefix) { | |
| 141 | if (depth > maxDepth) return | |
| 142 | ||
| 143 | // Sort children by totalBytes descending | |
| 144 | const dirs = [...node.children.entries()].sort((a, b) => b[1].totalBytes - a[1].totalBytes) | |
| 145 | ||
| 146 | for (const [name, child] of dirs) { | |
| 147 | if (child.totalBytes < minBytes && minBytes > 0) continue | |
| 148 | const sizeStr = fmt(child.totalBytes).padStart(10) | |
| 149 | const countStr = `${child.totalFiles} files`.padStart(10) | |
| 150 | lines.push(`${prefix}${sizeStr} ${countStr} ${name}/`) | |
| 151 | walk(child, depth + 1, prefix + ' ') | |
| 152 | } | |
| 153 | ||
| 154 | // Show files at this level | |
| 155 | if (showFiles && depth <= maxDepth) { | |
| 156 | const sortedFiles = [...node.files].sort((a, b) => b.bytes - a.bytes) | |
| 157 | for (const f of sortedFiles) { | |
| 158 | if (f.bytes < minBytes && minBytes > 0) continue | |
| 159 | const sizeStr = fmt(f.bytes).padStart(10) | |
| 160 | const exec = f.executable ? ' *' : '' | |
| 161 | const link = f.linkname ? ` -> ${f.linkname}` : '' | |
| 162 | const blk = f.blob ? ` [blk ${f.blob.blockOffset}..${f.blob.blockOffset + f.blob.blockLength}]` : '' | |
| 163 | lines.push(`${prefix}${sizeStr} ${f.name}${exec}${link}${blk}`) | |
| 164 | } | |
| 165 | } | |
| 166 | } | |
| 167 | ||
| 168 | const totalStr = fmt(root.totalBytes).padStart(10) | |
| 169 | lines.push(`${totalStr} ${root.totalFiles} files /`) | |
| 170 | walk(root, 1, ' ') | |
| 171 | ||
| 172 | return lines.join('\n') | |
| 173 | } | |
| 174 | ||
| 175 | // Format as flat ls output (like `ls -lhRS`) | |
| 176 | export function formatFlat (files, opts = {}) { | |
| 177 | const { sortBy = 'size', limit = Infinity, filter = null } = opts | |
| 178 | const lines = [] | |
| 179 | ||
| 180 | let sorted = [...files] | |
| 181 | if (sortBy === 'size') sorted.sort((a, b) => b.bytes - a.bytes) | |
| 182 | else if (sortBy === 'path') sorted.sort((a, b) => a.path.localeCompare(b.path)) | |
| 183 | ||
| 184 | if (filter) { | |
| 185 | sorted = sorted.filter(f => f.path.includes(filter)) | |
| 186 | } | |
| 187 | ||
| 188 | function fmt (bytes) { | |
| 189 | if (bytes >= 1024 * 1024 * 1024) return (bytes / 1024 / 1024 / 1024).toFixed(2) + ' GB' | |
| 190 | if (bytes >= 1024 * 1024) return (bytes / 1024 / 1024).toFixed(2) + ' MB' | |
| 191 | if (bytes >= 1024) return (bytes / 1024).toFixed(1) + ' KB' | |
| 192 | return bytes + ' B' | |
| 193 | } | |
| 194 | ||
| 195 | for (const f of sorted.slice(0, limit)) { | |
| 196 | const sizeStr = fmt(f.bytes).padStart(10) | |
| 197 | const exec = f.executable ? ' *' : '' | |
| 198 | const blk = f.blob ? ` [blk ${f.blob.blockOffset}..${f.blob.blockOffset + f.blob.blockLength}]` : '' | |
| 199 | lines.push(`${sizeStr} ${f.path}${exec}${blk}`) | |
| 200 | } | |
| 201 | ||
| 202 | return lines.join('\n') | |
| 203 | } |
| … | … | @@ -0,0 +1,302 @@ |
| 1 | // du — comprehensive disk usage analysis for all Hyperdrives in a corestore | |
| 2 | import RocksDB from 'rocksdb-native' | |
| 3 | import b4a from 'b4a' | |
| 4 | import { decodeNode, decodeDriveEntry, stripSubEncoder, decodeHeader } from './block-reader.js' | |
| 5 | ||
| 6 | function encodeVarint (n) { | |
| 7 | const bytes = [] | |
| 8 | while (n >= 0x80) { bytes.push((n & 0x7f) | 0x80); n >>>= 7 } | |
| 9 | bytes.push(n & 0x7f) | |
| 10 | return Buffer.from(bytes) | |
| 11 | } | |
| 12 | ||
| 13 | function fmt (bytes) { | |
| 14 | if (bytes >= 1024 * 1024 * 1024) return (bytes / 1024 / 1024 / 1024).toFixed(2) + ' GB' | |
| 15 | if (bytes >= 1024 * 1024) return (bytes / 1024 / 1024).toFixed(2) + ' MB' | |
| 16 | if (bytes >= 1024) return (bytes / 1024).toFixed(1) + ' KB' | |
| 17 | return bytes + ' B' | |
| 18 | } | |
| 19 | ||
| 20 | // Full disk usage report for a single corestore RocksDB | |
| 21 | export async function corestoreDU (dbPath, opts = {}) { | |
| 22 | const { readOnly = true, maxBlocksPerDrive = 50000, dirDepth = 2 } = opts | |
| 23 | ||
| 24 | const db = new RocksDB(dbPath, { readOnly, columnFamilies: ['corestore'] }) | |
| 25 | await db.ready() | |
| 26 | const cs = db.columnFamily('corestore') | |
| 27 | ||
| 28 | // Step 1: Enumerate all cores (key prefix 0x01 = TL_CORE_BY_DKEY) | |
| 29 | const cores = [] | |
| 30 | const coreIter = cs.iterator({ gte: Buffer.from([0x01]), lt: Buffer.from([0x02]), limit: 10000 }) | |
| 31 | await drain(coreIter, ({ key, value }) => { | |
| 32 | if (!value || key[0] !== 0x01) return | |
| 33 | const discoveryKey = b4a.toString(key.subarray(1, 33), 'hex') | |
| 34 | try { | |
| 35 | let pos = 0 | |
| 36 | const r = (buf, off) => { let r = 0, s = 0, p = off; while (p < buf.length) { const b = buf[p]; r |= (b & 0x7f) << s; p++; if ((b & 0x80) === 0) break; s += 7 } return { v: r, e: p } } | |
| 37 | const { v: version, e: e1 } = r(value, pos) | |
| 38 | const { v: corePointer, e: e2 } = r(value, e1) | |
| 39 | const { v: dataPointer, e: e3 } = r(value, e2) | |
| 40 | const { v: flags, e: e4 } = r(value, e3) | |
| 41 | let alias = null | |
| 42 | if (flags & 1) { | |
| 43 | const { v: nameLen, e: ne } = r(value, e4) | |
| 44 | alias = b4a.toString(value.subarray(ne, ne + nameLen), 'utf8') | |
| 45 | } | |
| 46 | ||
| 47 | // Read auth for public key | |
| 48 | const authKey = Buffer.concat([encodeVarint(3), encodeVarint(corePointer), encodeVarint(0)]) | |
| 49 | cores.push({ discoveryKey, corePointer, dataPointer, alias }) | |
| 50 | } catch {} | |
| 51 | }) | |
| 52 | ||
| 53 | // Step 2: For each core, check if block 0 is a Hyperbee header | |
| 54 | const drives = [] | |
| 55 | const nonDriveCores = [] | |
| 56 | ||
| 57 | for (const core of cores) { | |
| 58 | const blockKey = Buffer.concat([encodeVarint(4), encodeVarint(core.dataPointer), encodeVarint(3), encodeVarint(0)]) | |
| 59 | const blockEnd = Buffer.concat([encodeVarint(4), encodeVarint(core.dataPointer), encodeVarint(3), encodeVarint(1)]) | |
| 60 | ||
| 61 | let block0 = null | |
| 62 | const b0Iter = cs.iterator({ gte: blockKey, lt: blockEnd, limit: 1 }) | |
| 63 | await drain(b0Iter, ({ value }) => { block0 = value }) | |
| 64 | ||
| 65 | if (!block0) { | |
| 66 | nonDriveCores.push({ ...core, type: 'empty' }) | |
| 67 | continue | |
| 68 | } | |
| 69 | ||
| 70 | const hex = b4a.toString(block0.subarray(0, Math.min(12, block0.length)), 'hex') | |
| 71 | const isHyperbee = hex.startsWith('0a08687970657262') | |
| 72 | ||
| 73 | if (!isHyperbee) { | |
| 74 | nonDriveCores.push({ ...core, type: 'data' }) | |
| 75 | continue | |
| 76 | } | |
| 77 | ||
| 78 | // Parse header for contentFeed | |
| 79 | let header = null | |
| 80 | try { header = decodeHeader(block0) } catch {} | |
| 81 | ||
| 82 | const contentKey = header?.contentFeed ? b4a.toString(header.contentFeed, 'hex') : null | |
| 83 | ||
| 84 | drives.push({ ...core, header, contentKey, type: 'hyperbee' }) | |
| 85 | } | |
| 86 | ||
| 87 | // Step 3: For each Hyperbee, read all blocks and decode file entries | |
| 88 | const driveReports = [] | |
| 89 | ||
| 90 | for (const drive of drives) { | |
| 91 | const prefix = Buffer.concat([encodeVarint(4), encodeVarint(drive.dataPointer), encodeVarint(3)]) | |
| 92 | const endPrefix = Buffer.concat([encodeVarint(4), encodeVarint(drive.dataPointer), encodeVarint(4)]) | |
| 93 | ||
| 94 | const files = [] | |
| 95 | let blocksRead = 0 | |
| 96 | ||
| 97 | const iter = cs.iterator({ gte: prefix, lt: endPrefix, limit: maxBlocksPerDrive }) | |
| 98 | await drain(iter, ({ key, value }) => { | |
| 99 | blocksRead++ | |
| 100 | if (!value) return | |
| 101 | ||
| 102 | // Skip block 0 (header) | |
| 103 | let pos = prefix.length | |
| 104 | let blockIdx = 0 | |
| 105 | let shift = 0 | |
| 106 | while (pos < key.length) { | |
| 107 | const byte = key[pos]; blockIdx |= (byte & 0x7f) << shift; pos++ | |
| 108 | if ((byte & 0x80) === 0) break; shift += 7 | |
| 109 | } | |
| 110 | if (blockIdx === 0) return | |
| 111 | ||
| 112 | try { | |
| 113 | const node = decodeNode(value) | |
| 114 | if (!node.key || node.key.length === 0) return | |
| 115 | ||
| 116 | const rawKey = stripSubEncoder(node.key) | |
| 117 | let filePath = '' | |
| 118 | try { filePath = b4a.toString(rawKey, 'utf8') } catch { return } | |
| 119 | if (!filePath.startsWith('/')) return | |
| 120 | ||
| 121 | let entry = null | |
| 122 | try { entry = decodeDriveEntry(node.value) } catch {} | |
| 123 | if (!entry) return | |
| 124 | ||
| 125 | files.push({ | |
| 126 | path: filePath, | |
| 127 | bytes: entry.blob?.byteLength || 0, | |
| 128 | blockOffset: entry.blob?.blockOffset, | |
| 129 | blockLength: entry.blob?.blockLength, | |
| 130 | executable: entry.executable | |
| 131 | }) | |
| 132 | } catch {} | |
| 133 | }) | |
| 134 | ||
| 135 | // Build directory breakdown | |
| 136 | const dirs = buildDirSummary(files, dirDepth) | |
| 137 | const totalBytes = files.reduce((s, f) => s + f.bytes, 0) | |
| 138 | ||
| 139 | // Fingerprint the app by unique files | |
| 140 | const appName = identifyApp(files) | |
| 141 | ||
| 142 | driveReports.push({ | |
| 143 | dataPointer: drive.dataPointer, | |
| 144 | corePointer: drive.corePointer, | |
| 145 | alias: drive.alias, | |
| 146 | discoveryKey: drive.discoveryKey, | |
| 147 | contentKey: drive.contentKey, | |
| 148 | blocksRead, | |
| 149 | fileCount: files.length, | |
| 150 | totalBytes, | |
| 151 | appName, | |
| 152 | dirs, | |
| 153 | files | |
| 154 | }) | |
| 155 | } | |
| 156 | ||
| 157 | // Step 4: Compute RocksDB disk usage per data pointer (sample) | |
| 158 | const diskUsage = new Map() | |
| 159 | const diskIter = cs.iterator({ gte: Buffer.from([0x04]), lt: Buffer.from([0x05]), limit: 1000000 }) | |
| 160 | await drain(diskIter, ({ key, value }) => { | |
| 161 | let pos = 1 | |
| 162 | let dataPtr = 0 | |
| 163 | let shift = 0 | |
| 164 | while (pos < key.length) { | |
| 165 | const byte = key[pos]; dataPtr |= (byte & 0x7f) << shift; pos++ | |
| 166 | if ((byte & 0x80) === 0) break; shift += 7 | |
| 167 | } | |
| 168 | const cur = diskUsage.get(dataPtr) || 0 | |
| 169 | diskUsage.set(dataPtr, cur + (value ? value.length : 0)) | |
| 170 | }) | |
| 171 | ||
| 172 | await db.close() | |
| 173 | ||
| 174 | // Map disk usage to drives | |
| 175 | const driveDataPtrs = new Set(driveReports.map(d => d.dataPointer)) | |
| 176 | let orphanedBytes = 0 | |
| 177 | let totalDiskBytes = 0 | |
| 178 | for (const [ptr, bytes] of diskUsage) { | |
| 179 | totalDiskBytes += bytes | |
| 180 | if (!driveDataPtrs.has(ptr)) { | |
| 181 | // Check if this is a content core referenced by a drive | |
| 182 | const isContent = driveReports.some(d => { | |
| 183 | const contentCore = cores.find(c => c.discoveryKey === d.contentKey?.slice(0, 64)) | |
| 184 | return contentCore && contentCore.dataPointer === ptr | |
| 185 | }) | |
| 186 | if (!isContent) { | |
| 187 | // Check if it's a known non-drive core (odd-numbered = typically content for compat drives) | |
| 188 | const isOddPair = driveReports.some(d => d.dataPointer === ptr - 1 || d.dataPointer === ptr + 1) | |
| 189 | if (!isOddPair) { | |
| 190 | orphanedBytes += bytes | |
| 191 | } | |
| 192 | } | |
| 193 | } | |
| 194 | } | |
| 195 | ||
| 196 | return { | |
| 197 | driveReports: driveReports.sort((a, b) => b.totalBytes - a.totalBytes), | |
| 198 | nonDriveCores, | |
| 199 | totalDiskSampled: totalDiskBytes, | |
| 200 | orphanedBytes, | |
| 201 | coreCount: cores.length, | |
| 202 | driveCount: driveReports.length | |
| 203 | } | |
| 204 | } | |
| 205 | ||
| 206 | function buildDirSummary (files, maxDepth) { | |
| 207 | const dirs = new Map() | |
| 208 | ||
| 209 | for (const f of files) { | |
| 210 | const parts = f.path.split('/').filter(Boolean) | |
| 211 | ||
| 212 | for (let depth = 1; depth <= Math.min(parts.length, maxDepth); depth++) { | |
| 213 | const dirPath = '/' + parts.slice(0, depth).join('/') | |
| 214 | const info = dirs.get(dirPath) || { path: dirPath, bytes: 0, count: 0, depth } | |
| 215 | info.bytes += f.bytes | |
| 216 | info.count++ | |
| 217 | dirs.set(dirPath, info) | |
| 218 | } | |
| 219 | ||
| 220 | // Root | |
| 221 | const rootInfo = dirs.get('/') || { path: '/', bytes: 0, count: 0, depth: 0 } | |
| 222 | rootInfo.bytes += f.bytes | |
| 223 | rootInfo.count++ | |
| 224 | dirs.set('/', rootInfo) | |
| 225 | } | |
| 226 | ||
| 227 | return [...dirs.values()].sort((a, b) => b.bytes - a.bytes) | |
| 228 | } | |
| 229 | ||
| 230 | function identifyApp (files) { | |
| 231 | // Heuristics based on unique files | |
| 232 | for (const f of files) { | |
| 233 | if (f.path === '/gateway.js') return 'hyperident-gateway' | |
| 234 | if (f.path.includes('/hyper-wallet.bundle.js')) return 'hyper-wallet' | |
| 235 | if (f.path === '/ARCHITECTURE.md' && files.some(g => g.path.includes('/by-arch/'))) return 'pear-runtime' | |
| 236 | if (f.path.includes('/bare-ffmpeg/') || f.path.includes('/pear-electron/')) return 'keet' | |
| 237 | if (f.path.includes('/socks5-server')) return 'hyperident-gateway' | |
| 238 | if (f.path.includes('/vendor/pear-docs/')) return 'pear-git' | |
| 239 | if (f.path === '/public/app.js') return 'wirecore' | |
| 240 | if (f.path.includes('/keet-tui/') || f.path === '/keet-tui.js') return 'keet-tui' | |
| 241 | if (f.path.includes('/keet-cli/') || f.path === '/keet-cli.js') return 'keet-cli' | |
| 242 | if (f.path.includes('/anproto')) return 'anproto-terminal' | |
| 243 | } | |
| 244 | ||
| 245 | // Check top-level files for package.json identification | |
| 246 | for (const f of files) { | |
| 247 | if (f.path === '/package.json' && f.bytes > 0 && f.bytes < 10000) { | |
| 248 | // We can't read the content from here, just note it | |
| 249 | return '(unknown app)' | |
| 250 | } | |
| 251 | } | |
| 252 | ||
| 253 | return files.length > 0 ? '(unknown)' : '(empty)' | |
| 254 | } | |
| 255 | ||
| 256 | export function formatDU (result, opts = {}) { | |
| 257 | const { dirDepth = 2, minSize = 1024 * 1024 } = opts | |
| 258 | const lines = [] | |
| 259 | ||
| 260 | lines.push(`${result.coreCount} cores, ${result.driveCount} Hyperdrives found`) | |
| 261 | lines.push(`RocksDB sampled: ${fmt(result.totalDiskSampled)}`) | |
| 262 | lines.push('') | |
| 263 | ||
| 264 | for (const d of result.driveReports) { | |
| 265 | const name = d.appName || d.alias || `core #${d.corePointer}` | |
| 266 | lines.push(` ${fmt(d.totalBytes).padStart(10)} ${name} (dptr=${d.dataPointer}, ${d.fileCount} files)`) | |
| 267 | ||
| 268 | // Show top directories | |
| 269 | const topDirs = d.dirs | |
| 270 | .filter(dir => dir.depth > 0 && dir.depth <= dirDepth && dir.bytes >= minSize) | |
| 271 | .slice(0, 15) | |
| 272 | ||
| 273 | for (const dir of topDirs) { | |
| 274 | const indent = ' ' + ' '.repeat(dir.depth - 1) | |
| 275 | lines.push(`${indent}${fmt(dir.bytes).padStart(10)} ${dir.count.toString().padStart(5)} files ${dir.path}`) | |
| 276 | } | |
| 277 | ||
| 278 | lines.push('') | |
| 279 | } | |
| 280 | ||
| 281 | if (result.nonDriveCores.length > 0) { | |
| 282 | const dataCores = result.nonDriveCores.filter(c => c.type === 'data') | |
| 283 | const emptyCores = result.nonDriveCores.filter(c => c.type === 'empty') | |
| 284 | if (dataCores.length > 0) { | |
| 285 | lines.push(` ${dataCores.length} content/blob cores (paired with Hyperdrives above)`) | |
| 286 | } | |
| 287 | if (emptyCores.length > 0) { | |
| 288 | lines.push(` ${emptyCores.length} empty cores (no data blocks)`) | |
| 289 | } | |
| 290 | } | |
| 291 | ||
| 292 | return lines.join('\n') | |
| 293 | } | |
| 294 | ||
| 295 | // Helper: drain a RocksDB iterator stream | |
| 296 | function drain (iter, onData) { | |
| 297 | return new Promise((resolve, reject) => { | |
| 298 | iter.on('data', onData) | |
| 299 | iter.on('end', resolve) | |
| 300 | iter.on('error', reject) | |
| 301 | }) | |
| 302 | } |
| … | … | @@ -0,0 +1,129 @@ |
| 1 | import RocksDB from 'rocksdb-native' | |
| 2 | import b4a from 'b4a' | |
| 3 | ||
| 4 | const CORESTORE_CF = 'corestore' | |
| 5 | ||
| 6 | // Key type labels based on hypercore-storage schema | |
| 7 | const KEY_TYPES = { | |
| 8 | 0x00: 'seed', | |
| 9 | 0x01: 'core-key', | |
| 10 | 0x02: 'discovery-key', | |
| 11 | 0x03: 'core-info', | |
| 12 | 0x04: 'data-block' | |
| 13 | } | |
| 14 | ||
| 15 | export async function openDB (dbPath, opts = {}) { | |
| 16 | const { readOnly = false } = opts | |
| 17 | const db = new RocksDB(dbPath, { | |
| 18 | readOnly, | |
| 19 | columnFamilies: [CORESTORE_CF] | |
| 20 | }) | |
| 21 | await db.ready() | |
| 22 | return db | |
| 23 | } | |
| 24 | ||
| 25 | export async function scanColumnFamily (session, opts = {}) { | |
| 26 | const { limit = 100000 } = opts | |
| 27 | ||
| 28 | const keyTypes = new Map() | |
| 29 | let totalKeys = 0 | |
| 30 | let totalBytes = 0 | |
| 31 | const readable = [] | |
| 32 | ||
| 33 | const iter = session.iterator({ limit }) | |
| 34 | ||
| 35 | return new Promise((resolve, reject) => { | |
| 36 | iter.on('data', ({ key, value }) => { | |
| 37 | totalKeys++ | |
| 38 | const valLen = value ? value.length : 0 | |
| 39 | totalBytes += valLen | |
| 40 | ||
| 41 | const firstByte = key[0] | |
| 42 | const info = keyTypes.get(firstByte) || { count: 0, bytes: 0 } | |
| 43 | info.count++ | |
| 44 | info.bytes += valLen | |
| 45 | keyTypes.set(firstByte, info) | |
| 46 | ||
| 47 | // Collect readable strings | |
| 48 | if (value && valLen > 2 && valLen < 2000) { | |
| 49 | try { | |
| 50 | const str = b4a.toString(value, 'utf8') | |
| 51 | if (/^[\x20-\x7e\n\r\t]+$/.test(str) && readable.length < 100) { | |
| 52 | readable.push({ | |
| 53 | key: b4a.toString(key, 'hex'), | |
| 54 | type: firstByte, | |
| 55 | value: str.slice(0, 300) | |
| 56 | }) | |
| 57 | } | |
| 58 | } catch {} | |
| 59 | } | |
| 60 | }) | |
| 61 | ||
| 62 | iter.on('end', () => resolve({ totalKeys, totalBytes, keyTypes, readable })) | |
| 63 | iter.on('error', reject) | |
| 64 | }) | |
| 65 | } | |
| 66 | ||
| 67 | export async function listApps (db) { | |
| 68 | const defaultIter = db.iterator({ limit: 10000 }) | |
| 69 | ||
| 70 | const pearApps = [] | |
| 71 | const fileSources = [] | |
| 72 | ||
| 73 | return new Promise((resolve, reject) => { | |
| 74 | defaultIter.on('data', ({ key, value }) => { | |
| 75 | if (key.length < 2) return | |
| 76 | ||
| 77 | // Extract readable string from key (skip prefix byte) | |
| 78 | let keyStr = '' | |
| 79 | for (let i = 1; i < key.length; i++) { | |
| 80 | if (key[i] >= 0x20 && key[i] <= 0x7e) { | |
| 81 | keyStr += String.fromCharCode(key[i]) | |
| 82 | } | |
| 83 | } | |
| 84 | ||
| 85 | // Extract readable string from value | |
| 86 | let valStr = '' | |
| 87 | if (value && value.length < 500) { | |
| 88 | for (let i = 0; i < value.length; i++) { | |
| 89 | if (value[i] >= 0x20 && value[i] <= 0x7e) { | |
| 90 | valStr += String.fromCharCode(value[i]) | |
| 91 | } | |
| 92 | } | |
| 93 | } | |
| 94 | ||
| 95 | const prefix = key[0] | |
| 96 | if (keyStr.startsWith('pear://')) { | |
| 97 | pearApps.push({ prefix, url: keyStr, storage: valStr, valLen: value ? value.length : 0 }) | |
| 98 | } else if (keyStr.startsWith('file://')) { | |
| 99 | fileSources.push({ prefix, path: keyStr, storage: valStr, valLen: value ? value.length : 0 }) | |
| 100 | } | |
| 101 | }) | |
| 102 | ||
| 103 | defaultIter.on('end', () => resolve({ pearApps, fileSources })) | |
| 104 | defaultIter.on('error', reject) | |
| 105 | }) | |
| 106 | } | |
| 107 | ||
| 108 | export async function getCorestoreStats (db) { | |
| 109 | const cs = db.columnFamily(CORESTORE_CF) | |
| 110 | const stats = await scanColumnFamily(cs) | |
| 111 | ||
| 112 | return { | |
| 113 | ...stats, | |
| 114 | typeBreakdown: [...stats.keyTypes.entries()].map(([byte, info]) => ({ | |
| 115 | type: KEY_TYPES[byte] || `unknown-0x${byte.toString(16)}`, | |
| 116 | byte, | |
| 117 | count: info.count, | |
| 118 | bytes: info.bytes, | |
| 119 | mb: (info.bytes / 1024 / 1024).toFixed(2) | |
| 120 | })).sort((a, b) => b.bytes - a.bytes) | |
| 121 | } | |
| 122 | } | |
| 123 | ||
| 124 | export function formatBytes (bytes) { | |
| 125 | if (bytes >= 1024 * 1024 * 1024) return (bytes / 1024 / 1024 / 1024).toFixed(2) + ' GB' | |
| 126 | if (bytes >= 1024 * 1024) return (bytes / 1024 / 1024).toFixed(2) + ' MB' | |
| 127 | if (bytes >= 1024) return (bytes / 1024).toFixed(1) + ' KB' | |
| 128 | return bytes + ' B' | |
| 129 | } |
| … | … | @@ -0,0 +1,110 @@ |
| 1 | import { execSync } from 'child_process' | |
| 2 | import { existsSync, readdirSync, statSync } from 'fs' | |
| 3 | import { join } from 'path' | |
| 4 | ||
| 5 | // Known Hypercore storage locations under a home directory | |
| 6 | const KNOWN_PATHS = [ | |
| 7 | '.config/pear/corestores', | |
| 8 | '.config/pear/app-storage', | |
| 9 | '.config/pear/by-dkey', | |
| 10 | '.config/Holepunch', | |
| 11 | '.config/hypernote', | |
| 12 | '.config/keet-cli', | |
| 13 | '.config/keet-daemon', | |
| 14 | '.config/godot-keet', | |
| 15 | '.config/hyper-tps', | |
| 16 | '.config/agregore-browser/hyper', | |
| 17 | '.config/Beaker Browser', | |
| 18 | '.config/manyverse', | |
| 19 | '.config/holepunch-runtime', | |
| 20 | '.config/pear-runtime', | |
| 21 | '.config/hyper-social-web' | |
| 22 | ] | |
| 23 | ||
| 24 | export function findCorestores (homeDir) { | |
| 25 | const results = [] | |
| 26 | ||
| 27 | // Check known paths | |
| 28 | for (const rel of KNOWN_PATHS) { | |
| 29 | const full = join(homeDir, rel) | |
| 30 | if (existsSync(full)) { | |
| 31 | const size = getDirSize(full) | |
| 32 | results.push({ path: full, size, type: 'known', rel }) | |
| 33 | } | |
| 34 | } | |
| 35 | ||
| 36 | // Find CORESTORE marker files | |
| 37 | try { | |
| 38 | const output = execSync( | |
| 39 | `find ${homeDir} -maxdepth 5 -name "CORESTORE" -type f 2>/dev/null`, | |
| 40 | { encoding: 'utf8', timeout: 30000 } | |
| 41 | ) | |
| 42 | for (const line of output.trim().split('\n').filter(Boolean)) { | |
| 43 | const dir = line.replace(/\/CORESTORE$/, '') | |
| 44 | // Check if already covered by known paths | |
| 45 | const isKnown = results.some(r => dir.startsWith(r.path)) | |
| 46 | if (!isKnown) { | |
| 47 | const size = getDirSize(dir) | |
| 48 | results.push({ path: dir, size, type: 'discovered', marker: line }) | |
| 49 | } | |
| 50 | } | |
| 51 | } catch {} | |
| 52 | ||
| 53 | // Sort by size descending | |
| 54 | results.sort((a, b) => b.size - a.size) | |
| 55 | ||
| 56 | return results | |
| 57 | } | |
| 58 | ||
| 59 | export function getDirSize (dirPath) { | |
| 60 | try { | |
| 61 | const output = execSync(`du -sb "${dirPath}" 2>/dev/null`, { encoding: 'utf8', timeout: 30000 }) | |
| 62 | return parseInt(output.split('\t')[0]) || 0 | |
| 63 | } catch { | |
| 64 | return 0 | |
| 65 | } | |
| 66 | } | |
| 67 | ||
| 68 | export function getLastModified (dirPath) { | |
| 69 | try { | |
| 70 | const stat = statSync(dirPath) | |
| 71 | return stat.mtime | |
| 72 | } catch { | |
| 73 | return null | |
| 74 | } | |
| 75 | } | |
| 76 | ||
| 77 | export function isLocked (dbPath) { | |
| 78 | try { | |
| 79 | const lockFile = join(dbPath, 'LOCK') | |
| 80 | if (!existsSync(lockFile)) return false | |
| 81 | const output = execSync(`lsof "${lockFile}" 2>/dev/null`, { encoding: 'utf8', timeout: 5000 }) | |
| 82 | return output.trim().length > 0 | |
| 83 | } catch { | |
| 84 | return false | |
| 85 | } | |
| 86 | } | |
| 87 | ||
| 88 | export function getLockHolder (dbPath) { | |
| 89 | try { | |
| 90 | const lockFile = join(dbPath, 'LOCK') | |
| 91 | const output = execSync(`lsof "${lockFile}" 2>/dev/null`, { encoding: 'utf8', timeout: 5000 }) | |
| 92 | const lines = output.trim().split('\n') | |
| 93 | if (lines.length > 1) { | |
| 94 | const parts = lines[1].split(/\s+/) | |
| 95 | return { command: parts[0], pid: parts[1] } | |
| 96 | } | |
| 97 | } catch {} | |
| 98 | return null | |
| 99 | } | |
| 100 | ||
| 101 | export function hasRocksDB (dirPath) { | |
| 102 | const dbPath = join(dirPath, 'db') | |
| 103 | if (existsSync(dbPath)) { | |
| 104 | try { | |
| 105 | const files = readdirSync(dbPath) | |
| 106 | return files.some(f => f.endsWith('.sst') || f.endsWith('.blob')) | |
| 107 | } catch {} | |
| 108 | } | |
| 109 | return false | |
| 110 | } |
| … | … | @@ -0,0 +1,1004 @@ |
| 1 | { | |
| 2 | "name": "hyper-inspector", | |
| 3 | "version": "0.1.0", | |
| 4 | "lockfileVersion": 3, | |
| 5 | "requires": true, | |
| 6 | "packages": { | |
| 7 | "node_modules/@hyperswarm/secret-stream": { | |
| 8 | "version": "6.9.1", | |
| 9 | "resolved": "https://registry.npmjs.org/@hyperswarm/secret-stream/-/secret-stream-6.9.1.tgz", | |
| 10 | "integrity": "sha512-xb0S5y3YJwBakD77JOGBHlBxdp63mHClZoXBYoLv+9wH8e054ESKlmQptWqjJK5dv5VMUIVYOJB4MaOpB0JdGw==", | |
| 11 | "license": "Apache-2.0", | |
| 12 | "dependencies": { | |
| 13 | "b4a": "^1.1.0", | |
| 14 | "hypercore-crypto": "^3.3.1", | |
| 15 | "noise-curve-ed": "^2.0.1", | |
| 16 | "noise-handshake": "^4.0.0", | |
| 17 | "sodium-secretstream": "^1.1.0", | |
| 18 | "sodium-universal": "^5.0.0", | |
| 19 | "streamx": "^2.14.0", | |
| 20 | "timeout-refresh": "^2.0.0", | |
| 21 | "unslab": "^1.3.0" | |
| 22 | } | |
| 23 | }, | |
| 24 | "node_modules/@hyperswarm/secret-stream/node_modules/sodium-native": { | |
| 25 | "version": "5.1.0", | |
| 26 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.1.0.tgz", | |
| 27 | "integrity": "sha512-3RxgyWyJlhTsABPnJVpCI5CoTDANZTqqFrEPqr+kjfnRaBihpVtMUE3yTF40ukdoB1APXeoBNKF3MzZAIHg39g==", | |
| 28 | "license": "MIT", | |
| 29 | "dependencies": { | |
| 30 | "bare-assert": "^1.2.0", | |
| 31 | "require-addon": "^1.1.0", | |
| 32 | "which-runtime": "^1.2.1" | |
| 33 | }, | |
| 34 | "engines": { | |
| 35 | "bare": ">=1.16.0" | |
| 36 | } | |
| 37 | }, | |
| 38 | "node_modules/@hyperswarm/secret-stream/node_modules/sodium-universal": { | |
| 39 | "version": "5.0.1", | |
| 40 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz", | |
| 41 | "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==", | |
| 42 | "license": "MIT", | |
| 43 | "dependencies": { | |
| 44 | "sodium-native": "^5.0.1" | |
| 45 | }, | |
| 46 | "peerDependencies": { | |
| 47 | "sodium-javascript": "~0.8.0" | |
| 48 | }, | |
| 49 | "peerDependenciesMeta": { | |
| 50 | "sodium-javascript": { | |
| 51 | "optional": true | |
| 52 | } | |
| 53 | } | |
| 54 | }, | |
| 55 | "node_modules/b4a": { | |
| 56 | "version": "1.8.0", | |
| 57 | "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", | |
| 58 | "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==", | |
| 59 | "license": "Apache-2.0", | |
| 60 | "peerDependencies": { | |
| 61 | "react-native-b4a": "*" | |
| 62 | }, | |
| 63 | "peerDependenciesMeta": { | |
| 64 | "react-native-b4a": { | |
| 65 | "optional": true | |
| 66 | } | |
| 67 | } | |
| 68 | }, | |
| 69 | "node_modules/bare-addon-resolve": { | |
| 70 | "version": "1.10.0", | |
| 71 | "resolved": "https://registry.npmjs.org/bare-addon-resolve/-/bare-addon-resolve-1.10.0.tgz", | |
| 72 | "integrity": "sha512-sSd0jieRJlDaODOzj0oe0RjFVC1QI0ZIjGIdPkbrTXsdVVtENg14c+lHHAhHwmWCZ2nQlMhy8jA3Y5LYPc/isA==", | |
| 73 | "license": "Apache-2.0", | |
| 74 | "dependencies": { | |
| 75 | "bare-module-resolve": "^1.10.0", | |
| 76 | "bare-semver": "^1.0.0" | |
| 77 | }, | |
| 78 | "peerDependencies": { | |
| 79 | "bare-url": "*" | |
| 80 | }, | |
| 81 | "peerDependenciesMeta": { | |
| 82 | "bare-url": { | |
| 83 | "optional": true | |
| 84 | } | |
| 85 | } | |
| 86 | }, | |
| 87 | "node_modules/bare-ansi-escapes": { | |
| 88 | "version": "2.2.3", | |
| 89 | "resolved": "https://registry.npmjs.org/bare-ansi-escapes/-/bare-ansi-escapes-2.2.3.tgz", | |
| 90 | "integrity": "sha512-02ES4/E2RbrtZSnHJ9LntBhYkLA6lPpSEeP8iqS3MccBIVhVBlEmruF1I7HZqx5Q8aiTeYfQVeqmrU9YO2yYoQ==", | |
| 91 | "license": "Apache-2.0", | |
| 92 | "dependencies": { | |
| 93 | "bare-stream": "^2.6.5" | |
| 94 | }, | |
| 95 | "peerDependencies": { | |
| 96 | "bare-buffer": "*" | |
| 97 | }, | |
| 98 | "peerDependenciesMeta": { | |
| 99 | "bare-buffer": { | |
| 100 | "optional": true | |
| 101 | } | |
| 102 | } | |
| 103 | }, | |
| 104 | "node_modules/bare-assert": { | |
| 105 | "version": "1.2.0", | |
| 106 | "resolved": "https://registry.npmjs.org/bare-assert/-/bare-assert-1.2.0.tgz", | |
| 107 | "integrity": "sha512-c6uvgvTJBspTDxtVnPgrBKmLgcpW3Fp72NVKDLg6oT4QjQbhGtvrkHMhGYMK1sh4vjBHOBmuUalyt9hSzV37fQ==", | |
| 108 | "license": "Apache-2.0", | |
| 109 | "dependencies": { | |
| 110 | "bare-inspect": "^3.1.2" | |
| 111 | } | |
| 112 | }, | |
| 113 | "node_modules/bare-events": { | |
| 114 | "version": "2.8.2", | |
| 115 | "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", | |
| 116 | "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", | |
| 117 | "license": "Apache-2.0", | |
| 118 | "peerDependencies": { | |
| 119 | "bare-abort-controller": "*" | |
| 120 | }, | |
| 121 | "peerDependenciesMeta": { | |
| 122 | "bare-abort-controller": { | |
| 123 | "optional": true | |
| 124 | } | |
| 125 | } | |
| 126 | }, | |
| 127 | "node_modules/bare-fs": { | |
| 128 | "version": "4.5.6", | |
| 129 | "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.6.tgz", | |
| 130 | "integrity": "sha512-1QovqDrR80Pmt5HPAsMsXTCFcDYr+NSUKW6nd6WO5v0JBmnItc/irNRzm2KOQ5oZ69P37y+AMujNyNtG+1Rggw==", | |
| 131 | "license": "Apache-2.0", | |
| 132 | "dependencies": { | |
| 133 | "bare-events": "^2.5.4", | |
| 134 | "bare-path": "^3.0.0", | |
| 135 | "bare-stream": "^2.6.4", | |
| 136 | "bare-url": "^2.2.2", | |
| 137 | "fast-fifo": "^1.3.2" | |
| 138 | }, | |
| 139 | "engines": { | |
| 140 | "bare": ">=1.16.0" | |
| 141 | }, | |
| 142 | "peerDependencies": { | |
| 143 | "bare-buffer": "*" | |
| 144 | }, | |
| 145 | "peerDependenciesMeta": { | |
| 146 | "bare-buffer": { | |
| 147 | "optional": true | |
| 148 | } | |
| 149 | } | |
| 150 | }, | |
| 151 | "node_modules/bare-inspect": { | |
| 152 | "version": "3.1.4", | |
| 153 | "resolved": "https://registry.npmjs.org/bare-inspect/-/bare-inspect-3.1.4.tgz", | |
| 154 | "integrity": "sha512-jfW5KRA84o3REpI6Vr4nbvMn+hqVAw8GU1mMdRwUsY5yJovQamxYeKGVKGqdzs+8ZbG4jRzGUXP/3Ji/DnqfPg==", | |
| 155 | "license": "Apache-2.0", | |
| 156 | "dependencies": { | |
| 157 | "bare-ansi-escapes": "^2.1.0", | |
| 158 | "bare-type": "^1.0.0" | |
| 159 | }, | |
| 160 | "engines": { | |
| 161 | "bare": ">=1.18.0" | |
| 162 | } | |
| 163 | }, | |
| 164 | "node_modules/bare-module-resolve": { | |
| 165 | "version": "1.12.1", | |
| 166 | "resolved": "https://registry.npmjs.org/bare-module-resolve/-/bare-module-resolve-1.12.1.tgz", | |
| 167 | "integrity": "sha512-hbmAPyFpEq8FoZMd5sFO3u6MC5feluWoGE8YKlA8fCrl6mNtx68Wjg4DTiDJcqRJaovTvOYKfYngoBUnbaT7eg==", | |
| 168 | "license": "Apache-2.0", | |
| 169 | "dependencies": { | |
| 170 | "bare-semver": "^1.0.0" | |
| 171 | }, | |
| 172 | "peerDependencies": { | |
| 173 | "bare-url": "*" | |
| 174 | }, | |
| 175 | "peerDependenciesMeta": { | |
| 176 | "bare-url": { | |
| 177 | "optional": true | |
| 178 | } | |
| 179 | } | |
| 180 | }, | |
| 181 | "node_modules/bare-os": { | |
| 182 | "version": "3.8.0", | |
| 183 | "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.8.0.tgz", | |
| 184 | "integrity": "sha512-Dc9/SlwfxkXIGYhvMQNUtKaXCaGkZYGcd1vuNUUADVqzu4/vQfvnMkYYOUnt2VwQ2AqKr/8qAVFRtwETljgeFg==", | |
| 185 | "license": "Apache-2.0", | |
| 186 | "engines": { | |
| 187 | "bare": ">=1.14.0" | |
| 188 | } | |
| 189 | }, | |
| 190 | "node_modules/bare-path": { | |
| 191 | "version": "3.0.0", | |
| 192 | "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", | |
| 193 | "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", | |
| 194 | "license": "Apache-2.0", | |
| 195 | "dependencies": { | |
| 196 | "bare-os": "^3.0.1" | |
| 197 | } | |
| 198 | }, | |
| 199 | "node_modules/bare-semver": { | |
| 200 | "version": "1.0.2", | |
| 201 | "resolved": "https://registry.npmjs.org/bare-semver/-/bare-semver-1.0.2.tgz", | |
| 202 | "integrity": "sha512-ESVaN2nzWhcI5tf3Zzcq9aqCZ676VWzqw07eEZ0qxAcEOAFYBa0pWq8sK34OQeHLY3JsfKXZS9mDyzyxGjeLzA==", | |
| 203 | "license": "Apache-2.0" | |
| 204 | }, | |
| 205 | "node_modules/bare-stream": { | |
| 206 | "version": "2.10.0", | |
| 207 | "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.10.0.tgz", | |
| 208 | "integrity": "sha512-DOPZF/DDcDruKDA43cOw6e9Quq5daua7ygcAwJE/pKJsRWhgSSemi7qVNGE5kyDIxIeN1533G/zfbvWX7Wcb9w==", | |
| 209 | "license": "Apache-2.0", | |
| 210 | "dependencies": { | |
| 211 | "streamx": "^2.25.0", | |
| 212 | "teex": "^1.0.1" | |
| 213 | }, | |
| 214 | "peerDependencies": { | |
| 215 | "bare-buffer": "*", | |
| 216 | "bare-events": "*" | |
| 217 | }, | |
| 218 | "peerDependenciesMeta": { | |
| 219 | "bare-buffer": { | |
| 220 | "optional": true | |
| 221 | }, | |
| 222 | "bare-events": { | |
| 223 | "optional": true | |
| 224 | } | |
| 225 | } | |
| 226 | }, | |
| 227 | "node_modules/bare-type": { | |
| 228 | "version": "1.1.0", | |
| 229 | "resolved": "https://registry.npmjs.org/bare-type/-/bare-type-1.1.0.tgz", | |
| 230 | "integrity": "sha512-LdtnnEEYldOc87Dr4GpsKnStStZk3zfgoEMXy8yvEZkXrcCv9RtYDrUYWFsBQHtaB0s1EUWmcvS6XmEZYIj3Bw==", | |
| 231 | "license": "Apache-2.0", | |
| 232 | "engines": { | |
| 233 | "bare": ">=1.2.0" | |
| 234 | } | |
| 235 | }, | |
| 236 | "node_modules/bare-url": { | |
| 237 | "version": "2.4.0", | |
| 238 | "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.0.tgz", | |
| 239 | "integrity": "sha512-NSTU5WN+fy/L0DDenfE8SXQna4voXuW0FHM7wH8i3/q9khUSchfPbPezO4zSFMnDGIf9YE+mt/RWhZgNRKRIXA==", | |
| 240 | "license": "Apache-2.0", | |
| 241 | "dependencies": { | |
| 242 | "bare-path": "^3.0.0" | |
| 243 | } | |
| 244 | }, | |
| 245 | "node_modules/big-sparse-array": { | |
| 246 | "version": "1.0.3", | |
| 247 | "resolved": "https://registry.npmjs.org/big-sparse-array/-/big-sparse-array-1.0.3.tgz", | |
| 248 | "integrity": "sha512-6RjV/3mSZORlMdpUaQ6rUSpG637cZm0//E54YYGtQg1c1O+AbZP8UTdJ/TchsDZcTVLmyWZcseBfp2HBeXUXOQ==", | |
| 249 | "license": "MIT" | |
| 250 | }, | |
| 251 | "node_modules/binary-stream-equals": { | |
| 252 | "version": "1.0.0", | |
| 253 | "resolved": "https://registry.npmjs.org/binary-stream-equals/-/binary-stream-equals-1.0.0.tgz", | |
| 254 | "integrity": "sha512-xiUT5LGfD8JiLhbXiG+ByOnbgb9f2ssRLfZDQMl3nZdf89EotQZGZuMkDN8J3n46emabE7RnJ1q0r7Hv3INExw==", | |
| 255 | "license": "MIT", | |
| 256 | "dependencies": { | |
| 257 | "b4a": "^1.3.1" | |
| 258 | } | |
| 259 | }, | |
| 260 | "node_modules/codecs": { | |
| 261 | "version": "3.1.0", | |
| 262 | "resolved": "https://registry.npmjs.org/codecs/-/codecs-3.1.0.tgz", | |
| 263 | "integrity": "sha512-Dqx8NwvBvnMeuPQdVKy/XEF71igjR5apxBvCGeV0pP1tXadOiaLvDTXt7xh+/5wI1ASB195mXQGJbw3Ml4YDWQ==", | |
| 264 | "license": "MIT", | |
| 265 | "dependencies": { | |
| 266 | "b4a": "^1.6.3" | |
| 267 | } | |
| 268 | }, | |
| 269 | "node_modules/compact-encoding": { | |
| 270 | "version": "2.19.1", | |
| 271 | "resolved": "https://registry.npmjs.org/compact-encoding/-/compact-encoding-2.19.1.tgz", | |
| 272 | "integrity": "sha512-4J4FHc5UugLv2YMss1tL8OBZdgR0vVkV4EuXoAqFS4NbK4gz0Qp+E+VGUnwvDqmMOw2ce/H8duXh3rreC0Qccg==", | |
| 273 | "license": "Apache-2.0", | |
| 274 | "dependencies": { | |
| 275 | "b4a": "^1.3.0" | |
| 276 | } | |
| 277 | }, | |
| 278 | "node_modules/corestore": { | |
| 279 | "version": "6.18.4", | |
| 280 | "resolved": "https://registry.npmjs.org/corestore/-/corestore-6.18.4.tgz", | |
| 281 | "integrity": "sha512-KL1cOUyj3DFRNzeVRekGPRiGN4sIfGo7BsncSJQQBT3Rh2y1nQ32MOR9X5DlOMlWIT4txwqBzhzam6+JXtwFVw==", | |
| 282 | "license": "MIT", | |
| 283 | "dependencies": { | |
| 284 | "b4a": "^1.6.4", | |
| 285 | "hypercore": "^10.37.10", | |
| 286 | "hypercore-crypto": "^3.4.0", | |
| 287 | "hypercore-id-encoding": "^1.2.0", | |
| 288 | "read-write-mutexify": "^2.1.0", | |
| 289 | "ready-resource": "^1.0.0", | |
| 290 | "safety-catch": "^1.0.1", | |
| 291 | "sodium-universal": "^4.0.0", | |
| 292 | "xache": "^1.1.0" | |
| 293 | } | |
| 294 | }, | |
| 295 | "node_modules/crc-native": { | |
| 296 | "version": "1.1.8", | |
| 297 | "resolved": "https://registry.npmjs.org/crc-native/-/crc-native-1.1.8.tgz", | |
| 298 | "integrity": "sha512-R/RYbD8lhG5M1k0iLX49tJdwHM0ipevJPpZw2Wk7b4EShyvx2P0uaq79xyRpcknjiVGiUfjFA8Gyy0mOlhABvw==", | |
| 299 | "license": "Apache-2.0", | |
| 300 | "optional": true, | |
| 301 | "dependencies": { | |
| 302 | "require-addon": "^1.1.0" | |
| 303 | } | |
| 304 | }, | |
| 305 | "node_modules/crc-universal": { | |
| 306 | "version": "1.0.4", | |
| 307 | "resolved": "https://registry.npmjs.org/crc-universal/-/crc-universal-1.0.4.tgz", | |
| 308 | "integrity": "sha512-CE9xWEI6Gd5V0Bdmj5NoWH3d7+EIe4zUpy1sv6uvKYznzsDP1vhiWsTBmL6q9IrH2P6RRshp+8AkhR4CMtY5Hg==", | |
| 309 | "license": "ISC", | |
| 310 | "optionalDependencies": { | |
| 311 | "crc-native": "^1.0.3" | |
| 312 | } | |
| 313 | }, | |
| 314 | "node_modules/debounceify": { | |
| 315 | "version": "1.1.0", | |
| 316 | "resolved": "https://registry.npmjs.org/debounceify/-/debounceify-1.1.0.tgz", | |
| 317 | "integrity": "sha512-eKuHDVfJVg+u/0nPy8P+fhnLgbyuTgVxuCRrS/R7EpDSMMkBDgSes41MJtSAY1F1hcqfHz3Zy/qpqHHIp/EhdA==", | |
| 318 | "license": "MIT" | |
| 319 | }, | |
| 320 | "node_modules/events-universal": { | |
| 321 | "version": "1.0.1", | |
| 322 | "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", | |
| 323 | "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", | |
| 324 | "license": "Apache-2.0", | |
| 325 | "dependencies": { | |
| 326 | "bare-events": "^2.7.0" | |
| 327 | } | |
| 328 | }, | |
| 329 | "node_modules/fast-fifo": { | |
| 330 | "version": "1.3.2", | |
| 331 | "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", | |
| 332 | "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", | |
| 333 | "license": "MIT" | |
| 334 | }, | |
| 335 | "node_modules/flat-tree": { | |
| 336 | "version": "1.13.0", | |
| 337 | "resolved": "https://registry.npmjs.org/flat-tree/-/flat-tree-1.13.0.tgz", | |
| 338 | "integrity": "sha512-fT3HIuCPwHhFgJ20QYzDHgUG0zMmFg5cHvFiFo5h+QMSJ28TihsEVY0f8HGliuO+pOzmvjMx1odToeaEWkTnyQ==", | |
| 339 | "license": "MIT" | |
| 340 | }, | |
| 341 | "node_modules/fs-native-extensions": { | |
| 342 | "version": "1.4.5", | |
| 343 | "resolved": "https://registry.npmjs.org/fs-native-extensions/-/fs-native-extensions-1.4.5.tgz", | |
| 344 | "integrity": "sha512-ekV0T//iDm4AvhOcuPaHpxub4DI7HvY5ucLJVDvi7T2J+NZkQ9S6MuvgP0yeQvoqNUaAGyLjVYb1905BF9bpmg==", | |
| 345 | "license": "Apache-2.0", | |
| 346 | "optional": true, | |
| 347 | "dependencies": { | |
| 348 | "require-addon": "^1.1.0", | |
| 349 | "which-runtime": "^1.2.0" | |
| 350 | } | |
| 351 | }, | |
| 352 | "node_modules/hyperbee": { | |
| 353 | "version": "2.27.3", | |
| 354 | "resolved": "https://registry.npmjs.org/hyperbee/-/hyperbee-2.27.3.tgz", | |
| 355 | "integrity": "sha512-PXURH2U4juUZyJRKHTrY5z1zX851pmI1Q0jfv5F/hCIErDt/ND8jOZuxc3hfOLM9f0W3qJEDTMlV5AJBkVPy8w==", | |
| 356 | "license": "MIT", | |
| 357 | "dependencies": { | |
| 358 | "b4a": "^1.6.0", | |
| 359 | "codecs": "^3.0.0", | |
| 360 | "debounceify": "^1.0.0", | |
| 361 | "hypercore-errors": "^1.0.0", | |
| 362 | "mutexify": "^1.4.0", | |
| 363 | "protocol-buffers-encodings": "^1.2.0", | |
| 364 | "rache": "^1.0.0", | |
| 365 | "ready-resource": "^1.0.0", | |
| 366 | "resolve-reject-promise": "^1.1.0", | |
| 367 | "safety-catch": "^1.0.2", | |
| 368 | "streamx": "^2.12.4", | |
| 369 | "unslab": "^1.2.0" | |
| 370 | } | |
| 371 | }, | |
| 372 | "node_modules/hyperblobs": { | |
| 373 | "version": "2.9.0", | |
| 374 | "resolved": "https://registry.npmjs.org/hyperblobs/-/hyperblobs-2.9.0.tgz", | |
| 375 | "integrity": "sha512-kbJV2+QvXIcFejqOj2UgSjeeHMjWqP1n083vcI+j4E93MhV3oVV2L8/6X+K/id/KQrUmngPTzNTHHXKlkb1CEA==", | |
| 376 | "license": "Apache-2.0", | |
| 377 | "dependencies": { | |
| 378 | "b4a": "^1.6.1", | |
| 379 | "bare-events": "^2.5.0", | |
| 380 | "compact-encoding": "^2.18.0", | |
| 381 | "hypercore-crypto": "^3.6.1", | |
| 382 | "hypercore-errors": "^1.1.1", | |
| 383 | "mutexify": "^1.4.0", | |
| 384 | "speedometer": "^1.1.0", | |
| 385 | "streamx": "^2.13.2" | |
| 386 | } | |
| 387 | }, | |
| 388 | "node_modules/hypercore": { | |
| 389 | "version": "10.38.2", | |
| 390 | "resolved": "https://registry.npmjs.org/hypercore/-/hypercore-10.38.2.tgz", | |
| 391 | "integrity": "sha512-2vx42g2RKdP/o2XzlxKfvgEaZ3+h/B855gCR8iscUDnxUoOZej7vXZ2buex0sweDUEW5aEfMxFR6o5hncio3Nw==", | |
| 392 | "license": "MIT", | |
| 393 | "dependencies": { | |
| 394 | "@hyperswarm/secret-stream": "^6.0.0", | |
| 395 | "b4a": "^1.1.0", | |
| 396 | "bare-events": "^2.2.0", | |
| 397 | "big-sparse-array": "^1.0.3", | |
| 398 | "compact-encoding": "^2.11.0", | |
| 399 | "crc-universal": "^1.0.2", | |
| 400 | "fast-fifo": "^1.3.0", | |
| 401 | "flat-tree": "^1.9.0", | |
| 402 | "hypercore-crypto": "^3.2.1", | |
| 403 | "hypercore-errors": "^1.2.0", | |
| 404 | "hypercore-id-encoding": "^1.2.0", | |
| 405 | "is-options": "^1.0.1", | |
| 406 | "protomux": "^3.5.0", | |
| 407 | "quickbit-universal": "^2.2.0", | |
| 408 | "random-access-file": "^4.0.0", | |
| 409 | "random-array-iterator": "^1.0.0", | |
| 410 | "safety-catch": "^1.0.1", | |
| 411 | "sodium-universal": "^4.0.0", | |
| 412 | "streamx": "^2.12.4", | |
| 413 | "unslab": "^1.3.0", | |
| 414 | "xache": "^1.1.0", | |
| 415 | "z32": "^1.0.0" | |
| 416 | } | |
| 417 | }, | |
| 418 | "node_modules/hypercore-crypto": { | |
| 419 | "version": "3.6.1", | |
| 420 | "resolved": "https://registry.npmjs.org/hypercore-crypto/-/hypercore-crypto-3.6.1.tgz", | |
| 421 | "integrity": "sha512-ltIz2uDwy9pO/ZGTvqcjzyBkvt6O4cVm4r/nNxh0GFs/RbQtqP/i4wCvLEdmU7ptgtnw7fI67WYD1aHPuv4OVA==", | |
| 422 | "license": "MIT", | |
| 423 | "dependencies": { | |
| 424 | "b4a": "^1.6.6", | |
| 425 | "compact-encoding": "^2.15.0", | |
| 426 | "sodium-universal": "^5.0.0" | |
| 427 | } | |
| 428 | }, | |
| 429 | "node_modules/hypercore-crypto/node_modules/sodium-native": { | |
| 430 | "version": "5.1.0", | |
| 431 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.1.0.tgz", | |
| 432 | "integrity": "sha512-3RxgyWyJlhTsABPnJVpCI5CoTDANZTqqFrEPqr+kjfnRaBihpVtMUE3yTF40ukdoB1APXeoBNKF3MzZAIHg39g==", | |
| 433 | "license": "MIT", | |
| 434 | "dependencies": { | |
| 435 | "bare-assert": "^1.2.0", | |
| 436 | "require-addon": "^1.1.0", | |
| 437 | "which-runtime": "^1.2.1" | |
| 438 | }, | |
| 439 | "engines": { | |
| 440 | "bare": ">=1.16.0" | |
| 441 | } | |
| 442 | }, | |
| 443 | "node_modules/hypercore-crypto/node_modules/sodium-universal": { | |
| 444 | "version": "5.0.1", | |
| 445 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz", | |
| 446 | "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==", | |
| 447 | "license": "MIT", | |
| 448 | "dependencies": { | |
| 449 | "sodium-native": "^5.0.1" | |
| 450 | }, | |
| 451 | "peerDependencies": { | |
| 452 | "sodium-javascript": "~0.8.0" | |
| 453 | }, | |
| 454 | "peerDependenciesMeta": { | |
| 455 | "sodium-javascript": { | |
| 456 | "optional": true | |
| 457 | } | |
| 458 | } | |
| 459 | }, | |
| 460 | "node_modules/hypercore-errors": { | |
| 461 | "version": "1.5.0", | |
| 462 | "resolved": "https://registry.npmjs.org/hypercore-errors/-/hypercore-errors-1.5.0.tgz", | |
| 463 | "integrity": "sha512-5KQ/SuDxsvet+7qWA35Ay6zdD9WyAHQoyWHGcPUTbmJBd300gvNIJoi3oma7kp4TTCSzii6qYumNZe/s0j/saQ==", | |
| 464 | "license": "Apache-2.0", | |
| 465 | "dependencies": { | |
| 466 | "hypercore-id-encoding": "^1.3.0" | |
| 467 | } | |
| 468 | }, | |
| 469 | "node_modules/hypercore-id-encoding": { | |
| 470 | "version": "1.3.0", | |
| 471 | "resolved": "https://registry.npmjs.org/hypercore-id-encoding/-/hypercore-id-encoding-1.3.0.tgz", | |
| 472 | "integrity": "sha512-W6sHdGo5h7LXEsoWfKf/KfuROZmZRQDlGqJF2EPHW+noCK66Vvr0+zE6cL0vqQi18s0kQPeN7Sq3QyR0Ytc2VQ==", | |
| 473 | "license": "Apache-2.0", | |
| 474 | "dependencies": { | |
| 475 | "b4a": "^1.5.3", | |
| 476 | "z32": "^1.0.0" | |
| 477 | } | |
| 478 | }, | |
| 479 | "node_modules/hyperdrive": { | |
| 480 | "version": "11.13.4", | |
| 481 | "resolved": "https://registry.npmjs.org/hyperdrive/-/hyperdrive-11.13.4.tgz", | |
| 482 | "integrity": "sha512-PN0gClSRx+QBbsLGnGqo2ei9TavJN8d6D3jv2yNMlu7zgXSnqR6QcGo8mgyKnrzgR336oNfvQ2C2Y6cY5klScg==", | |
| 483 | "license": "Apache-2.0", | |
| 484 | "dependencies": { | |
| 485 | "hyperbee": "^2.11.1", | |
| 486 | "hyperblobs": "^2.3.0", | |
| 487 | "hypercore": "^10.33.0", | |
| 488 | "hypercore-errors": "^1.0.0", | |
| 489 | "is-options": "^1.0.2", | |
| 490 | "mirror-drive": "^1.2.0", | |
| 491 | "ready-resource": "^1.0.0", | |
| 492 | "safety-catch": "^1.0.2", | |
| 493 | "speedometer": "^1.1.0", | |
| 494 | "streamx": "^2.12.4", | |
| 495 | "sub-encoder": "^2.1.1", | |
| 496 | "unix-path-resolve": "^1.0.2" | |
| 497 | } | |
| 498 | }, | |
| 499 | "node_modules/is-options": { | |
| 500 | "version": "1.0.2", | |
| 501 | "resolved": "https://registry.npmjs.org/is-options/-/is-options-1.0.2.tgz", | |
| 502 | "integrity": "sha512-u+Ai74c8Q74aS8BuHwPdI1jptGOT1FQXgCq8/zv0xRuE+wRgSMEJLj8lVO8Zp9BeGb29BXY6AsNPinfqjkr7Fg==", | |
| 503 | "license": "MIT", | |
| 504 | "dependencies": { | |
| 505 | "b4a": "^1.1.1" | |
| 506 | } | |
| 507 | }, | |
| 508 | "node_modules/mirror-drive": { | |
| 509 | "version": "1.13.0", | |
| 510 | "resolved": "https://registry.npmjs.org/mirror-drive/-/mirror-drive-1.13.0.tgz", | |
| 511 | "integrity": "sha512-dwf2eIaeFgzv03hfW4aCQ/smNIGuxbsnNRrdGAGIyS70Hp0lbOoHXce2V34hGCoxmzn7jeLA4bHJiWlfKOu2Sg==", | |
| 512 | "license": "Apache-2.0", | |
| 513 | "dependencies": { | |
| 514 | "bare-events": "^2.8.2", | |
| 515 | "binary-stream-equals": "^1.0.0", | |
| 516 | "rabin-stream": "^2.0.0", | |
| 517 | "same-data": "^1.0.0", | |
| 518 | "speedometer": "^1.1.0", | |
| 519 | "streamx": "^2.22.1", | |
| 520 | "unix-path-resolve": "^1.0.2" | |
| 521 | } | |
| 522 | }, | |
| 523 | "node_modules/mutexify": { | |
| 524 | "version": "1.4.0", | |
| 525 | "resolved": "https://registry.npmjs.org/mutexify/-/mutexify-1.4.0.tgz", | |
| 526 | "integrity": "sha512-pbYSsOrSB/AKN5h/WzzLRMFgZhClWccf2XIB4RSMC8JbquiB0e0/SH5AIfdQMdyHmYtv4seU7yV/TvAwPLJ1Yg==", | |
| 527 | "license": "MIT", | |
| 528 | "dependencies": { | |
| 529 | "queue-tick": "^1.0.0" | |
| 530 | } | |
| 531 | }, | |
| 532 | "node_modules/nanoassert": { | |
| 533 | "version": "2.0.0", | |
| 534 | "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", | |
| 535 | "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==", | |
| 536 | "license": "ISC" | |
| 537 | }, | |
| 538 | "node_modules/noise-curve-ed": { | |
| 539 | "version": "2.1.0", | |
| 540 | "resolved": "https://registry.npmjs.org/noise-curve-ed/-/noise-curve-ed-2.1.0.tgz", | |
| 541 | "integrity": "sha512-zAzJx+VwZM3w6EA1hTmDhJfvAnCeBQn/1FAeZ0LtGxCcCtlAK/uJXQVF/eDVUOaAZ286lHlx77WJ+qj9SmsRRg==", | |
| 542 | "license": "ISC", | |
| 543 | "dependencies": { | |
| 544 | "b4a": "^1.1.0", | |
| 545 | "nanoassert": "^2.0.0", | |
| 546 | "sodium-universal": "^5.0.0" | |
| 547 | } | |
| 548 | }, | |
| 549 | "node_modules/noise-curve-ed/node_modules/sodium-native": { | |
| 550 | "version": "5.1.0", | |
| 551 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.1.0.tgz", | |
| 552 | "integrity": "sha512-3RxgyWyJlhTsABPnJVpCI5CoTDANZTqqFrEPqr+kjfnRaBihpVtMUE3yTF40ukdoB1APXeoBNKF3MzZAIHg39g==", | |
| 553 | "license": "MIT", | |
| 554 | "dependencies": { | |
| 555 | "bare-assert": "^1.2.0", | |
| 556 | "require-addon": "^1.1.0", | |
| 557 | "which-runtime": "^1.2.1" | |
| 558 | }, | |
| 559 | "engines": { | |
| 560 | "bare": ">=1.16.0" | |
| 561 | } | |
| 562 | }, | |
| 563 | "node_modules/noise-curve-ed/node_modules/sodium-universal": { | |
| 564 | "version": "5.0.1", | |
| 565 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz", | |
| 566 | "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==", | |
| 567 | "license": "MIT", | |
| 568 | "dependencies": { | |
| 569 | "sodium-native": "^5.0.1" | |
| 570 | }, | |
| 571 | "peerDependencies": { | |
| 572 | "sodium-javascript": "~0.8.0" | |
| 573 | }, | |
| 574 | "peerDependenciesMeta": { | |
| 575 | "sodium-javascript": { | |
| 576 | "optional": true | |
| 577 | } | |
| 578 | } | |
| 579 | }, | |
| 580 | "node_modules/noise-handshake": { | |
| 581 | "version": "4.2.0", | |
| 582 | "resolved": "https://registry.npmjs.org/noise-handshake/-/noise-handshake-4.2.0.tgz", | |
| 583 | "integrity": "sha512-9O/VTNX/E2/AToyMTTDU0J/4WhaXMTdqc2DHs9vf+snoZ0cenSBq0dNYTVV1snYYEkmo6QeRrYMxtqtoYnY+LA==", | |
| 584 | "license": "Apache-2.0", | |
| 585 | "dependencies": { | |
| 586 | "b4a": "^1.1.0", | |
| 587 | "nanoassert": "^2.0.0", | |
| 588 | "sodium-universal": "^5.0.0" | |
| 589 | } | |
| 590 | }, | |
| 591 | "node_modules/noise-handshake/node_modules/sodium-native": { | |
| 592 | "version": "5.1.0", | |
| 593 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.1.0.tgz", | |
| 594 | "integrity": "sha512-3RxgyWyJlhTsABPnJVpCI5CoTDANZTqqFrEPqr+kjfnRaBihpVtMUE3yTF40ukdoB1APXeoBNKF3MzZAIHg39g==", | |
| 595 | "license": "MIT", | |
| 596 | "dependencies": { | |
| 597 | "bare-assert": "^1.2.0", | |
| 598 | "require-addon": "^1.1.0", | |
| 599 | "which-runtime": "^1.2.1" | |
| 600 | }, | |
| 601 | "engines": { | |
| 602 | "bare": ">=1.16.0" | |
| 603 | } | |
| 604 | }, | |
| 605 | "node_modules/noise-handshake/node_modules/sodium-universal": { | |
| 606 | "version": "5.0.1", | |
| 607 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz", | |
| 608 | "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==", | |
| 609 | "license": "MIT", | |
| 610 | "dependencies": { | |
| 611 | "sodium-native": "^5.0.1" | |
| 612 | }, | |
| 613 | "peerDependencies": { | |
| 614 | "sodium-javascript": "~0.8.0" | |
| 615 | }, | |
| 616 | "peerDependenciesMeta": { | |
| 617 | "sodium-javascript": { | |
| 618 | "optional": true | |
| 619 | } | |
| 620 | } | |
| 621 | }, | |
| 622 | "node_modules/paparam": { | |
| 623 | "version": "1.10.1", | |
| 624 | "resolved": "https://registry.npmjs.org/paparam/-/paparam-1.10.1.tgz", | |
| 625 | "integrity": "sha512-viyQI64VIja0Za3njIzhoEP8ZVkgPowhZPuG0E96NwBfYJ6ZIyrrlhWGtFPkdN7eYLl2L8CTWL+wla0evm1KKQ==", | |
| 626 | "license": "Apache-2.0" | |
| 627 | }, | |
| 628 | "node_modules/protocol-buffers-encodings": { | |
| 629 | "version": "1.2.0", | |
| 630 | "resolved": "https://registry.npmjs.org/protocol-buffers-encodings/-/protocol-buffers-encodings-1.2.0.tgz", | |
| 631 | "integrity": "sha512-daeNPuKh1NlLD1uDfbLpD+xyUTc07nEtfHwmBZmt/vH0B7VOM+JOCOpDcx9ZRpqHjAiIkGqyTDi+wfGSl17R9w==", | |
| 632 | "license": "MIT", | |
| 633 | "dependencies": { | |
| 634 | "b4a": "^1.6.0", | |
| 635 | "signed-varint": "^2.0.1", | |
| 636 | "varint": "5.0.0" | |
| 637 | } | |
| 638 | }, | |
| 639 | "node_modules/protomux": { | |
| 640 | "version": "3.10.1", | |
| 641 | "resolved": "https://registry.npmjs.org/protomux/-/protomux-3.10.1.tgz", | |
| 642 | "integrity": "sha512-jgBqx8ZyaBWea/DFG4eOu1scOaeBwcnagiRC1XFVrjeGt7oAb0Pk5udPpBUpJ4DJBRjra50jD6YcZiQQTRqaaA==", | |
| 643 | "license": "MIT", | |
| 644 | "dependencies": { | |
| 645 | "b4a": "^1.3.1", | |
| 646 | "compact-encoding": "^2.5.1", | |
| 647 | "queue-tick": "^1.0.0", | |
| 648 | "safety-catch": "^1.0.1", | |
| 649 | "unslab": "^1.3.0" | |
| 650 | } | |
| 651 | }, | |
| 652 | "node_modules/queue-tick": { | |
| 653 | "version": "1.0.1", | |
| 654 | "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", | |
| 655 | "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", | |
| 656 | "license": "MIT" | |
| 657 | }, | |
| 658 | "node_modules/quickbit-native": { | |
| 659 | "version": "2.4.8", | |
| 660 | "resolved": "https://registry.npmjs.org/quickbit-native/-/quickbit-native-2.4.8.tgz", | |
| 661 | "integrity": "sha512-FcCcqI+nIAWGknqhtrYT5TSD7t/N+Xd8ctM+2PrIIBuwOi5hx0SxAvuPtzLIEMfT/2h9+fhBakUe2uALOHX6yw==", | |
| 662 | "license": "Apache-2.0", | |
| 663 | "optional": true, | |
| 664 | "dependencies": { | |
| 665 | "require-addon": "^1.1.0" | |
| 666 | } | |
| 667 | }, | |
| 668 | "node_modules/quickbit-universal": { | |
| 669 | "version": "2.2.0", | |
| 670 | "resolved": "https://registry.npmjs.org/quickbit-universal/-/quickbit-universal-2.2.0.tgz", | |
| 671 | "integrity": "sha512-w02i1R8n7+6pEKTud8DfF8zbFY9o7RtPlUc3jWbtCkDKvhbx/AvV7oNnz4/TcmsPGpSJS+fq5Ud6RH6+YPvSGg==", | |
| 672 | "license": "ISC", | |
| 673 | "dependencies": { | |
| 674 | "b4a": "^1.6.0", | |
| 675 | "simdle-universal": "^1.1.0" | |
| 676 | }, | |
| 677 | "optionalDependencies": { | |
| 678 | "quickbit-native": "^2.2.0" | |
| 679 | } | |
| 680 | }, | |
| 681 | "node_modules/rabin-native": { | |
| 682 | "version": "2.0.0", | |
| 683 | "resolved": "https://registry.npmjs.org/rabin-native/-/rabin-native-2.0.0.tgz", | |
| 684 | "integrity": "sha512-x1BlYdIWh+nk9G0scvxyscrYiDPJ7vQZepqqPlVUSInIko1Zxooi8cm6lzBghEGI9aN3DXiunC8FXLgy6kke3Q==", | |
| 685 | "license": "Apache-2.0", | |
| 686 | "dependencies": { | |
| 687 | "require-addon": "^1.1.0" | |
| 688 | } | |
| 689 | }, | |
| 690 | "node_modules/rabin-stream": { | |
| 691 | "version": "2.0.0", | |
| 692 | "resolved": "https://registry.npmjs.org/rabin-stream/-/rabin-stream-2.0.0.tgz", | |
| 693 | "integrity": "sha512-EzS2Ig/qsMBSk1PahC0O0IPdRZe3bspHHLWChhqW1feKz+J46tlVL3g4wWr4lrQdXGzABIMsOhvdT6bjXYMuUw==", | |
| 694 | "license": "Apache-2.0", | |
| 695 | "dependencies": { | |
| 696 | "rabin-native": "^2.0.0", | |
| 697 | "streamx": "^2.23.0" | |
| 698 | } | |
| 699 | }, | |
| 700 | "node_modules/rache": { | |
| 701 | "version": "1.0.0", | |
| 702 | "resolved": "https://registry.npmjs.org/rache/-/rache-1.0.0.tgz", | |
| 703 | "integrity": "sha512-e0k0g0w/8jOCB+7YqCIlOa+OJ38k0wrYS4x18pMSmqOvLKoyhmMhmQyCcvfY6VaP8D75cqkEnlakXs+RYYLqNg==", | |
| 704 | "license": "Apache-2.0" | |
| 705 | }, | |
| 706 | "node_modules/random-access-file": { | |
| 707 | "version": "4.1.2", | |
| 708 | "resolved": "https://registry.npmjs.org/random-access-file/-/random-access-file-4.1.2.tgz", | |
| 709 | "integrity": "sha512-GQM6R78DceZDcQod8KxlDFwXIiUvlvuy1EOzxTDsjuDjW5NlnlZi0MOk6iI4itAj/2vcvdqcEExYbVpC/dJcEw==", | |
| 710 | "license": "MIT", | |
| 711 | "dependencies": { | |
| 712 | "bare-fs": "^4.0.1", | |
| 713 | "bare-path": "^3.0.0", | |
| 714 | "random-access-storage": "^3.0.0" | |
| 715 | }, | |
| 716 | "optionalDependencies": { | |
| 717 | "fs-native-extensions": "^1.3.1" | |
| 718 | } | |
| 719 | }, | |
| 720 | "node_modules/random-access-storage": { | |
| 721 | "version": "3.0.2", | |
| 722 | "resolved": "https://registry.npmjs.org/random-access-storage/-/random-access-storage-3.0.2.tgz", | |
| 723 | "integrity": "sha512-Es9maUyWdJXWKckKy9s1+vT+DEgAt+PBb9lxPaake/0EDUsHehloKGv9v1zimS2V3gpFAcQXubvc1Rgci2sDPQ==", | |
| 724 | "license": "MIT", | |
| 725 | "dependencies": { | |
| 726 | "bare-events": "^2.2.0", | |
| 727 | "queue-tick": "^1.0.0" | |
| 728 | } | |
| 729 | }, | |
| 730 | "node_modules/random-array-iterator": { | |
| 731 | "version": "1.0.0", | |
| 732 | "resolved": "https://registry.npmjs.org/random-array-iterator/-/random-array-iterator-1.0.0.tgz", | |
| 733 | "integrity": "sha512-u7xCM93XqKEvPTP6xZp2ehttcAemKnh73oKNf1FvzuVCfpt6dILDt1Kxl1LeBjm2iNIeR49VGFhy4Iz3yOun+Q==", | |
| 734 | "license": "MIT" | |
| 735 | }, | |
| 736 | "node_modules/read-write-mutexify": { | |
| 737 | "version": "2.1.0", | |
| 738 | "resolved": "https://registry.npmjs.org/read-write-mutexify/-/read-write-mutexify-2.1.0.tgz", | |
| 739 | "integrity": "sha512-fDw/p5/acI1ytVY1UbxEDma/ej1yJH/n9NcjS9YNzcE6sPBPWdlru3ydRa/UBowUg4zqOvNMD5SOGYJrlQ6MzQ==", | |
| 740 | "license": "MIT" | |
| 741 | }, | |
| 742 | "node_modules/ready-resource": { | |
| 743 | "version": "1.2.0", | |
| 744 | "resolved": "https://registry.npmjs.org/ready-resource/-/ready-resource-1.2.0.tgz", | |
| 745 | "integrity": "sha512-nfcco/8iAFV0M+2PYnmIc+/xY0iRb35d42HFHQ7AfjulbGEAFa+XWpByfwSyeVeiBoMLLFVMv1HixxNCqzSQ1g==", | |
| 746 | "license": "MIT", | |
| 747 | "dependencies": { | |
| 748 | "bare-events": "^2.2.0" | |
| 749 | } | |
| 750 | }, | |
| 751 | "node_modules/refcounter": { | |
| 752 | "version": "1.0.0", | |
| 753 | "resolved": "https://registry.npmjs.org/refcounter/-/refcounter-1.0.0.tgz", | |
| 754 | "integrity": "sha512-1WosVzUy0kPUaPMEtlNDwm99UsteALIhXXR8rerELoa63WkYIXAl0hxgwPFrIYBRWZPGUyekQ04FRtPJ7dHk9w==", | |
| 755 | "license": "Apache-2.0" | |
| 756 | }, | |
| 757 | "node_modules/require-addon": { | |
| 758 | "version": "1.2.0", | |
| 759 | "resolved": "https://registry.npmjs.org/require-addon/-/require-addon-1.2.0.tgz", | |
| 760 | "integrity": "sha512-VNPDZlYgIYQwWp9jMTzljx+k0ZtatKlcvOhktZ/anNPI3dQ9NXk7cq2U4iJ1wd9IrytRnYhyEocFWbkdPb+MYA==", | |
| 761 | "license": "Apache-2.0", | |
| 762 | "dependencies": { | |
| 763 | "bare-addon-resolve": "^1.3.0" | |
| 764 | }, | |
| 765 | "engines": { | |
| 766 | "bare": ">=1.10.0" | |
| 767 | } | |
| 768 | }, | |
| 769 | "node_modules/resolve-reject-promise": { | |
| 770 | "version": "1.1.0", | |
| 771 | "resolved": "https://registry.npmjs.org/resolve-reject-promise/-/resolve-reject-promise-1.1.0.tgz", | |
| 772 | "integrity": "sha512-LWsTOA91AqzBTjSGgX79Tc130pwcBK6xjpJEO+qRT5IKZ6bGnHKcc8QL3upUBcWuU8OTIDzKK2VNSwmmlqvAVg==", | |
| 773 | "license": "MIT" | |
| 774 | }, | |
| 775 | "node_modules/rocksdb-native": { | |
| 776 | "version": "3.15.0", | |
| 777 | "resolved": "https://registry.npmjs.org/rocksdb-native/-/rocksdb-native-3.15.0.tgz", | |
| 778 | "integrity": "sha512-czYx0hOzKAy+HYmJROZm7thvJ1tHFWHNkGI3nomumXWzrJghJdrWo4MIhehGX4OTff0lnOCoB2zu6ekzIKY0rQ==", | |
| 779 | "license": "Apache-2.0", | |
| 780 | "dependencies": { | |
| 781 | "compact-encoding": "^2.15.0", | |
| 782 | "ready-resource": "^1.0.0", | |
| 783 | "refcounter": "^1.0.0", | |
| 784 | "require-addon": "^1.0.2", | |
| 785 | "resolve-reject-promise": "^1.1.0", | |
| 786 | "signal-promise": "^1.0.3", | |
| 787 | "streamx": "^2.16.1" | |
| 788 | }, | |
| 789 | "engines": { | |
| 790 | "bare": ">=1.16.0" | |
| 791 | } | |
| 792 | }, | |
| 793 | "node_modules/safety-catch": { | |
| 794 | "version": "1.0.3", | |
| 795 | "resolved": "https://registry.npmjs.org/safety-catch/-/safety-catch-1.0.3.tgz", | |
| 796 | "integrity": "sha512-Zq+J1TefpoEq/HTUabo0YXX5MNvttjWYODGohgPBO2jfko8Wqx3JYMgE823szDFVamdH5PlpByvfiWScTdSYDA==", | |
| 797 | "license": "MIT" | |
| 798 | }, | |
| 799 | "node_modules/same-data": { | |
| 800 | "version": "1.0.0", | |
| 801 | "resolved": "https://registry.npmjs.org/same-data/-/same-data-1.0.0.tgz", | |
| 802 | "integrity": "sha512-Eqn7N2yV+aKMlUHTRqUwYG1Iv0cJqjlvLKj/GoP5PozJn361QaOYX14+v87r7NqQUZC22noP/LfLrSQiPwAygw==", | |
| 803 | "license": "MIT" | |
| 804 | }, | |
| 805 | "node_modules/signal-promise": { | |
| 806 | "version": "1.0.3", | |
| 807 | "resolved": "https://registry.npmjs.org/signal-promise/-/signal-promise-1.0.3.tgz", | |
| 808 | "integrity": "sha512-WBgv0UnIq2C+Aeh0/n+IRpP6967eIx9WpynTUoiW3isPpfe1zu2LJzyfXdo9Tgef8yR/sGjcMvoUXD7EYdiz+g==", | |
| 809 | "license": "MIT" | |
| 810 | }, | |
| 811 | "node_modules/signed-varint": { | |
| 812 | "version": "2.0.1", | |
| 813 | "resolved": "https://registry.npmjs.org/signed-varint/-/signed-varint-2.0.1.tgz", | |
| 814 | "integrity": "sha512-abgDPg1106vuZZOvw7cFwdCABddfJRz5akcCcchzTbhyhYnsG31y4AlZEgp315T7W3nQq5P4xeOm186ZiPVFzw==", | |
| 815 | "license": "MIT", | |
| 816 | "dependencies": { | |
| 817 | "varint": "~5.0.0" | |
| 818 | } | |
| 819 | }, | |
| 820 | "node_modules/simdle-native": { | |
| 821 | "version": "1.3.9", | |
| 822 | "resolved": "https://registry.npmjs.org/simdle-native/-/simdle-native-1.3.9.tgz", | |
| 823 | "integrity": "sha512-Isc8sP4OiiIU0mpslD4GHEnR0VQWvR/54WN7YtwEDkNdTJVWtpmvsSvsgRlw5BNGxdYXlVRegdnrSu10H/PhvA==", | |
| 824 | "license": "Apache-2.0", | |
| 825 | "optional": true, | |
| 826 | "dependencies": { | |
| 827 | "b4a": "^1.6.0", | |
| 828 | "require-addon": "^1.1.0" | |
| 829 | } | |
| 830 | }, | |
| 831 | "node_modules/simdle-universal": { | |
| 832 | "version": "1.1.2", | |
| 833 | "resolved": "https://registry.npmjs.org/simdle-universal/-/simdle-universal-1.1.2.tgz", | |
| 834 | "integrity": "sha512-3n3w1bs+uwgHKQjt6arez83EywNlhZzYvNOhvAASTl/8KqNIcqr6aHyGt3JRlfuUC7iB0tomJRPlJ2cRGIpBzA==", | |
| 835 | "license": "ISC", | |
| 836 | "dependencies": { | |
| 837 | "b4a": "^1.6.0" | |
| 838 | }, | |
| 839 | "optionalDependencies": { | |
| 840 | "simdle-native": "^1.1.1" | |
| 841 | } | |
| 842 | }, | |
| 843 | "node_modules/sodium-native": { | |
| 844 | "version": "4.3.3", | |
| 845 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-4.3.3.tgz", | |
| 846 | "integrity": "sha512-OnxSlN3uyY8D0EsLHpmm2HOFmKddQVvEMmsakCrXUzSd8kjjbzL413t4ZNF3n0UxSwNgwTyUvkmZHTfuCeiYSw==", | |
| 847 | "license": "MIT", | |
| 848 | "dependencies": { | |
| 849 | "require-addon": "^1.1.0" | |
| 850 | } | |
| 851 | }, | |
| 852 | "node_modules/sodium-secretstream": { | |
| 853 | "version": "1.2.0", | |
| 854 | "resolved": "https://registry.npmjs.org/sodium-secretstream/-/sodium-secretstream-1.2.0.tgz", | |
| 855 | "integrity": "sha512-q/DbraNFXm1KfCiiZvapmz5UC3OlpirYFIvBK2MhGaOFSb3gRyk8OXTi17UI9SGfshQNCpsVvlopogbzZNyW6Q==", | |
| 856 | "license": "MIT", | |
| 857 | "dependencies": { | |
| 858 | "b4a": "^1.1.1", | |
| 859 | "sodium-universal": "^5.0.0" | |
| 860 | } | |
| 861 | }, | |
| 862 | "node_modules/sodium-secretstream/node_modules/sodium-native": { | |
| 863 | "version": "5.1.0", | |
| 864 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.1.0.tgz", | |
| 865 | "integrity": "sha512-3RxgyWyJlhTsABPnJVpCI5CoTDANZTqqFrEPqr+kjfnRaBihpVtMUE3yTF40ukdoB1APXeoBNKF3MzZAIHg39g==", | |
| 866 | "license": "MIT", | |
| 867 | "dependencies": { | |
| 868 | "bare-assert": "^1.2.0", | |
| 869 | "require-addon": "^1.1.0", | |
| 870 | "which-runtime": "^1.2.1" | |
| 871 | }, | |
| 872 | "engines": { | |
| 873 | "bare": ">=1.16.0" | |
| 874 | } | |
| 875 | }, | |
| 876 | "node_modules/sodium-secretstream/node_modules/sodium-universal": { | |
| 877 | "version": "5.0.1", | |
| 878 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz", | |
| 879 | "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==", | |
| 880 | "license": "MIT", | |
| 881 | "dependencies": { | |
| 882 | "sodium-native": "^5.0.1" | |
| 883 | }, | |
| 884 | "peerDependencies": { | |
| 885 | "sodium-javascript": "~0.8.0" | |
| 886 | }, | |
| 887 | "peerDependenciesMeta": { | |
| 888 | "sodium-javascript": { | |
| 889 | "optional": true | |
| 890 | } | |
| 891 | } | |
| 892 | }, | |
| 893 | "node_modules/sodium-universal": { | |
| 894 | "version": "4.0.1", | |
| 895 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-4.0.1.tgz", | |
| 896 | "integrity": "sha512-sNp13PrxYLaUFHTGoDKkSDFvoEu51bfzE12RwGlqU1fcrkpAOK0NvizaJzOWV0Omtk9me2+Pnbjcf/l0efxuGQ==", | |
| 897 | "license": "MIT", | |
| 898 | "dependencies": { | |
| 899 | "sodium-native": "^4.0.0" | |
| 900 | }, | |
| 901 | "peerDependencies": { | |
| 902 | "sodium-javascript": "~0.8.0" | |
| 903 | }, | |
| 904 | "peerDependenciesMeta": { | |
| 905 | "sodium-javascript": { | |
| 906 | "optional": true | |
| 907 | } | |
| 908 | } | |
| 909 | }, | |
| 910 | "node_modules/speedometer": { | |
| 911 | "version": "1.1.0", | |
| 912 | "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-1.1.0.tgz", | |
| 913 | "integrity": "sha512-z/wAiTESw2XVPssY2XRcme4niTc4S5FkkJ4gknudtVoc33Zil8TdTxHy5torRcgqMqksJV2Yz8HQcvtbsnw0mQ==", | |
| 914 | "license": "MIT" | |
| 915 | }, | |
| 916 | "node_modules/streamx": { | |
| 917 | "version": "2.25.0", | |
| 918 | "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.25.0.tgz", | |
| 919 | "integrity": "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==", | |
| 920 | "license": "MIT", | |
| 921 | "dependencies": { | |
| 922 | "events-universal": "^1.0.0", | |
| 923 | "fast-fifo": "^1.3.2", | |
| 924 | "text-decoder": "^1.1.0" | |
| 925 | } | |
| 926 | }, | |
| 927 | "node_modules/sub-encoder": { | |
| 928 | "version": "2.1.3", | |
| 929 | "resolved": "https://registry.npmjs.org/sub-encoder/-/sub-encoder-2.1.3.tgz", | |
| 930 | "integrity": "sha512-Xxx04ygZo/1J3yHvaSA6VhDmiSaBQkw/PmO3YnnYFXle+tfOGToC6FcDpIfMztWZXJzuKG14b/57HMkiL58C6A==", | |
| 931 | "license": "Apache-2.0", | |
| 932 | "dependencies": { | |
| 933 | "b4a": "^1.6.0", | |
| 934 | "codecs": "^3.1.0" | |
| 935 | } | |
| 936 | }, | |
| 937 | "node_modules/teex": { | |
| 938 | "version": "1.0.1", | |
| 939 | "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", | |
| 940 | "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", | |
| 941 | "license": "MIT", | |
| 942 | "dependencies": { | |
| 943 | "streamx": "^2.12.5" | |
| 944 | } | |
| 945 | }, | |
| 946 | "node_modules/text-decoder": { | |
| 947 | "version": "1.2.7", | |
| 948 | "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", | |
| 949 | "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", | |
| 950 | "license": "Apache-2.0", | |
| 951 | "dependencies": { | |
| 952 | "b4a": "^1.6.4" | |
| 953 | } | |
| 954 | }, | |
| 955 | "node_modules/timeout-refresh": { | |
| 956 | "version": "2.0.1", | |
| 957 | "resolved": "https://registry.npmjs.org/timeout-refresh/-/timeout-refresh-2.0.1.tgz", | |
| 958 | "integrity": "sha512-SVqEcMZBsZF9mA78rjzCrYrUs37LMJk3ShZ851ygZYW1cMeIjs9mL57KO6Iv5mmjSQnOe/29/VAfGXo+oRCiVw==", | |
| 959 | "license": "MIT" | |
| 960 | }, | |
| 961 | "node_modules/unix-path-resolve": { | |
| 962 | "version": "1.0.2", | |
| 963 | "resolved": "https://registry.npmjs.org/unix-path-resolve/-/unix-path-resolve-1.0.2.tgz", | |
| 964 | "integrity": "sha512-kG4g5nobBBaMnH2XbrS4sLUXEpx4nY2J3C6KAlAUcnahG2HChxSPVKWYrqEq76iTo+cyMkLUjqxGaQR2tz097Q==", | |
| 965 | "license": "MIT" | |
| 966 | }, | |
| 967 | "node_modules/unslab": { | |
| 968 | "version": "1.3.0", | |
| 969 | "resolved": "https://registry.npmjs.org/unslab/-/unslab-1.3.0.tgz", | |
| 970 | "integrity": "sha512-YATkfKAFj47kTzmiQrWXMyRvaVrHsW6MEALa4bm+FhiA2YG4oira+Z3DXN6LrYOYn2Y8eO94Lwl9DOHjs1FpoQ==", | |
| 971 | "license": "Apache-2.0", | |
| 972 | "dependencies": { | |
| 973 | "b4a": "^1.6.6" | |
| 974 | } | |
| 975 | }, | |
| 976 | "node_modules/varint": { | |
| 977 | "version": "5.0.0", | |
| 978 | "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz", | |
| 979 | "integrity": "sha512-gC13b/bWrqQoKY2EmROCZ+AR0jitc6DnDGaQ6Ls9QpKmuSgJB1eQ7H3KETtQm7qSdMWMKCmsshyCmUwMLh3OAA==", | |
| 980 | "license": "MIT" | |
| 981 | }, | |
| 982 | "node_modules/which-runtime": { | |
| 983 | "version": "1.3.2", | |
| 984 | "resolved": "https://registry.npmjs.org/which-runtime/-/which-runtime-1.3.2.tgz", | |
| 985 | "integrity": "sha512-5kwCfWml7+b2NO7KrLMhYihjRx0teKkd3yGp1Xk5Vaf2JGdSh+rgVhEALAD9c/59dP+YwJHXoEO7e8QPy7gOkw==", | |
| 986 | "license": "Apache-2.0" | |
| 987 | }, | |
| 988 | "node_modules/xache": { | |
| 989 | "version": "1.2.1", | |
| 990 | "resolved": "https://registry.npmjs.org/xache/-/xache-1.2.1.tgz", | |
| 991 | "integrity": "sha512-igRS6jPreJ54ABdzhh4mCDXcz+XMaWO2q1ABRV2yWYuk29jlp8VT7UBdCqNkX7rpYBbXsebVVKkwIuYZjyZNqA==", | |
| 992 | "license": "MIT" | |
| 993 | }, | |
| 994 | "node_modules/z32": { | |
| 995 | "version": "1.1.0", | |
| 996 | "resolved": "https://registry.npmjs.org/z32/-/z32-1.1.0.tgz", | |
| 997 | "integrity": "sha512-1WUHy+VS6d0HPNspDxvLssBbeQjXMjSnpv0vH82vRAUfg847NmX3OXozp/hRP5jPhxBbrVzrgvAt+UsGNzRFQQ==", | |
| 998 | "license": "MIT", | |
| 999 | "dependencies": { | |
| 1000 | "b4a": "^1.5.3" | |
| 1001 | } | |
| 1002 | } | |
| 1003 | } | |
| 1004 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,160 @@ |
| 1 | # @hyperswarm/secret-stream | |
| 2 | ||
| 3 | ### [See the full API docs at docs.holepunch.to](https://docs.holepunch.to/building-blocks/hyperswarm#secretstream) | |
| 4 | ||
| 5 | Secret stream backed by Noise and libsodium's secretstream | |
| 6 | ||
| 7 | ``` | |
| 8 | npm install @hyperswarm/secret-stream | |
| 9 | ``` | |
| 10 | ||
| 11 | ## Usage | |
| 12 | ||
| 13 | You can either make a secret stream from an existing transport stream. | |
| 14 | ||
| 15 | ```js | |
| 16 | const SecretStream = require('@hyperswarm/secret-stream') | |
| 17 | ||
| 18 | const a = new SecretStream(true, tcpClientStream) | |
| 19 | const b = new SecretStream(false, tcpServerStream) | |
| 20 | ||
| 21 | // pipe the underlying rawstreams together | |
| 22 | ||
| 23 | a.write(Buffer.from('hello encrypted!')) | |
| 24 | ||
| 25 | b.on('data', function (data) { | |
| 26 | console.log(data) // <Buffer hello encrypted!> | |
| 27 | }) | |
| 28 | ``` | |
| 29 | ||
| 30 | Or by making your own pipeline | |
| 31 | ||
| 32 | ```js | |
| 33 | const a = new SecretStream(true) | |
| 34 | const b = new SecretStream(false) | |
| 35 | ||
| 36 | // pipe the underlying rawstreams together | |
| 37 | a.rawStream.pipe(b.rawStream).pipe(a.rawStream) | |
| 38 | ||
| 39 | a.write(Buffer.from('hello encrypted!')) | |
| 40 | ||
| 41 | b.on('data', function (data) { | |
| 42 | console.log(data) // <Buffer hello encrypted!> | |
| 43 | }) | |
| 44 | ``` | |
| 45 | ||
| 46 | ## API | |
| 47 | ||
| 48 | #### `const s = new SecretStream(isInitiator, [rawStream], [options])` | |
| 49 | ||
| 50 | Make a new stream. `isInitiator` is a boolean indication whether you are the client or the server. | |
| 51 | `rawStream` can be set to an underlying transport stream you want to run the noise stream over. | |
| 52 | ||
| 53 | Options include: | |
| 54 | ||
| 55 | ```js | |
| 56 | { | |
| 57 | pattern: 'XX', // which noise pattern to use | |
| 58 | remotePublicKey, // set if your handshake requires it | |
| 59 | keyPair: { publicKey, secretKey }, | |
| 60 | handshake: { // if you want to use an handshake performed elsewhere pass it here | |
| 61 | tx, | |
| 62 | rx, | |
| 63 | hash, | |
| 64 | publicKey, | |
| 65 | remotePublicKey | |
| 66 | }, | |
| 67 | enableSend: true // (advanced) set false to disable the send API | |
| 68 | } | |
| 69 | ``` | |
| 70 | ||
| 71 | The SecretStream returned is a Duplex stream that you use as as normal stream, to write/read data from, | |
| 72 | except it's payloads are encrypted using the libsodium secretstream. | |
| 73 | ||
| 74 | Note that this uses ed25519 for the handshakes per default. | |
| 75 | ||
| 76 | If need to load the key pair asynchronously, then secret-stream also supports passing in a promise | |
| 77 | instead of the keypair that later resolves to `{ publicKey, secretKey }`. The stream lifecycle will wait | |
| 78 | for the resolution and auto destroy the stream if the promise errors. | |
| 79 | ||
| 80 | #### `s.start(rawStream, [options])` | |
| 81 | ||
| 82 | Start a SecretStream from a rawStream asynchrously. | |
| 83 | ||
| 84 | ```js | |
| 85 | const s = new SecretStream({ | |
| 86 | autoStart: false // call start manually | |
| 87 | }) | |
| 88 | ||
| 89 | // ... do async stuff or destroy the stream | |
| 90 | ||
| 91 | s.start(rawStream, { | |
| 92 | ... options from above | |
| 93 | }) | |
| 94 | ``` | |
| 95 | ||
| 96 | #### `s.setTimeout(ms)` | |
| 97 | ||
| 98 | Set the stream timeout. If no data is received within a `ms` window, | |
| 99 | the stream is auto destroyed. | |
| 100 | ||
| 101 | #### `s.setKeepAlive(ms)` | |
| 102 | ||
| 103 | Send a heartbeat (empty message) every time the socket is idle for `ms` milliseconds. **Note:** If one side calls `s.setKeepAlive()` and the other does not, then the empty messages will be passed through to the piped stream. | |
| 104 | ||
| 105 | #### `s.publicKey` | |
| 106 | ||
| 107 | Get the local public key. | |
| 108 | ||
| 109 | #### `s.remotePublicKey` | |
| 110 | ||
| 111 | Get the remote's public key. | |
| 112 | Populated after `open` is emitted. | |
| 113 | ||
| 114 | #### `s.handshakeHash` | |
| 115 | ||
| 116 | Get the unique hash of this handshake. | |
| 117 | Populated after `open` is emitted. | |
| 118 | ||
| 119 | #### `s.keepAlive` | |
| 120 | ||
| 121 | Get the interval (in milliseconds) at which keep-alive messages are sent (0 means none are sent). | |
| 122 | ||
| 123 | #### `s.sendKeepAlive()` | |
| 124 | ||
| 125 | A convenience method that sends an empty message. | |
| 126 | ||
| 127 | #### `s.rawBytesWritten` | |
| 128 | ||
| 129 | The number of bytes (measured after encryption) written. | |
| 130 | ||
| 131 | #### `s.rawBytesRead` | |
| 132 | ||
| 133 | The number of bytes (measured before decryption) received. | |
| 134 | ||
| 135 | #### `s.on('connect', onconnect)` | |
| 136 | ||
| 137 | Emitted when the handshake is fully done. | |
| 138 | It is safe to write to the stream immediately though, as data is buffered | |
| 139 | internally before the handshake has been completed. | |
| 140 | ||
| 141 | #### `await s.send(buffer)` | |
| 142 | ||
| 143 | Sends an encrypted unordered message, see [udx-native](https://github.com/holepunchto/udx-native/tree/main?tab=readme-ov-file#await-streamsendbuffer) for details. | |
| 144 | This method with silently fail if called before handshake is complete or if the underlying rawStream is not an UDX-stream (not capable of UDP). | |
| 145 | ||
| 146 | #### `s.trySend(buffer)` | |
| 147 | ||
| 148 | Same as `send(buffer)` but does not return a promise. | |
| 149 | ||
| 150 | #### `s.on('message', onmessage)` | |
| 151 | ||
| 152 | Emmitted when an unordered message is received | |
| 153 | ||
| 154 | #### `keyPair = SecretStream.keyPair([seed])` | |
| 155 | ||
| 156 | Generate a ed25519 key pair. | |
| 157 | ||
| 158 | ## License | |
| 159 | ||
| 160 | Apache-2.0 |
| … | … | @@ -0,0 +1,645 @@ |
| 1 | const { Pull, Push, HEADERBYTES, KEYBYTES, ABYTES } = require('sodium-secretstream') | |
| 2 | const sodium = require('sodium-universal') | |
| 3 | const crypto = require('hypercore-crypto') | |
| 4 | const { Duplex, Writable, getStreamError } = require('streamx') | |
| 5 | const b4a = require('b4a') | |
| 6 | const Timeout = require('timeout-refresh') | |
| 7 | const unslab = require('unslab') | |
| 8 | const Bridge = require('./lib/bridge') | |
| 9 | const Handshake = require('./lib/handshake') | |
| 10 | ||
| 11 | const IDHEADERBYTES = HEADERBYTES + 32 | |
| 12 | const [NS_INITIATOR, NS_RESPONDER, NS_SEND] = crypto.namespace('hyperswarm/secret-stream', 3) | |
| 13 | const MAX_ATOMIC_WRITE = 256 * 256 * 256 - 1 | |
| 14 | ||
| 15 | module.exports = class NoiseSecretStream extends Duplex { | |
| 16 | constructor(isInitiator, rawStream, opts = {}) { | |
| 17 | super({ mapWritable: toBuffer }) | |
| 18 | ||
| 19 | if (typeof isInitiator !== 'boolean') { | |
| 20 | throw new Error('isInitiator should be a boolean') | |
| 21 | } | |
| 22 | ||
| 23 | this.noiseStream = this | |
| 24 | this.isInitiator = isInitiator | |
| 25 | this.rawStream = null | |
| 26 | ||
| 27 | this.publicKey = opts.publicKey || null | |
| 28 | this.remotePublicKey = opts.remotePublicKey || null | |
| 29 | this.handshakeHash = null | |
| 30 | this.connected = false | |
| 31 | this.keepAlive = opts.keepAlive || 0 | |
| 32 | this.timeout = 0 | |
| 33 | this.enableSend = opts.enableSend !== false | |
| 34 | ||
| 35 | // pointer for upstream to set data here if they want | |
| 36 | this.userData = null | |
| 37 | ||
| 38 | let openedDone = null | |
| 39 | this.opened = new Promise((resolve) => { | |
| 40 | openedDone = resolve | |
| 41 | }) | |
| 42 | ||
| 43 | this.rawBytesWritten = 0 | |
| 44 | this.rawBytesRead = 0 | |
| 45 | ||
| 46 | // metadata used by 'hyperdht' | |
| 47 | this.relay = null | |
| 48 | this.puncher = null | |
| 49 | ||
| 50 | // unwrapped raw stream | |
| 51 | this._rawStream = null | |
| 52 | ||
| 53 | // handshake state | |
| 54 | this._handshake = null | |
| 55 | this._handshakePattern = opts.pattern || null | |
| 56 | this._handshakeDone = null | |
| 57 | ||
| 58 | // message parsing state | |
| 59 | this._state = 0 | |
| 60 | this._len = 0 | |
| 61 | this._tmp = 1 | |
| 62 | this._message = null | |
| 63 | ||
| 64 | this._openedDone = openedDone | |
| 65 | this._startDone = null | |
| 66 | this._drainDone = null | |
| 67 | this._outgoingPlain = null | |
| 68 | this._outgoingWrapped = null | |
| 69 | this._utp = null | |
| 70 | this._setup = true | |
| 71 | this._ended = 2 | |
| 72 | this._encrypt = null | |
| 73 | this._decrypt = null | |
| 74 | this._timeoutTimer = null | |
| 75 | this._keepAliveTimer = null | |
| 76 | this._sendState = null | |
| 77 | ||
| 78 | if (opts.autoStart !== false) this.start(rawStream, opts) | |
| 79 | ||
| 80 | // wiggle it to trigger open immediately (TODO add streamx option for this) | |
| 81 | this.resume() | |
| 82 | this.pause() | |
| 83 | } | |
| 84 | ||
| 85 | static keyPair(seed) { | |
| 86 | return Handshake.keyPair(seed) | |
| 87 | } | |
| 88 | ||
| 89 | static id(handshakeHash, isInitiator, id) { | |
| 90 | return streamId(handshakeHash, isInitiator, id) | |
| 91 | } | |
| 92 | ||
| 93 | setTimeout(ms) { | |
| 94 | if (!ms) ms = 0 | |
| 95 | ||
| 96 | this._clearTimeout() | |
| 97 | this.timeout = ms | |
| 98 | ||
| 99 | if (!ms || this.rawStream === null) return | |
| 100 | ||
| 101 | this._timeoutTimer = Timeout.once(ms, destroyTimeout, this) | |
| 102 | this._timeoutTimer.unref() | |
| 103 | } | |
| 104 | ||
| 105 | setKeepAlive(ms) { | |
| 106 | if (!ms) ms = 0 | |
| 107 | ||
| 108 | this._clearKeepAlive() | |
| 109 | ||
| 110 | this.keepAlive = ms | |
| 111 | ||
| 112 | if (!ms || this.rawStream === null) return | |
| 113 | ||
| 114 | this._keepAliveTimer = Timeout.on(ms, sendKeepAlive, this) | |
| 115 | this._keepAliveTimer.unref() | |
| 116 | } | |
| 117 | ||
| 118 | sendKeepAlive() { | |
| 119 | const empty = this.alloc(0) | |
| 120 | this.write(empty) | |
| 121 | } | |
| 122 | ||
| 123 | start(rawStream, opts = {}) { | |
| 124 | if (rawStream) { | |
| 125 | this.rawStream = rawStream | |
| 126 | this._rawStream = rawStream | |
| 127 | if (typeof this.rawStream.setContentSize === 'function') { | |
| 128 | this._utp = rawStream | |
| 129 | } | |
| 130 | } else { | |
| 131 | this.rawStream = new Bridge(this) | |
| 132 | this._rawStream = this.rawStream.reverse | |
| 133 | } | |
| 134 | ||
| 135 | this.rawStream.on('error', this._onrawerror.bind(this)) | |
| 136 | this.rawStream.on('close', this._onrawclose.bind(this)) | |
| 137 | ||
| 138 | this._startHandshake(opts.handshake, opts.keyPair || null) | |
| 139 | this._continueOpen(null) | |
| 140 | ||
| 141 | if (this.destroying) return | |
| 142 | ||
| 143 | if (opts.data) this._onrawdata(opts.data) | |
| 144 | if (opts.ended) this._onrawend() | |
| 145 | ||
| 146 | if (this.keepAlive > 0 && this._keepAliveTimer === null) { | |
| 147 | this.setKeepAlive(this.keepAlive) | |
| 148 | } | |
| 149 | ||
| 150 | if (this.timeout > 0 && this._timeoutTimer === null) { | |
| 151 | this.setTimeout(this.timeout) | |
| 152 | } | |
| 153 | } | |
| 154 | ||
| 155 | async flush() { | |
| 156 | if ((await this.opened) === false) return false | |
| 157 | if ((await Writable.drained(this)) === false) return false | |
| 158 | if (this.destroying) return false | |
| 159 | ||
| 160 | if (this.rawStream !== null && this.rawStream.flush) { | |
| 161 | return await this.rawStream.flush() | |
| 162 | } | |
| 163 | ||
| 164 | return true | |
| 165 | } | |
| 166 | ||
| 167 | _continueOpen(err) { | |
| 168 | if (err) this.destroy(err) | |
| 169 | if (this._startDone === null) return | |
| 170 | const done = this._startDone | |
| 171 | this._startDone = null | |
| 172 | this._open(done) | |
| 173 | } | |
| 174 | ||
| 175 | _onkeypairpromise(p) { | |
| 176 | const self = this | |
| 177 | const cont = this._continueOpen.bind(this) | |
| 178 | ||
| 179 | p.then(onkeypair, cont) | |
| 180 | ||
| 181 | function onkeypair(kp) { | |
| 182 | self._onkeypair(kp) | |
| 183 | cont(null) | |
| 184 | } | |
| 185 | } | |
| 186 | ||
| 187 | _onkeypair(keyPair) { | |
| 188 | const pattern = this._handshakePattern || 'XX' | |
| 189 | const remotePublicKey = this.remotePublicKey | |
| 190 | ||
| 191 | this._handshake = new Handshake(this.isInitiator, keyPair, remotePublicKey, pattern) | |
| 192 | this.publicKey = this._handshake.keyPair.publicKey | |
| 193 | } | |
| 194 | ||
| 195 | _startHandshake(handshake, keyPair) { | |
| 196 | if (handshake) { | |
| 197 | const { tx, rx, hash, publicKey, remotePublicKey } = handshake | |
| 198 | this._setupSecretStream(tx, rx, hash, publicKey, remotePublicKey) | |
| 199 | return | |
| 200 | } | |
| 201 | ||
| 202 | if (!keyPair) keyPair = Handshake.keyPair() | |
| 203 | ||
| 204 | if (typeof keyPair.then === 'function') { | |
| 205 | this._onkeypairpromise(keyPair) | |
| 206 | } else { | |
| 207 | this._onkeypair(keyPair) | |
| 208 | } | |
| 209 | } | |
| 210 | ||
| 211 | _onrawerror(err) { | |
| 212 | this.destroy(err) | |
| 213 | } | |
| 214 | ||
| 215 | _onrawclose() { | |
| 216 | if (this._ended !== 0) this.destroy() | |
| 217 | } | |
| 218 | ||
| 219 | _onrawdata(data) { | |
| 220 | let offset = 0 | |
| 221 | ||
| 222 | if (this._timeoutTimer !== null) { | |
| 223 | this._timeoutTimer.refresh() | |
| 224 | } | |
| 225 | ||
| 226 | do { | |
| 227 | switch (this._state) { | |
| 228 | case 0: { | |
| 229 | while (this._tmp !== 0x1000000 && offset < data.byteLength) { | |
| 230 | const v = data[offset++] | |
| 231 | this._len += this._tmp * v | |
| 232 | this._tmp *= 256 | |
| 233 | } | |
| 234 | ||
| 235 | if (this._tmp === 0x1000000) { | |
| 236 | this._tmp = 0 | |
| 237 | this._state = 1 | |
| 238 | const unprocessed = data.byteLength - offset | |
| 239 | if (unprocessed < this._len && this._utp !== null) | |
| 240 | this._utp.setContentSize(this._len - unprocessed) | |
| 241 | } | |
| 242 | ||
| 243 | break | |
| 244 | } | |
| 245 | ||
| 246 | case 1: { | |
| 247 | const missing = this._len - this._tmp | |
| 248 | const end = missing + offset | |
| 249 | ||
| 250 | if (this._message === null && end <= data.byteLength) { | |
| 251 | this._message = data.subarray(offset, end) | |
| 252 | offset += missing | |
| 253 | this._incoming() | |
| 254 | break | |
| 255 | } | |
| 256 | ||
| 257 | const unprocessed = data.byteLength - offset | |
| 258 | ||
| 259 | if (this._message === null) { | |
| 260 | this._message = b4a.allocUnsafe(this._len) | |
| 261 | } | |
| 262 | ||
| 263 | b4a.copy(data, this._message, this._tmp, offset) | |
| 264 | this._tmp += unprocessed | |
| 265 | ||
| 266 | if (end <= data.byteLength) { | |
| 267 | offset += missing | |
| 268 | this._incoming() | |
| 269 | } else { | |
| 270 | offset += unprocessed | |
| 271 | } | |
| 272 | ||
| 273 | break | |
| 274 | } | |
| 275 | } | |
| 276 | } while (offset < data.byteLength && !this.destroying) | |
| 277 | } | |
| 278 | ||
| 279 | _onrawend() { | |
| 280 | this._ended-- | |
| 281 | this.push(null) | |
| 282 | } | |
| 283 | ||
| 284 | _onrawdrain() { | |
| 285 | const drain = this._drainDone | |
| 286 | if (drain === null) return | |
| 287 | this._drainDone = null | |
| 288 | drain() | |
| 289 | } | |
| 290 | ||
| 291 | _read(cb) { | |
| 292 | this.rawStream.resume() | |
| 293 | cb(null) | |
| 294 | } | |
| 295 | ||
| 296 | _incoming() { | |
| 297 | const message = this._message | |
| 298 | ||
| 299 | this._state = 0 | |
| 300 | this._len = 0 | |
| 301 | this._tmp = 1 | |
| 302 | this._message = null | |
| 303 | ||
| 304 | if (this._setup === true) { | |
| 305 | if (this._handshake) { | |
| 306 | this._onhandshakert(this._handshake.recv(message)) | |
| 307 | } else { | |
| 308 | if (message.byteLength !== IDHEADERBYTES) { | |
| 309 | this.destroy(new Error('Invalid header message received')) | |
| 310 | return | |
| 311 | } | |
| 312 | ||
| 313 | const remoteId = message.subarray(0, 32) | |
| 314 | const expectedId = streamId(this.handshakeHash, !this.isInitiator) | |
| 315 | const header = message.subarray(32) | |
| 316 | ||
| 317 | if (!b4a.equals(expectedId, remoteId)) { | |
| 318 | this.destroy(new Error('Invalid header received')) | |
| 319 | return | |
| 320 | } | |
| 321 | ||
| 322 | this._decrypt.init(header) | |
| 323 | this._setup = false // setup is now done | |
| 324 | } | |
| 325 | return | |
| 326 | } | |
| 327 | ||
| 328 | if (message.byteLength < ABYTES) { | |
| 329 | this.destroy(new Error('Invalid message received')) | |
| 330 | return | |
| 331 | } | |
| 332 | ||
| 333 | this.rawBytesRead += message.byteLength | |
| 334 | ||
| 335 | const plain = message.subarray(1, message.byteLength - ABYTES + 1) | |
| 336 | ||
| 337 | try { | |
| 338 | this._decrypt.next(message, plain) | |
| 339 | } catch (err) { | |
| 340 | this.destroy(err) | |
| 341 | return | |
| 342 | } | |
| 343 | ||
| 344 | // If keep alive is selective, eat the empty buffers (ie assume the other side has it enabled also) | |
| 345 | if (plain.byteLength === 0 && this.keepAlive !== 0) return | |
| 346 | ||
| 347 | if (this.push(plain) === false) { | |
| 348 | this.rawStream.pause() | |
| 349 | } | |
| 350 | } | |
| 351 | ||
| 352 | _onhandshakert(h) { | |
| 353 | if (this._handshakeDone === null) return | |
| 354 | ||
| 355 | if (h !== null) { | |
| 356 | if (h.data) this._rawStream.write(h.data) | |
| 357 | if (!h.tx) return | |
| 358 | } | |
| 359 | ||
| 360 | const done = this._handshakeDone | |
| 361 | const publicKey = this._handshake.keyPair.publicKey | |
| 362 | ||
| 363 | this._handshakeDone = null | |
| 364 | this._handshake = null | |
| 365 | ||
| 366 | if (h === null) return done(new Error('Noise handshake failed')) | |
| 367 | ||
| 368 | this._setupSecretStream(h.tx, h.rx, h.hash, publicKey, h.remotePublicKey) | |
| 369 | this._resolveOpened(true) | |
| 370 | done(null) | |
| 371 | } | |
| 372 | ||
| 373 | _setupSecretStream(tx, rx, handshakeHash, publicKey, remotePublicKey) { | |
| 374 | const buf = b4a.allocUnsafeSlow(3 + IDHEADERBYTES) | |
| 375 | writeUint24le(IDHEADERBYTES, buf) | |
| 376 | ||
| 377 | this._encrypt = new Push(unslab(tx.subarray(0, KEYBYTES)), undefined, buf.subarray(3 + 32)) | |
| 378 | this._decrypt = new Pull(unslab(rx.subarray(0, KEYBYTES))) | |
| 379 | ||
| 380 | this.publicKey = publicKey | |
| 381 | this.remotePublicKey = remotePublicKey | |
| 382 | this.handshakeHash = handshakeHash | |
| 383 | ||
| 384 | const id = buf.subarray(3, 3 + 32) | |
| 385 | streamId(handshakeHash, this.isInitiator, id) | |
| 386 | ||
| 387 | // initialize secretbox state for unordered messages | |
| 388 | this._setupSecretSend(handshakeHash) | |
| 389 | ||
| 390 | this.emit('handshake') | |
| 391 | // if rawStream is a bridge, also emit it there | |
| 392 | if (this.rawStream !== this._rawStream) this.rawStream.emit('handshake') | |
| 393 | ||
| 394 | if (this.destroying) return | |
| 395 | ||
| 396 | this._rawStream.write(buf) | |
| 397 | } | |
| 398 | ||
| 399 | _setupSecretSend(handshakeHash) { | |
| 400 | this._sendState = b4a.allocUnsafeSlow(32 + 32 + 8 + 8) | |
| 401 | const encrypt = this._sendState.subarray(0, 32) // secrets | |
| 402 | const decrypt = this._sendState.subarray(32, 64) | |
| 403 | const counter = this._sendState.subarray(64, 72) // nonce | |
| 404 | const initial = this._sendState.subarray(72) | |
| 405 | ||
| 406 | const inputs = this.isInitiator | |
| 407 | ? [ | |
| 408 | [NS_INITIATOR, NS_SEND], | |
| 409 | [NS_RESPONDER, NS_SEND] | |
| 410 | ] | |
| 411 | : [ | |
| 412 | [NS_RESPONDER, NS_SEND], | |
| 413 | [NS_INITIATOR, NS_SEND] | |
| 414 | ] | |
| 415 | ||
| 416 | sodium.crypto_generichash_batch(encrypt, inputs[0], handshakeHash) | |
| 417 | sodium.crypto_generichash_batch(decrypt, inputs[1], handshakeHash) | |
| 418 | ||
| 419 | sodium.randombytes_buf(initial) | |
| 420 | counter.set(initial) | |
| 421 | } | |
| 422 | ||
| 423 | _open(cb) { | |
| 424 | // no autostart or no handshake yet | |
| 425 | if (this._rawStream === null || (this._handshake === null && this._encrypt === null)) { | |
| 426 | this._startDone = cb | |
| 427 | return | |
| 428 | } | |
| 429 | ||
| 430 | this._rawStream.on('data', this._onrawdata.bind(this)) | |
| 431 | this._rawStream.on('end', this._onrawend.bind(this)) | |
| 432 | this._rawStream.on('drain', this._onrawdrain.bind(this)) | |
| 433 | ||
| 434 | if (this.enableSend) this._rawStream.on('message', this._onmessage.bind(this)) | |
| 435 | ||
| 436 | if (this._encrypt !== null) { | |
| 437 | this._resolveOpened(true) | |
| 438 | return cb(null) | |
| 439 | } | |
| 440 | ||
| 441 | this._handshakeDone = cb | |
| 442 | ||
| 443 | if (this.isInitiator) this._onhandshakert(this._handshake.send()) | |
| 444 | } | |
| 445 | ||
| 446 | _predestroy() { | |
| 447 | if (this.rawStream) { | |
| 448 | const error = getStreamError(this) | |
| 449 | this.rawStream.destroy(error) | |
| 450 | } | |
| 451 | ||
| 452 | if (this._startDone !== null) { | |
| 453 | const done = this._startDone | |
| 454 | this._startDone = null | |
| 455 | done(new Error('Stream destroyed')) | |
| 456 | } | |
| 457 | ||
| 458 | if (this._handshakeDone !== null) { | |
| 459 | const done = this._handshakeDone | |
| 460 | this._handshakeDone = null | |
| 461 | done(new Error('Stream destroyed')) | |
| 462 | } | |
| 463 | ||
| 464 | if (this._drainDone !== null) { | |
| 465 | const done = this._drainDone | |
| 466 | this._drainDone = null | |
| 467 | done(new Error('Stream destroyed')) | |
| 468 | } | |
| 469 | } | |
| 470 | ||
| 471 | _write(data, cb) { | |
| 472 | let wrapped = this._outgoingWrapped | |
| 473 | ||
| 474 | if (data !== this._outgoingPlain) { | |
| 475 | wrapped = b4a.allocUnsafe(data.byteLength + 3 + ABYTES) | |
| 476 | wrapped.set(data, 4) | |
| 477 | } else { | |
| 478 | this._outgoingWrapped = this._outgoingPlain = null | |
| 479 | } | |
| 480 | ||
| 481 | if (wrapped.byteLength - 3 > MAX_ATOMIC_WRITE) { | |
| 482 | return cb( | |
| 483 | new Error( | |
| 484 | 'Message is too large for an atomic write. Max size is ' + MAX_ATOMIC_WRITE + ' bytes.' | |
| 485 | ) | |
| 486 | ) | |
| 487 | } | |
| 488 | this.rawBytesWritten += wrapped.byteLength | |
| 489 | ||
| 490 | writeUint24le(wrapped.byteLength - 3, wrapped) | |
| 491 | // offset 4 so we can do it in-place | |
| 492 | this._encrypt.next(wrapped.subarray(4, 4 + data.byteLength), wrapped.subarray(3)) | |
| 493 | ||
| 494 | if (this._keepAliveTimer !== null) this._keepAliveTimer.refresh() | |
| 495 | ||
| 496 | if (this._rawStream.write(wrapped) === false) { | |
| 497 | this._drainDone = cb | |
| 498 | } else { | |
| 499 | cb(null) | |
| 500 | } | |
| 501 | } | |
| 502 | ||
| 503 | _final(cb) { | |
| 504 | this._clearKeepAlive() | |
| 505 | this._ended-- | |
| 506 | this._rawStream.end() | |
| 507 | cb(null) | |
| 508 | } | |
| 509 | ||
| 510 | _resolveOpened(val) { | |
| 511 | if (this._openedDone === null) return | |
| 512 | const opened = this._openedDone | |
| 513 | this._openedDone = null | |
| 514 | opened(val) | |
| 515 | if (!val) return | |
| 516 | this.connected = true | |
| 517 | this.emit('connect') | |
| 518 | } | |
| 519 | ||
| 520 | _clearTimeout() { | |
| 521 | if (this._timeoutTimer === null) return | |
| 522 | this._timeoutTimer.destroy() | |
| 523 | this._timeoutTimer = null | |
| 524 | this.timeout = 0 | |
| 525 | } | |
| 526 | ||
| 527 | _clearKeepAlive() { | |
| 528 | if (this._keepAliveTimer === null) return | |
| 529 | this._keepAliveTimer.destroy() | |
| 530 | this._keepAliveTimer = null | |
| 531 | this.keepAlive = 0 | |
| 532 | } | |
| 533 | ||
| 534 | _destroy(cb) { | |
| 535 | this._clearKeepAlive() | |
| 536 | this._clearTimeout() | |
| 537 | this._resolveOpened(false) | |
| 538 | cb(null) | |
| 539 | } | |
| 540 | ||
| 541 | _boxMessage(buffer) { | |
| 542 | const MB = sodium.crypto_secretbox_MACBYTES // 16 | |
| 543 | const NB = sodium.crypto_secretbox_NONCEBYTES // 24 | |
| 544 | ||
| 545 | const counter = this._sendState.subarray(64, 72) | |
| 546 | sodium.sodium_increment(counter) | |
| 547 | if (b4a.equals(counter, this._sendState.subarray(72))) { | |
| 548 | this.destroy(new Error('udp send nonce exchausted')) | |
| 549 | return | |
| 550 | } | |
| 551 | ||
| 552 | const secret = this._sendState.subarray(0, 32) | |
| 553 | const envelope = b4a.allocUnsafe(8 + MB + buffer.byteLength) | |
| 554 | const nonce = envelope.subarray(0, NB) | |
| 555 | const ciphertext = envelope.subarray(8) | |
| 556 | ||
| 557 | b4a.fill(nonce, 0) // pad suffix | |
| 558 | nonce.set(counter) | |
| 559 | ||
| 560 | sodium.crypto_secretbox_easy(ciphertext, buffer, nonce, secret) | |
| 561 | return envelope | |
| 562 | } | |
| 563 | ||
| 564 | send(buffer) { | |
| 565 | if (!this._sendState) return | |
| 566 | if (!this.rawStream?.send) return // udx-stream expected | |
| 567 | ||
| 568 | const message = this._boxMessage(buffer) | |
| 569 | return this.rawStream.send(message) | |
| 570 | } | |
| 571 | ||
| 572 | trySend(buffer) { | |
| 573 | if (!this._sendState) return | |
| 574 | if (!this.rawStream?.trySend) return // udx-stream expected | |
| 575 | ||
| 576 | const message = this._boxMessage(buffer) | |
| 577 | this.rawStream.trySend(message) | |
| 578 | } | |
| 579 | ||
| 580 | _onmessage(buffer) { | |
| 581 | if (!this._sendState) return // messages before handshake are dropped | |
| 582 | ||
| 583 | const MB = sodium.crypto_secretbox_MACBYTES // 16 | |
| 584 | const NB = sodium.crypto_secretbox_NONCEBYTES // 24 | |
| 585 | ||
| 586 | if (buffer.byteLength < NB) return // Invalid message | |
| 587 | ||
| 588 | const nonce = b4a.allocUnsafe(NB) | |
| 589 | b4a.fill(nonce, 0) | |
| 590 | nonce.set(buffer.subarray(0, 8)) | |
| 591 | ||
| 592 | const secret = this._sendState.subarray(32, 64) | |
| 593 | const ciphertext = buffer.subarray(8) | |
| 594 | const plain = buffer.subarray(8, buffer.byteLength - MB) | |
| 595 | ||
| 596 | if (ciphertext.byteLength < MB) return // invalid message | |
| 597 | ||
| 598 | const success = sodium.crypto_secretbox_open_easy(plain, ciphertext, nonce, secret) | |
| 599 | ||
| 600 | if (success) this.emit('message', plain) | |
| 601 | } | |
| 602 | ||
| 603 | alloc(len) { | |
| 604 | const buf = b4a.allocUnsafe(len + 3 + ABYTES) | |
| 605 | this._outgoingWrapped = buf | |
| 606 | this._outgoingPlain = buf.subarray(4, buf.byteLength - ABYTES + 1) | |
| 607 | return this._outgoingPlain | |
| 608 | } | |
| 609 | ||
| 610 | toJSON() { | |
| 611 | return { | |
| 612 | isInitiator: this.isInitiator, | |
| 613 | publicKey: this.publicKey && b4a.toString(this.publicKey, 'hex'), | |
| 614 | remotePublicKey: this.remotePublicKey && b4a.toString(this.remotePublicKey, 'hex'), | |
| 615 | connected: this.connected, | |
| 616 | destroying: this.destroying, | |
| 617 | destroyed: this.destroyed, | |
| 618 | rawStream: this.rawStream && this.rawStream.toJSON ? this.rawStream.toJSON() : null | |
| 619 | } | |
| 620 | } | |
| 621 | } | |
| 622 | ||
| 623 | function writeUint24le(n, buf) { | |
| 624 | buf[0] = n & 255 | |
| 625 | buf[1] = (n >>> 8) & 255 | |
| 626 | buf[2] = (n >>> 16) & 255 | |
| 627 | } | |
| 628 | ||
| 629 | function streamId(handshakeHash, isInitiator, out = b4a.allocUnsafe(32)) { | |
| 630 | sodium.crypto_generichash(out, isInitiator ? NS_INITIATOR : NS_RESPONDER, handshakeHash) | |
| 631 | return out | |
| 632 | } | |
| 633 | ||
| 634 | function toBuffer(data) { | |
| 635 | return typeof data === 'string' ? b4a.from(data) : data | |
| 636 | } | |
| 637 | ||
| 638 | function destroyTimeout() { | |
| 639 | this.destroy(new Error('Stream timed out')) | |
| 640 | } | |
| 641 | ||
| 642 | function sendKeepAlive() { | |
| 643 | const empty = this.alloc(0) | |
| 644 | this.write(empty) | |
| 645 | } |
| … | … | @@ -0,0 +1,76 @@ |
| 1 | const { Duplex, Writable } = require('streamx') | |
| 2 | ||
| 3 | class ReversePassThrough extends Duplex { | |
| 4 | constructor(s) { | |
| 5 | super() | |
| 6 | this._stream = s | |
| 7 | this._ondrain = null | |
| 8 | } | |
| 9 | ||
| 10 | _write(data, cb) { | |
| 11 | if (this._stream.push(data) === false) { | |
| 12 | this._stream._ondrain = cb | |
| 13 | } else { | |
| 14 | cb(null) | |
| 15 | } | |
| 16 | } | |
| 17 | ||
| 18 | _final(cb) { | |
| 19 | this._stream.push(null) | |
| 20 | cb(null) | |
| 21 | } | |
| 22 | ||
| 23 | _read(cb) { | |
| 24 | const ondrain = this._ondrain | |
| 25 | this._ondrain = null | |
| 26 | if (ondrain) ondrain() | |
| 27 | cb(null) | |
| 28 | } | |
| 29 | } | |
| 30 | ||
| 31 | module.exports = class Bridge extends Duplex { | |
| 32 | constructor(noiseStream) { | |
| 33 | super() | |
| 34 | ||
| 35 | this.noiseStream = noiseStream | |
| 36 | ||
| 37 | this._ondrain = null | |
| 38 | this.reverse = new ReversePassThrough(this) | |
| 39 | } | |
| 40 | ||
| 41 | get publicKey() { | |
| 42 | return this.noiseStream.publicKey | |
| 43 | } | |
| 44 | ||
| 45 | get remotePublicKey() { | |
| 46 | return this.noiseStream.remotePublicKey | |
| 47 | } | |
| 48 | ||
| 49 | get handshakeHash() { | |
| 50 | return this.noiseStream.handshakeHash | |
| 51 | } | |
| 52 | ||
| 53 | flush() { | |
| 54 | return Writable.drained(this) | |
| 55 | } | |
| 56 | ||
| 57 | _read(cb) { | |
| 58 | const ondrain = this._ondrain | |
| 59 | this._ondrain = null | |
| 60 | if (ondrain) ondrain() | |
| 61 | cb(null) | |
| 62 | } | |
| 63 | ||
| 64 | _write(data, cb) { | |
| 65 | if (this.reverse.push(data) === false) { | |
| 66 | this.reverse._ondrain = cb | |
| 67 | } else { | |
| 68 | cb(null) | |
| 69 | } | |
| 70 | } | |
| 71 | ||
| 72 | _final(cb) { | |
| 73 | this.reverse.push(null) | |
| 74 | cb(null) | |
| 75 | } | |
| 76 | } |
| … | … | @@ -0,0 +1,78 @@ |
| 1 | const sodium = require('sodium-universal') | |
| 2 | const curve = require('noise-curve-ed') | |
| 3 | const Noise = require('noise-handshake') | |
| 4 | const b4a = require('b4a') | |
| 5 | ||
| 6 | const EMPTY = b4a.alloc(0) | |
| 7 | ||
| 8 | module.exports = class Handshake { | |
| 9 | constructor(isInitiator, keyPair, remotePublicKey, pattern) { | |
| 10 | this.isInitiator = isInitiator | |
| 11 | this.keyPair = keyPair | |
| 12 | this.noise = new Noise(pattern, isInitiator, keyPair, { curve }) | |
| 13 | this.noise.initialise(EMPTY, remotePublicKey) | |
| 14 | this.destroyed = false | |
| 15 | } | |
| 16 | ||
| 17 | static keyPair(seed) { | |
| 18 | const publicKey = b4a.alloc(32) | |
| 19 | const secretKey = b4a.alloc(64) | |
| 20 | if (seed) sodium.crypto_sign_seed_keypair(publicKey, secretKey, seed) | |
| 21 | else sodium.crypto_sign_keypair(publicKey, secretKey) | |
| 22 | return { publicKey, secretKey } | |
| 23 | } | |
| 24 | ||
| 25 | recv(data) { | |
| 26 | try { | |
| 27 | this.noise.recv(data) | |
| 28 | if (this.noise.complete) return this._return(null) | |
| 29 | return this.send() | |
| 30 | } catch { | |
| 31 | this.destroy() | |
| 32 | return null | |
| 33 | } | |
| 34 | } | |
| 35 | ||
| 36 | // note that the data returned here is framed so we don't have to do an extra copy | |
| 37 | // when sending it... | |
| 38 | send() { | |
| 39 | try { | |
| 40 | const data = this.noise.send() | |
| 41 | const wrap = b4a.allocUnsafe(data.byteLength + 3) | |
| 42 | ||
| 43 | writeUint24le(data.byteLength, wrap) | |
| 44 | wrap.set(data, 3) | |
| 45 | ||
| 46 | return this._return(wrap) | |
| 47 | } catch { | |
| 48 | this.destroy() | |
| 49 | return null | |
| 50 | } | |
| 51 | } | |
| 52 | ||
| 53 | destroy() { | |
| 54 | if (this.destroyed) return | |
| 55 | this.destroyed = true | |
| 56 | } | |
| 57 | ||
| 58 | _return(data) { | |
| 59 | const tx = this.noise.complete ? b4a.toBuffer(this.noise.tx) : null | |
| 60 | const rx = this.noise.complete ? b4a.toBuffer(this.noise.rx) : null | |
| 61 | const hash = this.noise.complete ? b4a.toBuffer(this.noise.hash) : null | |
| 62 | const remotePublicKey = this.noise.complete ? b4a.toBuffer(this.noise.rs) : null | |
| 63 | ||
| 64 | return { | |
| 65 | data, | |
| 66 | remotePublicKey, | |
| 67 | hash, | |
| 68 | tx, | |
| 69 | rx | |
| 70 | } | |
| 71 | } | |
| 72 | } | |
| 73 | ||
| 74 | function writeUint24le(n, buf) { | |
| 75 | buf[0] = n & 255 | |
| 76 | buf[1] = (n >>> 8) & 255 | |
| 77 | buf[2] = (n >>> 16) & 255 | |
| 78 | } |
| … | … | @@ -0,0 +1,279 @@ |
| 1 | cmake_minimum_required(VERSION 3.25) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) | |
| 5 | find_package(cmake-napi REQUIRED PATHS node_modules/cmake-napi) | |
| 6 | find_package(cmake-npm REQUIRED PATHS node_modules/cmake-npm) | |
| 7 | ||
| 8 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) | |
| 9 | ||
| 10 | project(sodium_native C ASM CXX) | |
| 11 | ||
| 12 | fetch_package("github:jedisct1/libsodium#e18eee6" SOURCE_DIR sodium) | |
| 13 | ||
| 14 | fetch_package("github:holepunchto/libjstl#098664c") | |
| 15 | ||
| 16 | bare_target(target) | |
| 17 | ||
| 18 | if(target MATCHES "win32") | |
| 19 | add_compile_options(/MT$<$<CONFIG:Debug>:d>) | |
| 20 | endif() | |
| 21 | ||
| 22 | file(COPY_FILE "${sodium}/builds/msvc/version.h" "${sodium}/src/libsodium/include/sodium/version.h") | |
| 23 | ||
| 24 | file(GLOB_RECURSE sodium_headers CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.h") | |
| 25 | file(GLOB_RECURSE sodium_sources CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.c") | |
| 26 | file(GLOB_RECURSE sodium_asm_sources CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.S") | |
| 27 | ||
| 28 | add_library(sodium OBJECT) | |
| 29 | ||
| 30 | target_sources( | |
| 31 | sodium | |
| 32 | INTERFACE | |
| 33 | ${sodium_headers} | |
| 34 | PRIVATE | |
| 35 | ${sodium_sources} | |
| 36 | ) | |
| 37 | ||
| 38 | target_include_directories( | |
| 39 | sodium | |
| 40 | INTERFACE | |
| 41 | "${sodium}/src/libsodium/include" | |
| 42 | PRIVATE | |
| 43 | "${sodium}/src/libsodium/include/sodium" | |
| 44 | ) | |
| 45 | ||
| 46 | if(NOT target MATCHES "win32") | |
| 47 | target_compile_options( | |
| 48 | sodium | |
| 49 | PRIVATE | |
| 50 | -fvisibility=hidden | |
| 51 | -fno-strict-aliasing | |
| 52 | -fwrapv | |
| 53 | -flax-vector-conversions | |
| 54 | ) | |
| 55 | endif() | |
| 56 | ||
| 57 | target_compile_definitions( | |
| 58 | sodium | |
| 59 | PUBLIC | |
| 60 | SODIUM_STATIC=1 | |
| 61 | PRIVATE | |
| 62 | _GNU_SOURCE=1 | |
| 63 | CONFIGURED=1 | |
| 64 | DEV_MODE=1 | |
| 65 | HAVE_ATOMIC_OPS=1 | |
| 66 | HAVE_C11_MEMORY_FENCES=1 | |
| 67 | HAVE_CET_H=1 | |
| 68 | HAVE_GCC_MEMORY_FENCES=1 | |
| 69 | HAVE_INLINE_ASM=1 | |
| 70 | HAVE_INTTYPES_H=1 | |
| 71 | HAVE_STDINT_H=1 | |
| 72 | HAVE_TI_MODE=1 | |
| 73 | ) | |
| 74 | ||
| 75 | if(target MATCHES "darwin|ios") | |
| 76 | target_compile_definitions( | |
| 77 | sodium | |
| 78 | PRIVATE | |
| 79 | ASM_HIDE_SYMBOL=.private_extern | |
| 80 | TLS=_Thread_local | |
| 81 | HAVE_ARC4RANDOM=1 | |
| 82 | HAVE_ARC4RANDOM_BUF=1 | |
| 83 | HAVE_CATCHABLE_ABRT=1 | |
| 84 | HAVE_CATCHABLE_SEGV=1 | |
| 85 | HAVE_CLOCK_GETTIME=1 | |
| 86 | HAVE_GETPID=1 | |
| 87 | HAVE_MADVISE=1 | |
| 88 | HAVE_MEMSET_S=1 | |
| 89 | HAVE_MLOCK=1 | |
| 90 | HAVE_MMAP=1 | |
| 91 | HAVE_MPROTECT=1 | |
| 92 | HAVE_NANOSLEEP=1 | |
| 93 | HAVE_POSIX_MEMALIGN=1 | |
| 94 | HAVE_PTHREAD=1 | |
| 95 | HAVE_PTHREAD_PRIO_INHERIT=1 | |
| 96 | HAVE_RAISE=1 | |
| 97 | HAVE_SYSCONF=1 | |
| 98 | HAVE_SYS_MMAN_H=1 | |
| 99 | HAVE_SYS_PARAM_H=1 | |
| 100 | HAVE_WEAK_SYMBOLS=1 | |
| 101 | ) | |
| 102 | ||
| 103 | if(NOT target MATCHES "ios") | |
| 104 | target_compile_definitions( | |
| 105 | sodium | |
| 106 | PRIVATE | |
| 107 | HAVE_GETENTROPY=1 | |
| 108 | HAVE_SYS_RANDOM_H=1 | |
| 109 | ) | |
| 110 | endif() | |
| 111 | endif() | |
| 112 | ||
| 113 | if(target MATCHES "linux") | |
| 114 | target_compile_definitions( | |
| 115 | sodium | |
| 116 | PRIVATE | |
| 117 | ASM_HIDE_SYMBOL=.hidden | |
| 118 | TLS=_Thread_local | |
| 119 | HAVE_CATCHABLE_ABRT=1 | |
| 120 | HAVE_CATCHABLE_SEGV=1 | |
| 121 | HAVE_CLOCK_GETTIME=1 | |
| 122 | HAVE_GETPID=1 | |
| 123 | HAVE_MADVISE=1 | |
| 124 | HAVE_MLOCK=1 | |
| 125 | HAVE_MMAP=1 | |
| 126 | HAVE_MPROTECT=1 | |
| 127 | HAVE_NANOSLEEP=1 | |
| 128 | HAVE_POSIX_MEMALIGN=1 | |
| 129 | HAVE_PTHREAD_PRIO_INHERIT=1 | |
| 130 | HAVE_PTHREAD=1 | |
| 131 | HAVE_RAISE=1 | |
| 132 | HAVE_SYSCONF=1 | |
| 133 | HAVE_SYS_AUXV_H=1 | |
| 134 | HAVE_SYS_MMAN_H=1 | |
| 135 | HAVE_SYS_PARAM_H=1 | |
| 136 | HAVE_SYS_RANDOM_H=1 | |
| 137 | HAVE_WEAK_SYMBOLS=1 | |
| 138 | ) | |
| 139 | endif() | |
| 140 | ||
| 141 | if(target MATCHES "win32") | |
| 142 | target_compile_definitions( | |
| 143 | sodium | |
| 144 | PRIVATE | |
| 145 | _CRT_SECURE_NO_WARNINGS=1 | |
| 146 | HAVE_RAISE=1 | |
| 147 | ) | |
| 148 | endif() | |
| 149 | ||
| 150 | if(target MATCHES "x64") | |
| 151 | target_compile_definitions( | |
| 152 | sodium | |
| 153 | PRIVATE | |
| 154 | HAVE_CPUID=1 | |
| 155 | HAVE_RDRAND=1 | |
| 156 | HAVE_EMMINTRIN_H=1 # SSE2 | |
| 157 | HAVE_PMMINTRIN_H=1 # SSE3 | |
| 158 | HAVE_TMMINTRIN_H=1 # SSSE3 | |
| 159 | HAVE_SMMINTRIN_H=1 # SSE4.1 | |
| 160 | HAVE_WMMINTRIN_H=1 # AES | |
| 161 | HAVE_AVXINTRIN_H=1 # AVX | |
| 162 | HAVE_AVX2INTRIN_H=1 # AVX2 | |
| 163 | HAVE_AVX512FINTRIN_H # AVX512F | |
| 164 | ) | |
| 165 | ||
| 166 | if(NOT target MATCHES "win32") | |
| 167 | target_compile_definitions( | |
| 168 | sodium | |
| 169 | PRIVATE | |
| 170 | HAVE_AMD64_ASM=1 | |
| 171 | HAVE_AVX_ASM=1 | |
| 172 | ) | |
| 173 | ||
| 174 | target_sources( | |
| 175 | sodium | |
| 176 | PRIVATE | |
| 177 | ${sodium_asm_sources} | |
| 178 | ) | |
| 179 | endif() | |
| 180 | endif() | |
| 181 | ||
| 182 | if(target MATCHES "arm64") | |
| 183 | target_compile_definitions( | |
| 184 | sodium | |
| 185 | PRIVATE | |
| 186 | HAVE_ARMCRYPTO=1 | |
| 187 | ) | |
| 188 | endif() | |
| 189 | ||
| 190 | if(CMAKE_C_BYTE_ORDER MATCHES "BIG_ENDIAN") | |
| 191 | target_compile_definitions( | |
| 192 | sodium | |
| 193 | PRIVATE | |
| 194 | NATIVE_BIG_ENDIAN=1 | |
| 195 | ) | |
| 196 | else() | |
| 197 | target_compile_definitions( | |
| 198 | sodium | |
| 199 | PRIVATE | |
| 200 | NATIVE_LITTLE_ENDIAN=1 | |
| 201 | ) | |
| 202 | endif() | |
| 203 | ||
| 204 | if(target MATCHES "linux|android") | |
| 205 | target_link_options( | |
| 206 | sodium | |
| 207 | PUBLIC | |
| 208 | -Wl,-z,noexecstack | |
| 209 | ) | |
| 210 | endif() | |
| 211 | ||
| 212 | add_library(sodium_extensions OBJECT) | |
| 213 | ||
| 214 | target_sources( | |
| 215 | sodium_extensions | |
| 216 | PRIVATE | |
| 217 | extensions/tweak/tweak.c | |
| 218 | extensions/tweak/tweak.h | |
| 219 | extensions/pbkdf2/pbkdf2.c | |
| 220 | extensions/pbkdf2/pbkdf2.h | |
| 221 | ) | |
| 222 | ||
| 223 | target_link_libraries( | |
| 224 | sodium_extensions | |
| 225 | PUBLIC | |
| 226 | sodium | |
| 227 | ) | |
| 228 | ||
| 229 | add_bare_module(sodium_native_bare) | |
| 230 | ||
| 231 | target_sources( | |
| 232 | ${sodium_native_bare} | |
| 233 | PRIVATE | |
| 234 | binding.cc | |
| 235 | ) | |
| 236 | ||
| 237 | target_link_libraries( | |
| 238 | ${sodium_native_bare} | |
| 239 | PRIVATE | |
| 240 | $<TARGET_OBJECTS:sodium> | |
| 241 | $<TARGET_OBJECTS:sodium_extensions> | |
| 242 | jstl | |
| 243 | PUBLIC | |
| 244 | sodium | |
| 245 | sodium_extensions | |
| 246 | ) | |
| 247 | ||
| 248 | add_napi_module(sodium_native_node) | |
| 249 | ||
| 250 | target_sources( | |
| 251 | ${sodium_native_node} | |
| 252 | PRIVATE | |
| 253 | binding.cc | |
| 254 | ) | |
| 255 | ||
| 256 | target_compile_definitions( | |
| 257 | ${sodium_native_node} | |
| 258 | PRIVATE | |
| 259 | NAPI_VERSION=9 | |
| 260 | ) | |
| 261 | ||
| 262 | target_link_libraries( | |
| 263 | ${sodium_native_node} | |
| 264 | PRIVATE | |
| 265 | $<TARGET_OBJECTS:sodium> | |
| 266 | $<TARGET_OBJECTS:sodium_extensions> | |
| 267 | jstl | |
| 268 | PUBLIC | |
| 269 | sodium | |
| 270 | sodium_extensions | |
| 271 | ) | |
| 272 | ||
| 273 | resolve_node_module(bare-compat-napi compat) | |
| 274 | ||
| 275 | target_include_directories( | |
| 276 | ${sodium_native_node} | |
| 277 | PRIVATE | |
| 278 | "${compat}/include" | |
| 279 | ) |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2016 Mathias Buus and Emil Bay | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,51 @@ |
| 1 | # sodium-native | |
| 2 | ||
| 3 | Low level bindings for [libsodium](https://github.com/jedisct1/libsodium). | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install sodium-native | |
| 7 | ``` | |
| 8 | ||
| 9 | The goal of this project is to be thin, stable, unopionated wrapper around libsodium. | |
| 10 | ||
| 11 | All methods exposed are more or less a direct translation of the libsodium c-api. | |
| 12 | This means that most data types are buffers and you have to manage allocating return values and passing them in as arguments instead of receiving them as return values. | |
| 13 | ||
| 14 | This makes this API harder to use than other libsodium wrappers out there, but also means that you'll be able to get a lot of perf / memory improvements as you can do stuff like inline encryption / decryption, reuse buffers etc. | |
| 15 | ||
| 16 | This also makes this library useful as a foundation for more high level crypto abstractions that you want to make. | |
| 17 | ||
| 18 | ## Usage | |
| 19 | ||
| 20 | ```js | |
| 21 | var sodium = require('sodium-native') | |
| 22 | ||
| 23 | var nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES) | |
| 24 | var key = sodium.sodium_malloc(sodium.crypto_secretbox_KEYBYTES) // secure buffer | |
| 25 | var message = Buffer.from('Hello, World!') | |
| 26 | var ciphertext = Buffer.alloc(message.length + sodium.crypto_secretbox_MACBYTES) | |
| 27 | ||
| 28 | sodium.randombytes_buf(nonce) // insert random data into nonce | |
| 29 | sodium.randombytes_buf(key) // insert random data into key | |
| 30 | ||
| 31 | // encrypted message is stored in ciphertext. | |
| 32 | sodium.crypto_secretbox_easy(ciphertext, message, nonce, key) | |
| 33 | ||
| 34 | console.log('Encrypted message:', ciphertext) | |
| 35 | ||
| 36 | var plainText = Buffer.alloc(ciphertext.length - sodium.crypto_secretbox_MACBYTES) | |
| 37 | ||
| 38 | if (!sodium.crypto_secretbox_open_easy(plainText, ciphertext, nonce, key)) { | |
| 39 | console.log('Decryption failed!') | |
| 40 | } else { | |
| 41 | console.log('Decrypted message:', plainText, '(' + plainText.toString() + ')') | |
| 42 | } | |
| 43 | ``` | |
| 44 | ||
| 45 | ## Documentation | |
| 46 | ||
| 47 | Complete documentation may be found on the [sodium-friends website](https://sodium-friends.github.io/docs/docs/getstarted) | |
| 48 | ||
| 49 | ## License | |
| 50 | ||
| 51 | MIT |
| … | … | @@ -0,0 +1,4094 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <bare.h> | |
| 3 | #include <js.h> | |
| 4 | #include <jstl.h> | |
| 5 | #include <sodium.h> | |
| 6 | #include <stdbool.h> | |
| 7 | #include <stdint.h> | |
| 8 | #include <string.h> | |
| 9 | #include <uv.h> | |
| 10 | ||
| 11 | #include "extensions/pbkdf2/pbkdf2.h" | |
| 12 | #include "extensions/tweak/tweak.h" | |
| 13 | ||
| 14 | #define assert_bounds(arraybuffer) \ | |
| 15 | assert(arraybuffer##_offset + arraybuffer##_len <= arraybuffer.size()) | |
| 16 | ||
| 17 | static inline void | |
| 18 | sn_sodium_memzero( | |
| 19 | js_env_t *, | |
| 20 | js_receiver_t, | |
| 21 | js_typedarray_span_t<> buf | |
| 22 | ) { | |
| 23 | sodium_memzero(buf.data(), buf.size_bytes()); | |
| 24 | } | |
| 25 | ||
| 26 | static inline int | |
| 27 | sn_sodium_mlock( | |
| 28 | js_env_t *, | |
| 29 | js_receiver_t, | |
| 30 | js_typedarray_span_t<> buf | |
| 31 | ) { | |
| 32 | return sodium_mlock(buf.data(), buf.size_bytes()); | |
| 33 | } | |
| 34 | ||
| 35 | static inline int | |
| 36 | sn_sodium_munlock( | |
| 37 | js_env_t *, | |
| 38 | js_receiver_t, | |
| 39 | js_typedarray_span_t<> buf | |
| 40 | ) { | |
| 41 | return sodium_munlock(buf.data(), buf.size_bytes()); | |
| 42 | } | |
| 43 | ||
| 44 | static void | |
| 45 | sn_external_arraybuffer_finalize(js_env_t *env, uint8_t *ptr) { | |
| 46 | sodium_free(ptr); | |
| 47 | } | |
| 48 | ||
| 49 | static js_arraybuffer_t | |
| 50 | sn_sodium_malloc(js_env_t *env, js_receiver_t, uint32_t len) { | |
| 51 | int err; | |
| 52 | ||
| 53 | auto ptr = reinterpret_cast<uint8_t *>(sodium_malloc(len)); | |
| 54 | ||
| 55 | js_arraybuffer_t buffer; | |
| 56 | ||
| 57 | err = js_create_external_arraybuffer<sn_external_arraybuffer_finalize>(env, ptr, len, buffer); | |
| 58 | assert(err == 0); | |
| 59 | ||
| 60 | return buffer; | |
| 61 | } | |
| 62 | ||
| 63 | static void | |
| 64 | sn_sodium_free(js_env_t *env, js_receiver_t, js_arraybuffer_t buf) { | |
| 65 | int err; | |
| 66 | ||
| 67 | std::span<uint8_t> view; | |
| 68 | err = js_get_arraybuffer_info(env, buf, view); | |
| 69 | assert(err == 0); | |
| 70 | ||
| 71 | if (view.empty()) return; | |
| 72 | ||
| 73 | err = js_detach_arraybuffer(env, buf); | |
| 74 | assert(err == 0); | |
| 75 | } | |
| 76 | ||
| 77 | static inline int | |
| 78 | sn_sodium_mprotect_noaccess(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 79 | return sodium_mprotect_noaccess(buf.data()); | |
| 80 | } | |
| 81 | ||
| 82 | static inline int | |
| 83 | sn_sodium_mprotect_readonly(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 84 | return sodium_mprotect_readonly(buf.data()); | |
| 85 | } | |
| 86 | ||
| 87 | static inline int | |
| 88 | sn_sodium_mprotect_readwrite(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 89 | return sodium_mprotect_readwrite(buf.data()); | |
| 90 | } | |
| 91 | ||
| 92 | static inline uint32_t // TODO: test envless | |
| 93 | sn_randombytes_random(js_env_t *env, js_receiver_t) { | |
| 94 | return randombytes_random(); | |
| 95 | } | |
| 96 | ||
| 97 | static inline uint32_t // TODO: test envless | |
| 98 | sn_randombytes_uniform(js_env_t *env, js_receiver_t, uint32_t upper_bound) { | |
| 99 | return randombytes_uniform(upper_bound); | |
| 100 | } | |
| 101 | ||
| 102 | static inline void | |
| 103 | sn_randombytes_buf( | |
| 104 | js_env_t *env, | |
| 105 | js_receiver_t, | |
| 106 | js_arraybuffer_span_t buf, | |
| 107 | uint32_t buf_offset, | |
| 108 | uint32_t buf_len | |
| 109 | ) { | |
| 110 | assert_bounds(buf); | |
| 111 | ||
| 112 | randombytes_buf(&buf[buf_offset], buf_len); | |
| 113 | } | |
| 114 | ||
| 115 | static inline void | |
| 116 | sn_randombytes_buf_deterministic( | |
| 117 | js_env_t *env, | |
| 118 | js_receiver_t, | |
| 119 | ||
| 120 | js_arraybuffer_span_t buf, | |
| 121 | uint32_t buf_offset, | |
| 122 | uint32_t buf_len, | |
| 123 | ||
| 124 | js_arraybuffer_span_t seed, | |
| 125 | uint32_t seed_offset, | |
| 126 | uint32_t seed_len | |
| 127 | ) { | |
| 128 | assert_bounds(buf); | |
| 129 | assert_bounds(seed); | |
| 130 | ||
| 131 | assert(seed_len == randombytes_SEEDBYTES); | |
| 132 | ||
| 133 | randombytes_buf_deterministic(&buf[buf_offset], buf_len, &seed[seed_offset]); | |
| 134 | } | |
| 135 | ||
| 136 | static inline bool | |
| 137 | sn_sodium_memcmp(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 138 | if (a.size_bytes() != b.size_bytes()) return false; | |
| 139 | ||
| 140 | return sodium_memcmp(a.data(), b.data(), a.size_bytes()) == 0; | |
| 141 | } | |
| 142 | ||
| 143 | static inline void | |
| 144 | sn_sodium_increment(js_env_t *, js_receiver_t, js_typedarray_span_t<> n) { | |
| 145 | sodium_increment(n.data(), n.size_bytes()); | |
| 146 | } | |
| 147 | ||
| 148 | static inline void | |
| 149 | sn_sodium_add(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 150 | sodium_add(a.data(), b.data(), a.size_bytes()); | |
| 151 | } | |
| 152 | ||
| 153 | static inline void | |
| 154 | sn_sodium_sub(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 155 | sodium_sub(a.data(), b.data(), a.size_bytes()); | |
| 156 | } | |
| 157 | ||
| 158 | static inline int32_t | |
| 159 | sn_sodium_compare(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 160 | return sodium_compare(a.data(), b.data(), a.size_bytes()); | |
| 161 | } | |
| 162 | ||
| 163 | static inline bool | |
| 164 | sn_sodium_is_zero(js_env_t *, js_receiver_t, js_typedarray_span_t<> buffer, uint32_t len) { | |
| 165 | assert(len <= buffer.size_bytes()); | |
| 166 | ||
| 167 | return sodium_is_zero(buffer.data(), len) != 0; | |
| 168 | } | |
| 169 | ||
| 170 | static inline uint32_t | |
| 171 | sn_sodium_pad(js_env_t *, js_receiver_t, js_typedarray_span_t<> buf, uint32_t unpadded_buflen, uint32_t blocksize) { | |
| 172 | size_t padded_buflen; | |
| 173 | ||
| 174 | sodium_pad(&padded_buflen, buf.data(), unpadded_buflen, blocksize, buf.size_bytes()); | |
| 175 | ||
| 176 | return padded_buflen; | |
| 177 | } | |
| 178 | ||
| 179 | static inline uint32_t | |
| 180 | sn_sodium_unpad(js_env_t *, js_receiver_t, js_typedarray_span_t<> buf, uint32_t padded_buflen, uint32_t blocksize) { | |
| 181 | size_t unpadded_buflen; | |
| 182 | ||
| 183 | sodium_unpad(&unpadded_buflen, buf.data(), padded_buflen, blocksize); | |
| 184 | ||
| 185 | return unpadded_buflen; | |
| 186 | } | |
| 187 | ||
| 188 | static inline int | |
| 189 | sn_crypto_sign_keypair(js_env_t *, js_receiver_t, js_typedarray_span_t<> pk, js_typedarray_span_t<> sk) { | |
| 190 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 191 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 192 | ||
| 193 | return crypto_sign_keypair(pk.data(), sk.data()); | |
| 194 | } | |
| 195 | ||
| 196 | static inline int | |
| 197 | sn_crypto_sign_seed_keypair( | |
| 198 | js_env_t *, | |
| 199 | js_receiver_t, | |
| 200 | js_typedarray_span_t<> pk, | |
| 201 | js_typedarray_span_t<> sk, | |
| 202 | js_typedarray_span_t<> seed | |
| 203 | ) { | |
| 204 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 205 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 206 | assert(seed.size_bytes() == crypto_sign_SEEDBYTES); | |
| 207 | ||
| 208 | return crypto_sign_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 209 | } | |
| 210 | ||
| 211 | static inline int | |
| 212 | sn_crypto_sign( | |
| 213 | js_env_t *, | |
| 214 | js_receiver_t, | |
| 215 | js_typedarray_span_t<> sm, | |
| 216 | js_typedarray_span_t<> m, | |
| 217 | js_typedarray_span_t<> sk | |
| 218 | ) { | |
| 219 | assert(sm.size_bytes() == crypto_sign_BYTES + m.size_bytes()); | |
| 220 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 221 | ||
| 222 | return crypto_sign(sm.data(), NULL, m.data(), m.size_bytes(), sk.data()); | |
| 223 | } | |
| 224 | ||
| 225 | static inline bool | |
| 226 | sn_crypto_sign_open( | |
| 227 | js_env_t *, | |
| 228 | js_receiver_t, | |
| 229 | js_typedarray_span_t<> m, | |
| 230 | js_typedarray_span_t<> sm, | |
| 231 | js_typedarray_span_t<> pk | |
| 232 | ) { | |
| 233 | assert(m.size_bytes() == sm.size_bytes() - crypto_sign_BYTES); | |
| 234 | assert(sm.size_bytes() >= crypto_sign_BYTES); | |
| 235 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 236 | ||
| 237 | return crypto_sign_open(m.data(), NULL, sm.data(), sm.size_bytes(), pk.data()) == 0; | |
| 238 | } | |
| 239 | ||
| 240 | static inline int | |
| 241 | sn_crypto_sign_detached( | |
| 242 | js_env_t *env, | |
| 243 | js_receiver_t, | |
| 244 | js_typedarray_span_t<> sig, | |
| 245 | js_typedarray_span_t<> m, | |
| 246 | js_typedarray_span_t<> sk | |
| 247 | ) { | |
| 248 | assert(sig.size_bytes() == crypto_sign_BYTES); | |
| 249 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 250 | ||
| 251 | return crypto_sign_detached(sig.data(), NULL, m.data(), m.size_bytes(), sk.data()); | |
| 252 | } | |
| 253 | ||
| 254 | static inline bool | |
| 255 | sn_crypto_sign_verify_detached( | |
| 256 | js_env_t *env, | |
| 257 | js_receiver_t, | |
| 258 | ||
| 259 | js_arraybuffer_span_t sig, | |
| 260 | uint32_t sig_offset, | |
| 261 | uint32_t sig_len, | |
| 262 | ||
| 263 | js_arraybuffer_span_t m, | |
| 264 | uint32_t m_offset, | |
| 265 | uint32_t m_len, | |
| 266 | ||
| 267 | js_arraybuffer_span_t pk, | |
| 268 | uint32_t pk_offset, | |
| 269 | uint32_t pk_len | |
| 270 | ) { | |
| 271 | assert_bounds(sig); | |
| 272 | assert_bounds(m); | |
| 273 | assert_bounds(pk); | |
| 274 | ||
| 275 | assert(sig_len >= crypto_sign_BYTES); | |
| 276 | assert(pk_len == crypto_sign_PUBLICKEYBYTES); | |
| 277 | ||
| 278 | return crypto_sign_verify_detached(&sig[sig_offset], &m[m_offset], m_len, &pk[pk_offset]) == 0; | |
| 279 | } | |
| 280 | ||
| 281 | static inline int | |
| 282 | sn_crypto_sign_ed25519_sk_to_pk( | |
| 283 | js_env_t *, | |
| 284 | js_receiver_t, | |
| 285 | js_typedarray_span_t<> pk, | |
| 286 | js_typedarray_span_t<> sk | |
| 287 | ) { | |
| 288 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 289 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 290 | ||
| 291 | return crypto_sign_ed25519_sk_to_pk(pk.data(), sk.data()); | |
| 292 | } | |
| 293 | ||
| 294 | static inline int | |
| 295 | sn_crypto_sign_ed25519_pk_to_curve25519( | |
| 296 | js_env_t *, | |
| 297 | js_receiver_t, | |
| 298 | js_typedarray_span_t<> x25519_pk, | |
| 299 | js_typedarray_span_t<> ed25519_pk | |
| 300 | ) { | |
| 301 | assert(x25519_pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 302 | assert(ed25519_pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 303 | ||
| 304 | return crypto_sign_ed25519_pk_to_curve25519(x25519_pk.data(), ed25519_pk.data()); | |
| 305 | } | |
| 306 | ||
| 307 | static inline int | |
| 308 | sn_crypto_sign_ed25519_sk_to_curve25519( | |
| 309 | js_env_t *, | |
| 310 | js_receiver_t, | |
| 311 | js_typedarray_span_t<> x25519_sk, | |
| 312 | js_typedarray_span_t<> ed25519_sk | |
| 313 | ) { | |
| 314 | assert(x25519_sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 315 | assert( | |
| 316 | ed25519_sk.size_bytes() == crypto_sign_SECRETKEYBYTES || | |
| 317 | ed25519_sk.size_bytes() == crypto_box_SECRETKEYBYTES | |
| 318 | ); | |
| 319 | ||
| 320 | return crypto_sign_ed25519_sk_to_curve25519(x25519_sk.data(), ed25519_sk.data()); | |
| 321 | } | |
| 322 | ||
| 323 | static inline int | |
| 324 | sn_crypto_generichash( | |
| 325 | js_env_t *env, | |
| 326 | js_receiver_t, | |
| 327 | ||
| 328 | js_arraybuffer_span_t out, | |
| 329 | uint32_t out_offset, | |
| 330 | uint32_t out_len, | |
| 331 | ||
| 332 | js_arraybuffer_span_t in, | |
| 333 | uint32_t in_offset, | |
| 334 | uint32_t in_len, | |
| 335 | ||
| 336 | js_arraybuffer_span_t key, | |
| 337 | uint32_t key_offset, | |
| 338 | uint32_t key_len | |
| 339 | ) { | |
| 340 | assert_bounds(out); | |
| 341 | assert( | |
| 342 | out_len >= crypto_generichash_BYTES_MIN && | |
| 343 | out_len <= crypto_generichash_BYTES_MAX | |
| 344 | ); | |
| 345 | ||
| 346 | assert_bounds(in); | |
| 347 | ||
| 348 | uint8_t *key_data = NULL; | |
| 349 | if (key_len) { | |
| 350 | assert_bounds(key); | |
| 351 | assert( | |
| 352 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 353 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 354 | ); | |
| 355 | key_data = &key[key_offset]; | |
| 356 | } | |
| 357 | ||
| 358 | return crypto_generichash(&out[out_offset], out_len, &in[in_offset], in_len, key_data, key_len); | |
| 359 | } | |
| 360 | ||
| 361 | static inline int | |
| 362 | sn_crypto_generichash_batch( | |
| 363 | js_env_t *env, | |
| 364 | js_receiver_t, | |
| 365 | js_typedarray_t<uint8_t> out, | |
| 366 | std::vector<js_typedarray_span_t<>> batch, | |
| 367 | bool use_key, | |
| 368 | js_typedarray_t<uint8_t> key | |
| 369 | ) { | |
| 370 | int err; | |
| 371 | ||
| 372 | uint8_t *out_data; | |
| 373 | size_t out_len; | |
| 374 | err = js_get_typedarray_info(env, out, out_data, out_len); | |
| 375 | assert(err == 0); | |
| 376 | assert( | |
| 377 | out_len >= crypto_generichash_BYTES_MIN && | |
| 378 | out_len <= crypto_generichash_BYTES_MAX | |
| 379 | ); | |
| 380 | ||
| 381 | uint8_t *key_data = NULL; | |
| 382 | size_t key_len = 0; | |
| 383 | if (use_key) { | |
| 384 | err = js_get_typedarray_info(env, key, key_data, key_len); | |
| 385 | assert(err == 0); | |
| 386 | assert( | |
| 387 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 388 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 389 | ); | |
| 390 | } | |
| 391 | ||
| 392 | crypto_generichash_state state; | |
| 393 | err = crypto_generichash_init(&state, key_data, key_len, out_len); | |
| 394 | if (err != 0) return err; | |
| 395 | ||
| 396 | for (auto &buf : batch) { | |
| 397 | err = crypto_generichash_update(&state, buf.data(), buf.size()); | |
| 398 | if (err != 0) return err; | |
| 399 | } | |
| 400 | ||
| 401 | return crypto_generichash_final(&state, out_data, out_len); | |
| 402 | } | |
| 403 | ||
| 404 | static inline void | |
| 405 | sn_crypto_generichash_keygen( | |
| 406 | js_env_t *env, | |
| 407 | js_receiver_t, | |
| 408 | ||
| 409 | js_arraybuffer_span_t key, | |
| 410 | uint32_t key_offset, | |
| 411 | uint32_t key_len | |
| 412 | ) { | |
| 413 | assert_bounds(key); | |
| 414 | assert(key_len == crypto_generichash_KEYBYTES); | |
| 415 | ||
| 416 | crypto_generichash_keygen(&key[key_offset]); | |
| 417 | } | |
| 418 | ||
| 419 | static inline int | |
| 420 | sn_crypto_generichash_init( | |
| 421 | js_env_t *env, | |
| 422 | js_receiver_t, | |
| 423 | ||
| 424 | js_arraybuffer_span_t state, | |
| 425 | uint32_t state_offset, | |
| 426 | uint32_t state_len, | |
| 427 | ||
| 428 | js_arraybuffer_span_t key, | |
| 429 | uint32_t key_offset, | |
| 430 | uint32_t key_len, | |
| 431 | ||
| 432 | uint32_t out_len | |
| 433 | ) { | |
| 434 | assert_bounds(state); | |
| 435 | ||
| 436 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 437 | ||
| 438 | uint8_t *key_data = NULL; | |
| 439 | if (key_len) { | |
| 440 | assert_bounds(key); | |
| 441 | assert( | |
| 442 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 443 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 444 | ); | |
| 445 | key_data = &key[key_offset]; | |
| 446 | } | |
| 447 | ||
| 448 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 449 | ||
| 450 | return crypto_generichash_init(state_data, key_data, key_len, out_len); | |
| 451 | } | |
| 452 | ||
| 453 | static inline int | |
| 454 | sn_crypto_generichash_update( | |
| 455 | js_env_t *env, | |
| 456 | js_receiver_t, | |
| 457 | ||
| 458 | js_arraybuffer_span_t state, | |
| 459 | uint32_t state_offset, | |
| 460 | uint32_t state_len, | |
| 461 | ||
| 462 | js_arraybuffer_span_t in, | |
| 463 | uint32_t in_offset, | |
| 464 | uint32_t in_len | |
| 465 | ) { | |
| 466 | assert_bounds(state); | |
| 467 | assert_bounds(in); | |
| 468 | ||
| 469 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 470 | ||
| 471 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 472 | ||
| 473 | return crypto_generichash_update(state_data, &in[in_offset], in_len); | |
| 474 | } | |
| 475 | ||
| 476 | static inline int | |
| 477 | sn_crypto_generichash_final( | |
| 478 | js_env_t *env, | |
| 479 | js_receiver_t, | |
| 480 | ||
| 481 | js_arraybuffer_span_t state, | |
| 482 | uint32_t state_offset, | |
| 483 | uint32_t state_len, | |
| 484 | ||
| 485 | js_arraybuffer_span_t out, | |
| 486 | uint32_t out_offset, | |
| 487 | uint32_t out_len | |
| 488 | ) { | |
| 489 | assert_bounds(state); | |
| 490 | assert_bounds(out); | |
| 491 | ||
| 492 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 493 | ||
| 494 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 495 | ||
| 496 | return crypto_generichash_final(state_data, &out[out_offset], out_len); | |
| 497 | } | |
| 498 | ||
| 499 | static inline int | |
| 500 | sn_crypto_box_keypair(js_env_t *, js_receiver_t, js_typedarray_span_t<> pk, js_typedarray_span_t<> sk) { | |
| 501 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 502 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 503 | ||
| 504 | return crypto_box_keypair(pk.data(), sk.data()); | |
| 505 | } | |
| 506 | ||
| 507 | static inline int | |
| 508 | sn_crypto_box_seed_keypair( | |
| 509 | js_env_t *, | |
| 510 | js_receiver_t, | |
| 511 | js_typedarray_span_t<> pk, | |
| 512 | js_typedarray_span_t<> sk, | |
| 513 | js_typedarray_span_t<> seed | |
| 514 | ) { | |
| 515 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 516 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 517 | assert(seed.size_bytes() == crypto_box_SEEDBYTES); | |
| 518 | ||
| 519 | return crypto_box_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 520 | } | |
| 521 | ||
| 522 | static inline int | |
| 523 | sn_crypto_box_easy( | |
| 524 | js_env_t *, | |
| 525 | js_receiver_t, | |
| 526 | js_typedarray_span_t<> c, | |
| 527 | js_typedarray_span_t<> m, | |
| 528 | js_typedarray_span_t<> n, | |
| 529 | js_typedarray_span_t<> pk, | |
| 530 | js_typedarray_span_t<> sk | |
| 531 | ) { | |
| 532 | assert(c.size_bytes() == m.size_bytes() + crypto_box_MACBYTES); | |
| 533 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 534 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 535 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 536 | ||
| 537 | return crypto_box_easy(c.data(), m.data(), m.size_bytes(), n.data(), pk.data(), sk.data()); | |
| 538 | } | |
| 539 | ||
| 540 | static inline bool | |
| 541 | sn_crypto_box_open_easy( | |
| 542 | js_env_t *, | |
| 543 | js_receiver_t, | |
| 544 | js_typedarray_span_t<> m, | |
| 545 | js_typedarray_span_t<> c, | |
| 546 | js_typedarray_span_t<> n, | |
| 547 | js_typedarray_span_t<> pk, | |
| 548 | js_typedarray_span_t<> sk | |
| 549 | ) { | |
| 550 | assert(c.size_bytes() >= crypto_box_MACBYTES); | |
| 551 | assert(m.size_bytes() == c.size_bytes() - crypto_box_MACBYTES); | |
| 552 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 553 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 554 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 555 | ||
| 556 | return crypto_box_open_easy(m.data(), c.data(), c.size_bytes(), n.data(), pk.data(), sk.data()) == 0; | |
| 557 | } | |
| 558 | ||
| 559 | static inline int | |
| 560 | sn_crypto_box_detached( | |
| 561 | js_env_t *, | |
| 562 | js_receiver_t, | |
| 563 | js_typedarray_span_t<> c, | |
| 564 | js_typedarray_span_t<> mac, | |
| 565 | js_typedarray_span_t<> m, | |
| 566 | js_typedarray_span_t<> n, | |
| 567 | js_typedarray_span_t<> pk, | |
| 568 | js_typedarray_span_t<> sk | |
| 569 | ) { | |
| 570 | assert(c.size_bytes() == m.size_bytes()); | |
| 571 | assert(mac.size_bytes() == crypto_box_MACBYTES); | |
| 572 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 573 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 574 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 575 | ||
| 576 | return crypto_box_detached(c.data(), mac.data(), m.data(), m.size_bytes(), n.data(), pk.data(), sk.data()); | |
| 577 | } | |
| 578 | ||
| 579 | static inline bool | |
| 580 | sn_crypto_box_open_detached( | |
| 581 | js_env_t *, | |
| 582 | js_receiver_t, | |
| 583 | js_typedarray_span_t<> m, | |
| 584 | js_typedarray_span_t<> c, | |
| 585 | js_typedarray_span_t<> mac, | |
| 586 | js_typedarray_span_t<> n, | |
| 587 | js_typedarray_span_t<> pk, | |
| 588 | js_typedarray_span_t<> sk | |
| 589 | ) { | |
| 590 | assert(m.size_bytes() == c.size_bytes()); | |
| 591 | assert(mac.size_bytes() == crypto_box_MACBYTES); | |
| 592 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 593 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 594 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 595 | ||
| 596 | return crypto_box_open_detached(m.data(), c.data(), mac.data(), c.size_bytes(), n.data(), pk.data(), sk.data()) == 0; | |
| 597 | } | |
| 598 | ||
| 599 | static inline int | |
| 600 | sn_crypto_box_seal( | |
| 601 | js_env_t *, | |
| 602 | js_receiver_t, | |
| 603 | js_typedarray_span_t<> c, | |
| 604 | js_typedarray_span_t<> m, | |
| 605 | js_typedarray_span_t<> pk | |
| 606 | ) { | |
| 607 | assert(c.size_bytes() == m.size_bytes() + crypto_box_SEALBYTES); | |
| 608 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 609 | ||
| 610 | return crypto_box_seal(c.data(), m.data(), m.size_bytes(), pk.data()); | |
| 611 | } | |
| 612 | ||
| 613 | static inline bool | |
| 614 | sn_crypto_box_seal_open( | |
| 615 | js_env_t *env, | |
| 616 | js_receiver_t, | |
| 617 | ||
| 618 | js_arraybuffer_span_t m, | |
| 619 | uint32_t m_offset, | |
| 620 | uint32_t m_len, | |
| 621 | ||
| 622 | js_arraybuffer_span_t c, | |
| 623 | uint32_t c_offset, | |
| 624 | uint32_t c_len, | |
| 625 | ||
| 626 | js_arraybuffer_span_t pk, | |
| 627 | uint32_t pk_offset, | |
| 628 | uint32_t pk_len, | |
| 629 | ||
| 630 | js_arraybuffer_span_t sk, | |
| 631 | uint32_t sk_offset, | |
| 632 | uint32_t sk_len | |
| 633 | ) { | |
| 634 | assert_bounds(m); | |
| 635 | assert_bounds(c); | |
| 636 | assert_bounds(pk); | |
| 637 | assert_bounds(sk); | |
| 638 | ||
| 639 | assert(m_len == c_len - crypto_box_SEALBYTES); | |
| 640 | assert(c_len >= crypto_box_SEALBYTES); | |
| 641 | assert(sk_len == crypto_box_SECRETKEYBYTES); | |
| 642 | assert(pk_len == crypto_box_PUBLICKEYBYTES); | |
| 643 | ||
| 644 | return crypto_box_seal_open(&m[m_offset], &c[c_offset], c_len, &pk[pk_offset], &sk[sk_offset]) == 0; | |
| 645 | } | |
| 646 | ||
| 647 | static inline int | |
| 648 | sn_crypto_secretbox_easy( | |
| 649 | js_env_t *, | |
| 650 | js_receiver_t, | |
| 651 | js_typedarray_span_t<> c, | |
| 652 | js_typedarray_span_t<> m, | |
| 653 | js_typedarray_span_t<> n, | |
| 654 | js_typedarray_span_t<> k | |
| 655 | ) { | |
| 656 | assert(c.size_bytes() == m.size_bytes() + crypto_secretbox_MACBYTES); | |
| 657 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 658 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 659 | ||
| 660 | return crypto_secretbox_easy(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 661 | } | |
| 662 | ||
| 663 | static inline bool | |
| 664 | sn_crypto_secretbox_open_easy( | |
| 665 | js_env_t *, | |
| 666 | js_receiver_t, | |
| 667 | js_typedarray_span_t<> m, | |
| 668 | js_typedarray_span_t<> c, | |
| 669 | js_typedarray_span_t<> n, | |
| 670 | js_typedarray_span_t<> k | |
| 671 | ) { | |
| 672 | assert(m.size_bytes() == c.size_bytes() - crypto_secretbox_MACBYTES); | |
| 673 | assert(c.size_bytes() >= crypto_secretbox_MACBYTES); | |
| 674 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 675 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 676 | ||
| 677 | return crypto_secretbox_open_easy(m.data(), c.data(), c.size_bytes(), n.data(), k.data()) == 0; | |
| 678 | } | |
| 679 | ||
| 680 | static inline int | |
| 681 | sn_crypto_secretbox_detached( | |
| 682 | js_env_t *, | |
| 683 | js_receiver_t, | |
| 684 | js_typedarray_span_t<> c, | |
| 685 | js_typedarray_span_t<> mac, | |
| 686 | js_typedarray_span_t<> m, | |
| 687 | js_typedarray_span_t<> n, | |
| 688 | js_typedarray_span_t<> k | |
| 689 | ) { | |
| 690 | assert(c.size_bytes() == m.size_bytes()); | |
| 691 | assert(mac.size_bytes() == crypto_secretbox_MACBYTES); | |
| 692 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 693 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 694 | ||
| 695 | return crypto_secretbox_detached(c.data(), mac.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 696 | } | |
| 697 | ||
| 698 | static inline bool | |
| 699 | sn_crypto_secretbox_open_detached( | |
| 700 | js_env_t *, | |
| 701 | js_receiver_t, | |
| 702 | js_typedarray_span_t<> m, | |
| 703 | js_typedarray_span_t<> c, | |
| 704 | js_typedarray_span_t<> mac, | |
| 705 | js_typedarray_span_t<> n, | |
| 706 | js_typedarray_span_t<> k | |
| 707 | ) { | |
| 708 | assert(m.size_bytes() == c.size_bytes()); | |
| 709 | assert(mac.size_bytes() == crypto_secretbox_MACBYTES); | |
| 710 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 711 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 712 | ||
| 713 | return crypto_secretbox_open_detached(m.data(), c.data(), mac.data(), c.size_bytes(), n.data(), k.data()) == 0; | |
| 714 | } | |
| 715 | ||
| 716 | static inline int | |
| 717 | sn_crypto_stream( | |
| 718 | js_env_t *, | |
| 719 | js_receiver_t, | |
| 720 | js_typedarray_span_t<> c, | |
| 721 | js_typedarray_span_t<> n, | |
| 722 | js_typedarray_span_t<> k | |
| 723 | ) { | |
| 724 | assert(n.size_bytes() == crypto_stream_NONCEBYTES); | |
| 725 | assert(k.size_bytes() == crypto_stream_KEYBYTES); | |
| 726 | ||
| 727 | return crypto_stream(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 728 | } | |
| 729 | ||
| 730 | static inline int | |
| 731 | sn_crypto_stream_xor( | |
| 732 | js_env_t *env, | |
| 733 | js_receiver_t, | |
| 734 | ||
| 735 | js_arraybuffer_span_t c, | |
| 736 | uint32_t c_offset, | |
| 737 | uint32_t c_len, | |
| 738 | ||
| 739 | js_arraybuffer_span_t m, | |
| 740 | uint32_t m_offset, | |
| 741 | uint32_t m_len, | |
| 742 | ||
| 743 | js_arraybuffer_span_t n, | |
| 744 | uint32_t n_offset, | |
| 745 | uint32_t n_len, | |
| 746 | ||
| 747 | js_arraybuffer_span_t k, | |
| 748 | uint32_t k_offset, | |
| 749 | uint32_t k_len | |
| 750 | ) { | |
| 751 | assert_bounds(c); | |
| 752 | assert_bounds(m); | |
| 753 | assert_bounds(n); | |
| 754 | assert_bounds(k); | |
| 755 | ||
| 756 | assert(c_len == m_len); | |
| 757 | assert(n_len == crypto_stream_NONCEBYTES); | |
| 758 | assert(k_len == crypto_stream_KEYBYTES); | |
| 759 | ||
| 760 | return crypto_stream_xor(&c[c_offset], &m[m_offset], m_len, &n[n_offset], &k[k_offset]); | |
| 761 | } | |
| 762 | ||
| 763 | static inline int | |
| 764 | sn_crypto_stream_chacha20( | |
| 765 | js_env_t *, | |
| 766 | js_receiver_t, | |
| 767 | js_typedarray_span_t<> c, | |
| 768 | js_typedarray_span_t<> n, | |
| 769 | js_typedarray_span_t<> k | |
| 770 | ) { | |
| 771 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 772 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 773 | ||
| 774 | return crypto_stream_chacha20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 775 | } | |
| 776 | ||
| 777 | static inline int | |
| 778 | sn_crypto_stream_chacha20_xor( | |
| 779 | js_env_t *, | |
| 780 | js_receiver_t, | |
| 781 | js_typedarray_span_t<> c, | |
| 782 | js_typedarray_span_t<> m, | |
| 783 | js_typedarray_span_t<> n, | |
| 784 | js_typedarray_span_t<> k | |
| 785 | ) { | |
| 786 | assert(c.size_bytes() == m.size_bytes()); | |
| 787 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 788 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 789 | ||
| 790 | return crypto_stream_chacha20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 791 | } | |
| 792 | ||
| 793 | static inline int | |
| 794 | sn_crypto_stream_chacha20_xor_ic( | |
| 795 | js_env_t *, | |
| 796 | js_receiver_t, | |
| 797 | js_typedarray_span_t<> c, | |
| 798 | js_typedarray_span_t<> m, | |
| 799 | js_typedarray_span_t<> n, | |
| 800 | uint32_t ic, | |
| 801 | js_typedarray_span_t<> k | |
| 802 | ) { | |
| 803 | assert(c.size_bytes() == m.size_bytes()); | |
| 804 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 805 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 806 | ||
| 807 | return crypto_stream_chacha20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 808 | } | |
| 809 | ||
| 810 | static inline int | |
| 811 | sn_crypto_stream_chacha20_ietf( | |
| 812 | js_env_t *, | |
| 813 | js_receiver_t, | |
| 814 | js_typedarray_span_t<> c, | |
| 815 | js_typedarray_span_t<> n, | |
| 816 | js_typedarray_span_t<> k | |
| 817 | ) { | |
| 818 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 819 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 820 | ||
| 821 | return crypto_stream_chacha20_ietf(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 822 | } | |
| 823 | ||
| 824 | static inline int | |
| 825 | sn_crypto_stream_chacha20_ietf_xor( | |
| 826 | js_env_t *, | |
| 827 | js_receiver_t, | |
| 828 | js_typedarray_span_t<> c, | |
| 829 | js_typedarray_span_t<> m, | |
| 830 | js_typedarray_span_t<> n, | |
| 831 | js_typedarray_span_t<> k | |
| 832 | ) { | |
| 833 | assert(c.size_bytes() == m.size_bytes()); | |
| 834 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 835 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 836 | ||
| 837 | return crypto_stream_chacha20_ietf_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 838 | } | |
| 839 | ||
| 840 | static inline int | |
| 841 | sn_crypto_stream_chacha20_ietf_xor_ic( | |
| 842 | js_env_t *, | |
| 843 | js_receiver_t, | |
| 844 | js_typedarray_span_t<> c, | |
| 845 | js_typedarray_span_t<> m, | |
| 846 | js_typedarray_span_t<> n, | |
| 847 | uint32_t ic, | |
| 848 | js_typedarray_span_t<> k | |
| 849 | ) { | |
| 850 | assert(c.size_bytes() == m.size_bytes()); | |
| 851 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 852 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 853 | ||
| 854 | return crypto_stream_chacha20_ietf_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 855 | } | |
| 856 | ||
| 857 | static inline int | |
| 858 | sn_crypto_stream_xchacha20( | |
| 859 | js_env_t *, | |
| 860 | js_receiver_t, | |
| 861 | js_typedarray_span_t<> c, | |
| 862 | js_typedarray_span_t<> n, | |
| 863 | js_typedarray_span_t<> k | |
| 864 | ) { | |
| 865 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 866 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 867 | ||
| 868 | return crypto_stream_xchacha20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 869 | } | |
| 870 | ||
| 871 | static inline int | |
| 872 | sn_crypto_stream_xchacha20_xor( | |
| 873 | js_env_t *, | |
| 874 | js_receiver_t, | |
| 875 | js_typedarray_span_t<> c, | |
| 876 | js_typedarray_span_t<> m, | |
| 877 | js_typedarray_span_t<> n, | |
| 878 | js_typedarray_span_t<> k | |
| 879 | ) { | |
| 880 | assert(c.size_bytes() == m.size_bytes()); | |
| 881 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 882 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 883 | ||
| 884 | return crypto_stream_xchacha20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 885 | } | |
| 886 | ||
| 887 | static inline int | |
| 888 | sn_crypto_stream_xchacha20_xor_ic( | |
| 889 | js_env_t *, | |
| 890 | js_receiver_t, | |
| 891 | js_typedarray_span_t<> c, | |
| 892 | js_typedarray_span_t<> m, | |
| 893 | js_typedarray_span_t<> n, | |
| 894 | uint32_t ic, | |
| 895 | js_typedarray_span_t<> k | |
| 896 | ) { | |
| 897 | assert(c.size_bytes() == m.size_bytes()); | |
| 898 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 899 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 900 | ||
| 901 | return crypto_stream_xchacha20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 902 | } | |
| 903 | ||
| 904 | static inline int | |
| 905 | sn_crypto_stream_salsa20( | |
| 906 | js_env_t *, | |
| 907 | js_receiver_t, | |
| 908 | js_typedarray_span_t<> c, | |
| 909 | js_typedarray_span_t<> n, | |
| 910 | js_typedarray_span_t<> k | |
| 911 | ) { | |
| 912 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 913 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 914 | ||
| 915 | return crypto_stream_salsa20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 916 | } | |
| 917 | ||
| 918 | static inline int | |
| 919 | sn_crypto_stream_salsa20_xor( | |
| 920 | js_env_t *, | |
| 921 | js_receiver_t, | |
| 922 | js_typedarray_span_t<> c, | |
| 923 | js_typedarray_span_t<> m, | |
| 924 | js_typedarray_span_t<> n, | |
| 925 | js_typedarray_span_t<> k | |
| 926 | ) { | |
| 927 | assert(c.size_bytes() == m.size_bytes()); | |
| 928 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 929 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 930 | ||
| 931 | return crypto_stream_salsa20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 932 | } | |
| 933 | ||
| 934 | static inline int | |
| 935 | sn_crypto_stream_salsa20_xor_ic( | |
| 936 | js_env_t *, | |
| 937 | js_receiver_t, | |
| 938 | js_typedarray_span_t<> c, | |
| 939 | js_typedarray_span_t<> m, | |
| 940 | js_typedarray_span_t<> n, | |
| 941 | uint32_t ic, | |
| 942 | js_typedarray_span_t<> k | |
| 943 | ) { | |
| 944 | assert(c.size_bytes() == m.size_bytes()); | |
| 945 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 946 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 947 | ||
| 948 | return crypto_stream_salsa20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 949 | } | |
| 950 | ||
| 951 | static inline int | |
| 952 | sn_crypto_auth( | |
| 953 | js_env_t *, | |
| 954 | js_receiver_t, | |
| 955 | js_typedarray_span_t<> out, | |
| 956 | js_typedarray_span_t<> in, | |
| 957 | js_typedarray_span_t<> k | |
| 958 | ) { | |
| 959 | assert(out.size_bytes() == crypto_auth_BYTES); | |
| 960 | assert(k.size_bytes() == crypto_auth_KEYBYTES); | |
| 961 | ||
| 962 | return crypto_auth(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 963 | } | |
| 964 | ||
| 965 | static inline bool | |
| 966 | sn_crypto_auth_verify( | |
| 967 | js_env_t *, | |
| 968 | js_receiver_t, | |
| 969 | js_typedarray_span_t<> h, | |
| 970 | js_typedarray_span_t<> in, | |
| 971 | js_typedarray_span_t<> k | |
| 972 | ) { | |
| 973 | assert(h.size_bytes() == crypto_auth_BYTES); | |
| 974 | assert(k.size_bytes() == crypto_auth_KEYBYTES); | |
| 975 | ||
| 976 | return crypto_auth_verify(h.data(), in.data(), in.size_bytes(), k.data()) == 0; | |
| 977 | } | |
| 978 | ||
| 979 | static inline int | |
| 980 | sn_crypto_onetimeauth( | |
| 981 | js_env_t *, | |
| 982 | js_receiver_t, | |
| 983 | js_typedarray_span_t<> out, | |
| 984 | js_typedarray_span_t<> in, | |
| 985 | js_typedarray_span_t<> k | |
| 986 | ) { | |
| 987 | assert(out.size_bytes() == crypto_onetimeauth_BYTES); | |
| 988 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 989 | ||
| 990 | return crypto_onetimeauth(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 991 | } | |
| 992 | ||
| 993 | static inline int | |
| 994 | sn_crypto_onetimeauth_init( | |
| 995 | js_env_t *, | |
| 996 | js_receiver_t, | |
| 997 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 998 | js_typedarray_span_t<> k | |
| 999 | ) { | |
| 1000 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 1001 | ||
| 1002 | return crypto_onetimeauth_init(&*state, k.data()); | |
| 1003 | } | |
| 1004 | ||
| 1005 | static inline int | |
| 1006 | sn_crypto_onetimeauth_update( | |
| 1007 | js_env_t *, | |
| 1008 | js_receiver_t, | |
| 1009 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 1010 | js_typedarray_span_t<> in | |
| 1011 | ) { | |
| 1012 | return crypto_onetimeauth_update(&*state, in.data(), in.size_bytes()); | |
| 1013 | } | |
| 1014 | ||
| 1015 | static inline int | |
| 1016 | sn_crypto_onetimeauth_final( | |
| 1017 | js_env_t *, | |
| 1018 | js_receiver_t, | |
| 1019 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 1020 | js_typedarray_span_t<> out | |
| 1021 | ) { | |
| 1022 | assert(out.size_bytes() == crypto_onetimeauth_BYTES); | |
| 1023 | ||
| 1024 | return crypto_onetimeauth_final(&*state, out.data()); | |
| 1025 | } | |
| 1026 | ||
| 1027 | static inline bool | |
| 1028 | sn_crypto_onetimeauth_verify( | |
| 1029 | js_env_t *, | |
| 1030 | js_receiver_t, | |
| 1031 | js_typedarray_span_t<> h, | |
| 1032 | js_typedarray_span_t<> in, | |
| 1033 | js_typedarray_span_t<> k | |
| 1034 | ) { | |
| 1035 | assert(h.size_bytes() == crypto_onetimeauth_BYTES); | |
| 1036 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 1037 | ||
| 1038 | return crypto_onetimeauth_verify(h.data(), in.data(), in.size_bytes(), k.data()) == 0; | |
| 1039 | } | |
| 1040 | ||
| 1041 | // CHECK: memlimit can be >32bit | |
| 1042 | static inline int | |
| 1043 | sn_crypto_pwhash( | |
| 1044 | js_env_t *, | |
| 1045 | js_receiver_t, | |
| 1046 | js_typedarray_span_t<> out, | |
| 1047 | js_typedarray_span_of_t<char> passwd, | |
| 1048 | js_typedarray_span_t<> salt, | |
| 1049 | uint64_t opslimit, | |
| 1050 | uint64_t memlimit, | |
| 1051 | int32_t alg | |
| 1052 | ) { | |
| 1053 | assert(out.size_bytes() >= crypto_pwhash_BYTES_MIN); | |
| 1054 | assert(out.size_bytes() <= crypto_pwhash_BYTES_MAX); | |
| 1055 | assert(salt.size_bytes() == crypto_pwhash_SALTBYTES); | |
| 1056 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1057 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1058 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1059 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1060 | assert(alg == 1 || alg == 2); // Argon2i or Argon2id | |
| 1061 | ||
| 1062 | return crypto_pwhash( | |
| 1063 | out.data(), | |
| 1064 | out.size_bytes(), | |
| 1065 | passwd.data(), | |
| 1066 | passwd.size_bytes(), | |
| 1067 | salt.data(), | |
| 1068 | opslimit, | |
| 1069 | memlimit, | |
| 1070 | alg | |
| 1071 | ); | |
| 1072 | } | |
| 1073 | ||
| 1074 | static inline int | |
| 1075 | sn_crypto_pwhash_str( | |
| 1076 | js_env_t *, | |
| 1077 | js_receiver_t, | |
| 1078 | js_typedarray_span_of_t<char> out, | |
| 1079 | js_typedarray_span_of_t<char> passwd, | |
| 1080 | uint64_t opslimit, | |
| 1081 | uint64_t memlimit | |
| 1082 | ) { | |
| 1083 | assert(out.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1084 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1085 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1086 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1087 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1088 | ||
| 1089 | return crypto_pwhash_str( | |
| 1090 | out.data(), | |
| 1091 | passwd.data(), | |
| 1092 | passwd.size_bytes(), | |
| 1093 | opslimit, | |
| 1094 | memlimit | |
| 1095 | ); | |
| 1096 | } | |
| 1097 | ||
| 1098 | static inline bool | |
| 1099 | sn_crypto_pwhash_str_verify( | |
| 1100 | js_env_t *, | |
| 1101 | js_receiver_t, | |
| 1102 | js_typedarray_span_of_t<char> str, | |
| 1103 | js_typedarray_span_of_t<char> passwd | |
| 1104 | ) { | |
| 1105 | assert(str.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1106 | ||
| 1107 | return crypto_pwhash_str_verify(str.data(), passwd.data(), passwd.size_bytes()) == 0; | |
| 1108 | } | |
| 1109 | ||
| 1110 | // CHECK: returns 1, 0, -1 | |
| 1111 | static inline bool | |
| 1112 | sn_crypto_pwhash_str_needs_rehash( | |
| 1113 | js_env_t *, | |
| 1114 | js_receiver_t, | |
| 1115 | js_typedarray_span_of_t<char> str, | |
| 1116 | uint64_t opslimit, | |
| 1117 | uint64_t memlimit | |
| 1118 | ) { | |
| 1119 | assert(str.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1120 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1121 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1122 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1123 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1124 | ||
| 1125 | return crypto_pwhash_str_needs_rehash(str.data(), opslimit, memlimit) != 0; | |
| 1126 | } | |
| 1127 | ||
| 1128 | // CHECK: memlimit can be >32bit | |
| 1129 | static inline int | |
| 1130 | sn_crypto_pwhash_scryptsalsa208sha256( | |
| 1131 | js_env_t *, | |
| 1132 | js_receiver_t, | |
| 1133 | js_typedarray_span_t<> out, | |
| 1134 | js_typedarray_span_of_t<char> passwd, | |
| 1135 | js_typedarray_span_t<> salt, | |
| 1136 | uint64_t opslimit, | |
| 1137 | uint64_t memlimit | |
| 1138 | ) { | |
| 1139 | assert(out.size_bytes() >= crypto_pwhash_scryptsalsa208sha256_BYTES_MIN); | |
| 1140 | assert(out.size_bytes() <= crypto_pwhash_scryptsalsa208sha256_BYTES_MAX); | |
| 1141 | assert(salt.size_bytes() == crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 1142 | assert(opslimit >= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 1143 | assert(opslimit <= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 1144 | assert(memlimit >= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 1145 | assert(memlimit <= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 1146 | ||
| 1147 | return crypto_pwhash_scryptsalsa208sha256( | |
| 1148 | out.data(), | |
| 1149 | out.size_bytes(), | |
| 1150 | passwd.data(), | |
| 1151 | passwd.size_bytes(), | |
| 1152 | salt.data(), | |
| 1153 | opslimit, | |
| 1154 | memlimit | |
| 1155 | ); | |
| 1156 | } | |
| 1157 | ||
| 1158 | static inline int | |
| 1159 | sn_crypto_pwhash_scryptsalsa208sha256_str( | |
| 1160 | js_env_t *, | |
| 1161 | js_receiver_t, | |
| 1162 | js_typedarray_span_of_t<char> out, | |
| 1163 | js_typedarray_span_of_t<char> passwd, | |
| 1164 | uint64_t opslimit, | |
| 1165 | uint64_t memlimit | |
| 1166 | ) { | |
| 1167 | assert(out.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1168 | assert(opslimit >= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 1169 | assert(opslimit <= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 1170 | assert(memlimit >= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 1171 | assert(memlimit <= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 1172 | ||
| 1173 | return crypto_pwhash_scryptsalsa208sha256_str( | |
| 1174 | out.data(), | |
| 1175 | passwd.data(), | |
| 1176 | passwd.size_bytes(), | |
| 1177 | opslimit, | |
| 1178 | memlimit | |
| 1179 | ); | |
| 1180 | } | |
| 1181 | ||
| 1182 | static inline bool | |
| 1183 | sn_crypto_pwhash_scryptsalsa208sha256_str_verify( | |
| 1184 | js_env_t *, | |
| 1185 | js_receiver_t, | |
| 1186 | js_typedarray_span_of_t<char> str, | |
| 1187 | js_typedarray_span_of_t<char> passwd | |
| 1188 | ) { | |
| 1189 | assert(str.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1190 | ||
| 1191 | return crypto_pwhash_scryptsalsa208sha256_str_verify(str.data(), passwd.data(), passwd.size_bytes()) == 0; | |
| 1192 | } | |
| 1193 | ||
| 1194 | static inline bool | |
| 1195 | sn_crypto_pwhash_scryptsalsa208sha256_str_needs_rehash( | |
| 1196 | js_env_t *, | |
| 1197 | js_receiver_t, | |
| 1198 | js_typedarray_span_of_t<char> str, | |
| 1199 | uint64_t opslimit, | |
| 1200 | uint64_t memlimit | |
| 1201 | ) { | |
| 1202 | assert(str.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1203 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1204 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1205 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1206 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1207 | ||
| 1208 | return crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(str.data(), opslimit, memlimit) != 0; | |
| 1209 | } | |
| 1210 | ||
| 1211 | static inline int | |
| 1212 | sn_crypto_kx_keypair( | |
| 1213 | js_env_t *, | |
| 1214 | js_receiver_t, | |
| 1215 | js_typedarray_span_t<> pk, | |
| 1216 | js_typedarray_span_t<> sk | |
| 1217 | ) { | |
| 1218 | assert(pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1219 | assert(sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1220 | ||
| 1221 | return crypto_kx_keypair(pk.data(), sk.data()); | |
| 1222 | } | |
| 1223 | ||
| 1224 | static inline int | |
| 1225 | sn_crypto_kx_seed_keypair( | |
| 1226 | js_env_t *, | |
| 1227 | js_receiver_t, | |
| 1228 | js_typedarray_span_t<> pk, | |
| 1229 | js_typedarray_span_t<> sk, | |
| 1230 | js_typedarray_span_t<> seed | |
| 1231 | ) { | |
| 1232 | assert(pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1233 | assert(sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1234 | assert(seed.size_bytes() == crypto_kx_SEEDBYTES); | |
| 1235 | ||
| 1236 | return crypto_kx_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 1237 | } | |
| 1238 | ||
| 1239 | static inline int | |
| 1240 | sn_crypto_kx_client_session_keys( | |
| 1241 | js_env_t *, | |
| 1242 | js_receiver_t, | |
| 1243 | std::optional<js_typedarray_span_t<>> rx, | |
| 1244 | std::optional<js_typedarray_span_t<>> tx, | |
| 1245 | js_typedarray_span_t<> client_pk, | |
| 1246 | js_typedarray_span_t<> client_sk, | |
| 1247 | js_typedarray_span_t<> server_pk | |
| 1248 | ) { | |
| 1249 | assert(rx.has_value() || tx.has_value()); | |
| 1250 | ||
| 1251 | if (rx) assert(rx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1252 | if (tx) assert(tx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1253 | ||
| 1254 | assert(client_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1255 | assert(client_sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1256 | assert(server_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1257 | ||
| 1258 | return crypto_kx_client_session_keys( | |
| 1259 | rx ? rx->data() : nullptr, | |
| 1260 | tx ? tx->data() : nullptr, | |
| 1261 | client_pk.data(), | |
| 1262 | client_sk.data(), | |
| 1263 | server_pk.data() | |
| 1264 | ); | |
| 1265 | } | |
| 1266 | ||
| 1267 | static inline int | |
| 1268 | sn_crypto_kx_server_session_keys( | |
| 1269 | js_env_t *, | |
| 1270 | js_receiver_t, | |
| 1271 | std::optional<js_typedarray_span_t<>> rx, | |
| 1272 | std::optional<js_typedarray_span_t<>> tx, | |
| 1273 | js_typedarray_span_t<> server_pk, | |
| 1274 | js_typedarray_span_t<> server_sk, | |
| 1275 | js_typedarray_span_t<> client_pk | |
| 1276 | ) { | |
| 1277 | assert(rx.has_value() || tx.has_value()); | |
| 1278 | ||
| 1279 | if (rx) assert(rx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1280 | if (tx) assert(tx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1281 | ||
| 1282 | assert(server_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1283 | assert(server_sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1284 | assert(client_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1285 | ||
| 1286 | return crypto_kx_server_session_keys( | |
| 1287 | rx ? rx->data() : nullptr, | |
| 1288 | tx ? tx->data() : nullptr, | |
| 1289 | server_pk.data(), | |
| 1290 | server_sk.data(), | |
| 1291 | client_pk.data() | |
| 1292 | ); | |
| 1293 | } | |
| 1294 | ||
| 1295 | static inline int | |
| 1296 | sn_crypto_scalarmult_base( | |
| 1297 | js_env_t *, | |
| 1298 | js_receiver_t, | |
| 1299 | js_typedarray_span_t<> q, | |
| 1300 | js_typedarray_span_t<> n | |
| 1301 | ) { | |
| 1302 | assert(q.size_bytes() == crypto_scalarmult_BYTES); | |
| 1303 | assert(n.size_bytes() == crypto_scalarmult_SCALARBYTES); | |
| 1304 | ||
| 1305 | return crypto_scalarmult_base(q.data(), n.data()); | |
| 1306 | } | |
| 1307 | ||
| 1308 | static inline int | |
| 1309 | sn_crypto_scalarmult( | |
| 1310 | js_env_t *, | |
| 1311 | js_receiver_t, | |
| 1312 | js_typedarray_span_t<> q, | |
| 1313 | js_typedarray_span_t<> n, | |
| 1314 | js_typedarray_span_t<> p | |
| 1315 | ) { | |
| 1316 | assert(q.size_bytes() == crypto_scalarmult_BYTES); | |
| 1317 | assert(n.size_bytes() == crypto_scalarmult_SCALARBYTES); | |
| 1318 | assert(p.size_bytes() == crypto_scalarmult_BYTES); | |
| 1319 | ||
| 1320 | return crypto_scalarmult(q.data(), n.data(), p.data()); | |
| 1321 | } | |
| 1322 | ||
| 1323 | static inline int | |
| 1324 | sn_crypto_scalarmult_ed25519_base( | |
| 1325 | js_env_t *, | |
| 1326 | js_receiver_t, | |
| 1327 | js_typedarray_span_t<> q, | |
| 1328 | js_typedarray_span_t<> n | |
| 1329 | ) { | |
| 1330 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1331 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1332 | ||
| 1333 | return crypto_scalarmult_ed25519_base(q.data(), n.data()); | |
| 1334 | } | |
| 1335 | ||
| 1336 | static inline int | |
| 1337 | sn_crypto_scalarmult_ed25519( | |
| 1338 | js_env_t *, | |
| 1339 | js_receiver_t, | |
| 1340 | js_typedarray_span_t<> q, | |
| 1341 | js_typedarray_span_t<> n, | |
| 1342 | js_typedarray_span_t<> p | |
| 1343 | ) { | |
| 1344 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1345 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1346 | assert(p.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1347 | ||
| 1348 | return crypto_scalarmult_ed25519(q.data(), n.data(), p.data()); | |
| 1349 | } | |
| 1350 | ||
| 1351 | static inline bool | |
| 1352 | sn_crypto_core_ed25519_is_valid_point( | |
| 1353 | js_env_t *, | |
| 1354 | js_receiver_t, | |
| 1355 | js_typedarray_span_t<> p | |
| 1356 | ) { | |
| 1357 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1358 | ||
| 1359 | return crypto_core_ed25519_is_valid_point(p.data()) != 0; | |
| 1360 | } | |
| 1361 | ||
| 1362 | static inline int | |
| 1363 | sn_crypto_core_ed25519_from_uniform( | |
| 1364 | js_env_t *, | |
| 1365 | js_receiver_t, | |
| 1366 | js_typedarray_span_t<> p, | |
| 1367 | js_typedarray_span_t<> r | |
| 1368 | ) { | |
| 1369 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1370 | assert(r.size_bytes() == crypto_core_ed25519_UNIFORMBYTES); | |
| 1371 | ||
| 1372 | return crypto_core_ed25519_from_uniform(p.data(), r.data()); | |
| 1373 | } | |
| 1374 | ||
| 1375 | static inline int | |
| 1376 | sn_crypto_scalarmult_ed25519_base_noclamp( | |
| 1377 | js_env_t *, | |
| 1378 | js_receiver_t, | |
| 1379 | js_typedarray_span_t<> q, | |
| 1380 | js_typedarray_span_t<> n | |
| 1381 | ) { | |
| 1382 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1383 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1384 | ||
| 1385 | return crypto_scalarmult_ed25519_base_noclamp(q.data(), n.data()); | |
| 1386 | } | |
| 1387 | ||
| 1388 | static inline int | |
| 1389 | sn_crypto_scalarmult_ed25519_noclamp( | |
| 1390 | js_env_t *, | |
| 1391 | js_receiver_t, | |
| 1392 | js_typedarray_span_t<> q, | |
| 1393 | js_typedarray_span_t<> n, | |
| 1394 | js_typedarray_span_t<> p | |
| 1395 | ) { | |
| 1396 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1397 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1398 | assert(p.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1399 | ||
| 1400 | return crypto_scalarmult_ed25519_noclamp(q.data(), n.data(), p.data()); | |
| 1401 | } | |
| 1402 | ||
| 1403 | static inline int | |
| 1404 | sn_crypto_core_ed25519_add( | |
| 1405 | js_env_t *, | |
| 1406 | js_receiver_t, | |
| 1407 | js_typedarray_span_t<> r, | |
| 1408 | js_typedarray_span_t<> p, | |
| 1409 | js_typedarray_span_t<> q | |
| 1410 | ) { | |
| 1411 | assert(r.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1412 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1413 | assert(q.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1414 | ||
| 1415 | return crypto_core_ed25519_add(r.data(), p.data(), q.data()); | |
| 1416 | } | |
| 1417 | ||
| 1418 | static inline int | |
| 1419 | sn_crypto_core_ed25519_sub( | |
| 1420 | js_env_t *, | |
| 1421 | js_receiver_t, | |
| 1422 | js_typedarray_span_t<> r, | |
| 1423 | js_typedarray_span_t<> p, | |
| 1424 | js_typedarray_span_t<> q | |
| 1425 | ) { | |
| 1426 | assert(r.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1427 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1428 | assert(q.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1429 | ||
| 1430 | return crypto_core_ed25519_sub(r.data(), p.data(), q.data()); | |
| 1431 | } | |
| 1432 | ||
| 1433 | static inline void | |
| 1434 | sn_crypto_core_ed25519_scalar_random( | |
| 1435 | js_env_t *, | |
| 1436 | js_receiver_t, | |
| 1437 | js_typedarray_span_t<> r | |
| 1438 | ) { | |
| 1439 | assert(r.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1440 | ||
| 1441 | crypto_core_ed25519_scalar_random(r.data()); | |
| 1442 | } | |
| 1443 | ||
| 1444 | static inline void | |
| 1445 | sn_crypto_core_ed25519_scalar_reduce( | |
| 1446 | js_env_t *, | |
| 1447 | js_receiver_t, | |
| 1448 | js_typedarray_span_t<> r, | |
| 1449 | js_typedarray_span_t<> s | |
| 1450 | ) { | |
| 1451 | assert(r.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1452 | assert(s.size_bytes() == crypto_core_ed25519_NONREDUCEDSCALARBYTES); | |
| 1453 | ||
| 1454 | crypto_core_ed25519_scalar_reduce(r.data(), s.data()); | |
| 1455 | } | |
| 1456 | ||
| 1457 | static inline void | |
| 1458 | sn_crypto_core_ed25519_scalar_invert( | |
| 1459 | js_env_t *, | |
| 1460 | js_receiver_t, | |
| 1461 | js_typedarray_span_t<> recip, | |
| 1462 | js_typedarray_span_t<> s | |
| 1463 | ) { | |
| 1464 | assert(recip.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1465 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1466 | ||
| 1467 | crypto_core_ed25519_scalar_invert(recip.data(), s.data()); | |
| 1468 | } | |
| 1469 | ||
| 1470 | static inline void | |
| 1471 | sn_crypto_core_ed25519_scalar_negate( | |
| 1472 | js_env_t *, | |
| 1473 | js_receiver_t, | |
| 1474 | js_typedarray_span_t<> neg, | |
| 1475 | js_typedarray_span_t<> s | |
| 1476 | ) { | |
| 1477 | assert(neg.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1478 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1479 | ||
| 1480 | crypto_core_ed25519_scalar_negate(neg.data(), s.data()); | |
| 1481 | } | |
| 1482 | ||
| 1483 | static inline void | |
| 1484 | sn_crypto_core_ed25519_scalar_complement( | |
| 1485 | js_env_t *, | |
| 1486 | js_receiver_t, | |
| 1487 | js_typedarray_span_t<> comp, | |
| 1488 | js_typedarray_span_t<> s | |
| 1489 | ) { | |
| 1490 | assert(comp.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1491 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1492 | ||
| 1493 | crypto_core_ed25519_scalar_complement(comp.data(), s.data()); | |
| 1494 | } | |
| 1495 | ||
| 1496 | static inline void | |
| 1497 | sn_crypto_core_ed25519_scalar_add( | |
| 1498 | js_env_t *, | |
| 1499 | js_receiver_t, | |
| 1500 | js_typedarray_span_t<> z, | |
| 1501 | js_typedarray_span_t<> x, | |
| 1502 | js_typedarray_span_t<> y | |
| 1503 | ) { | |
| 1504 | assert(z.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1505 | assert(x.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1506 | assert(y.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1507 | ||
| 1508 | crypto_core_ed25519_scalar_add(z.data(), x.data(), y.data()); | |
| 1509 | } | |
| 1510 | ||
| 1511 | static inline void | |
| 1512 | sn_crypto_core_ed25519_scalar_sub( | |
| 1513 | js_env_t *, | |
| 1514 | js_receiver_t, | |
| 1515 | js_typedarray_span_t<> z, | |
| 1516 | js_typedarray_span_t<> x, | |
| 1517 | js_typedarray_span_t<> y | |
| 1518 | ) { | |
| 1519 | assert(z.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1520 | assert(x.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1521 | assert(y.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1522 | ||
| 1523 | crypto_core_ed25519_scalar_sub(z.data(), x.data(), y.data()); | |
| 1524 | } | |
| 1525 | ||
| 1526 | static inline int | |
| 1527 | sn_crypto_shorthash( | |
| 1528 | js_env_t *, | |
| 1529 | js_receiver_t, | |
| 1530 | js_typedarray_span_t<> out, | |
| 1531 | js_typedarray_span_t<> in, | |
| 1532 | js_typedarray_span_t<> k | |
| 1533 | ) { | |
| 1534 | assert(out.size_bytes() == crypto_shorthash_BYTES); | |
| 1535 | assert(k.size_bytes() == crypto_shorthash_KEYBYTES); | |
| 1536 | ||
| 1537 | return crypto_shorthash(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 1538 | } | |
| 1539 | ||
| 1540 | static inline void | |
| 1541 | sn_crypto_kdf_keygen( | |
| 1542 | js_env_t *, | |
| 1543 | js_receiver_t, | |
| 1544 | js_typedarray_span_t<> key | |
| 1545 | ) { | |
| 1546 | assert(key.size_bytes() == crypto_kdf_KEYBYTES); | |
| 1547 | ||
| 1548 | crypto_kdf_keygen(key.data()); | |
| 1549 | } | |
| 1550 | ||
| 1551 | static inline int | |
| 1552 | sn_crypto_kdf_derive_from_key( | |
| 1553 | js_env_t *, | |
| 1554 | js_receiver_t, | |
| 1555 | js_typedarray_span_t<> subkey, | |
| 1556 | uint64_t subkey_id, | |
| 1557 | js_typedarray_span_of_t<char> ctx, | |
| 1558 | js_typedarray_span_t<> key | |
| 1559 | ) { | |
| 1560 | assert(subkey.size_bytes() >= crypto_kdf_BYTES_MIN); | |
| 1561 | assert(subkey.size_bytes() <= crypto_kdf_BYTES_MAX); | |
| 1562 | assert(ctx.size_bytes() == crypto_kdf_CONTEXTBYTES); | |
| 1563 | assert(key.size_bytes() == crypto_kdf_KEYBYTES); | |
| 1564 | ||
| 1565 | return crypto_kdf_derive_from_key( | |
| 1566 | subkey.data(), | |
| 1567 | subkey.size_bytes(), | |
| 1568 | subkey_id, | |
| 1569 | ctx.data(), | |
| 1570 | key.data() | |
| 1571 | ); | |
| 1572 | } | |
| 1573 | ||
| 1574 | static inline int | |
| 1575 | sn_crypto_hash( | |
| 1576 | js_env_t *, | |
| 1577 | js_receiver_t, | |
| 1578 | js_typedarray_span_t<> out, | |
| 1579 | js_typedarray_span_t<> in | |
| 1580 | ) { | |
| 1581 | assert(out.size_bytes() == crypto_hash_BYTES); | |
| 1582 | ||
| 1583 | return crypto_hash(out.data(), in.data(), in.size_bytes()); | |
| 1584 | } | |
| 1585 | ||
| 1586 | static inline int | |
| 1587 | sn_crypto_hash_sha256( | |
| 1588 | js_env_t *, | |
| 1589 | js_receiver_t, | |
| 1590 | js_typedarray_span_t<> out, | |
| 1591 | js_typedarray_span_t<> in | |
| 1592 | ) { | |
| 1593 | assert(out.size_bytes() == crypto_hash_sha256_BYTES); | |
| 1594 | ||
| 1595 | return crypto_hash_sha256(out.data(), in.data(), in.size_bytes()); | |
| 1596 | } | |
| 1597 | ||
| 1598 | static inline int | |
| 1599 | sn_crypto_hash_sha256_init( | |
| 1600 | js_env_t *, | |
| 1601 | js_receiver_t, | |
| 1602 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state | |
| 1603 | ) { | |
| 1604 | return crypto_hash_sha256_init(&*state); | |
| 1605 | } | |
| 1606 | ||
| 1607 | static inline int | |
| 1608 | sn_crypto_hash_sha256_update( | |
| 1609 | js_env_t *, | |
| 1610 | js_receiver_t, | |
| 1611 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state, | |
| 1612 | js_typedarray_span_t<> in | |
| 1613 | ) { | |
| 1614 | return crypto_hash_sha256_update(&*state, in.data(), in.size_bytes()); | |
| 1615 | } | |
| 1616 | ||
| 1617 | static inline int | |
| 1618 | sn_crypto_hash_sha256_final( | |
| 1619 | js_env_t *, | |
| 1620 | js_receiver_t, | |
| 1621 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state, | |
| 1622 | js_typedarray_span_t<> out | |
| 1623 | ) { | |
| 1624 | assert(out.size_bytes() == crypto_hash_sha256_BYTES); | |
| 1625 | ||
| 1626 | return crypto_hash_sha256_final(&*state, out.data()); | |
| 1627 | } | |
| 1628 | ||
| 1629 | static inline int | |
| 1630 | sn_crypto_hash_sha512( | |
| 1631 | js_env_t *, | |
| 1632 | js_receiver_t, | |
| 1633 | js_typedarray_span_t<> out, | |
| 1634 | js_typedarray_span_t<> in | |
| 1635 | ) { | |
| 1636 | assert(out.size_bytes() == crypto_hash_sha512_BYTES); | |
| 1637 | ||
| 1638 | return crypto_hash_sha512(out.data(), in.data(), in.size_bytes()); | |
| 1639 | } | |
| 1640 | ||
| 1641 | static inline int | |
| 1642 | sn_crypto_hash_sha512_init( | |
| 1643 | js_env_t *, | |
| 1644 | js_receiver_t, | |
| 1645 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state | |
| 1646 | ) { | |
| 1647 | return crypto_hash_sha512_init(&*state); | |
| 1648 | } | |
| 1649 | ||
| 1650 | static inline int | |
| 1651 | sn_crypto_hash_sha512_update( | |
| 1652 | js_env_t *, | |
| 1653 | js_receiver_t, | |
| 1654 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state, | |
| 1655 | js_typedarray_span_t<> in | |
| 1656 | ) { | |
| 1657 | return crypto_hash_sha512_update(&*state, in.data(), in.size_bytes()); | |
| 1658 | } | |
| 1659 | ||
| 1660 | static inline int | |
| 1661 | sn_crypto_hash_sha512_final( | |
| 1662 | js_env_t *, | |
| 1663 | js_receiver_t, | |
| 1664 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state, | |
| 1665 | js_typedarray_span_t<> out | |
| 1666 | ) { | |
| 1667 | assert(out.size_bytes() == crypto_hash_sha512_BYTES); | |
| 1668 | ||
| 1669 | return crypto_hash_sha512_final(&*state, out.data()); | |
| 1670 | } | |
| 1671 | ||
| 1672 | static inline void | |
| 1673 | sn_crypto_aead_xchacha20poly1305_ietf_keygen( | |
| 1674 | js_env_t *, | |
| 1675 | js_receiver_t, | |
| 1676 | js_typedarray_span_t<> k | |
| 1677 | ) { | |
| 1678 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1679 | ||
| 1680 | crypto_aead_xchacha20poly1305_ietf_keygen(k.data()); | |
| 1681 | } | |
| 1682 | ||
| 1683 | static inline uint64_t | |
| 1684 | sn_crypto_aead_xchacha20poly1305_ietf_encrypt( | |
| 1685 | js_env_t *, | |
| 1686 | js_receiver_t, | |
| 1687 | js_typedarray_span_t<> c, | |
| 1688 | js_typedarray_span_t<> m, | |
| 1689 | std::optional<js_typedarray_span_t<>> ad, | |
| 1690 | js_typedarray_span_t<> npub, | |
| 1691 | js_typedarray_span_t<> k | |
| 1692 | ) { | |
| 1693 | assert(c.size_bytes() == m.size_bytes() + crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1694 | assert(c.size_bytes() <= 0xffffffff); | |
| 1695 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1696 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1697 | ||
| 1698 | uint8_t *ad_data = nullptr; | |
| 1699 | size_t ad_size = 0; | |
| 1700 | if (ad) { | |
| 1701 | ad_data = ad->data(); | |
| 1702 | ad_size = ad->size_bytes(); | |
| 1703 | } | |
| 1704 | ||
| 1705 | unsigned long long clen = 0; | |
| 1706 | int status = crypto_aead_xchacha20poly1305_ietf_encrypt( | |
| 1707 | c.data(), | |
| 1708 | &clen, | |
| 1709 | m.data(), | |
| 1710 | m.size_bytes(), | |
| 1711 | ad_data, | |
| 1712 | ad_size, | |
| 1713 | nullptr, | |
| 1714 | npub.data(), | |
| 1715 | k.data() | |
| 1716 | ); | |
| 1717 | ||
| 1718 | if (status < 0) return status; | |
| 1719 | ||
| 1720 | return clen; | |
| 1721 | } | |
| 1722 | ||
| 1723 | static inline uint64_t | |
| 1724 | sn_crypto_aead_xchacha20poly1305_ietf_decrypt( | |
| 1725 | js_env_t *, | |
| 1726 | js_receiver_t, | |
| 1727 | js_typedarray_span_t<> m, | |
| 1728 | js_typedarray_span_t<> c, | |
| 1729 | std::optional<js_typedarray_span_t<>> ad, | |
| 1730 | js_typedarray_span_t<> npub, | |
| 1731 | js_typedarray_span_t<> k | |
| 1732 | ) { | |
| 1733 | assert(m.size_bytes() == c.size_bytes() - crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1734 | assert(m.size_bytes() <= 0xffffffff); | |
| 1735 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1736 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1737 | ||
| 1738 | uint8_t *ad_data = nullptr; | |
| 1739 | size_t ad_size = 0; | |
| 1740 | if (ad) { | |
| 1741 | ad_data = ad->data(); | |
| 1742 | ad_size = ad->size_bytes(); | |
| 1743 | } | |
| 1744 | ||
| 1745 | unsigned long long mlen = 0; | |
| 1746 | int status = crypto_aead_xchacha20poly1305_ietf_decrypt( | |
| 1747 | m.data(), | |
| 1748 | &mlen, | |
| 1749 | nullptr, | |
| 1750 | c.data(), | |
| 1751 | c.size_bytes(), | |
| 1752 | ad_data, | |
| 1753 | ad_size, | |
| 1754 | npub.data(), | |
| 1755 | k.data() | |
| 1756 | ); | |
| 1757 | ||
| 1758 | if (status < 0) return status; | |
| 1759 | ||
| 1760 | return mlen; | |
| 1761 | } | |
| 1762 | ||
| 1763 | static inline uint64_t | |
| 1764 | sn_crypto_aead_xchacha20poly1305_ietf_encrypt_detached( | |
| 1765 | js_env_t *, | |
| 1766 | js_receiver_t, | |
| 1767 | js_typedarray_span_t<> c, | |
| 1768 | js_typedarray_span_t<> mac, | |
| 1769 | js_typedarray_span_t<> m, | |
| 1770 | std::optional<js_typedarray_span_t<>> ad, | |
| 1771 | js_typedarray_span_t<> npub, | |
| 1772 | js_typedarray_span_t<> k | |
| 1773 | ) { | |
| 1774 | assert(c.size_bytes() == m.size_bytes()); | |
| 1775 | assert(mac.size_bytes() == crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1776 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1777 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1778 | ||
| 1779 | uint8_t *ad_data = nullptr; | |
| 1780 | size_t ad_size = 0; | |
| 1781 | if (ad) { | |
| 1782 | ad_data = ad->data(); | |
| 1783 | ad_size = ad->size_bytes(); | |
| 1784 | } | |
| 1785 | ||
| 1786 | unsigned long long maclen = 0; | |
| 1787 | int status = crypto_aead_xchacha20poly1305_ietf_encrypt_detached( | |
| 1788 | c.data(), | |
| 1789 | mac.data(), | |
| 1790 | &maclen, | |
| 1791 | m.data(), | |
| 1792 | m.size_bytes(), | |
| 1793 | ad_data, | |
| 1794 | ad_size, | |
| 1795 | nullptr, | |
| 1796 | npub.data(), | |
| 1797 | k.data() | |
| 1798 | ); | |
| 1799 | ||
| 1800 | if (status < 0) return status; | |
| 1801 | ||
| 1802 | return maclen; | |
| 1803 | } | |
| 1804 | ||
| 1805 | static inline int | |
| 1806 | sn_crypto_aead_xchacha20poly1305_ietf_decrypt_detached( | |
| 1807 | js_env_t *, | |
| 1808 | js_receiver_t, | |
| 1809 | js_typedarray_span_t<> m, | |
| 1810 | js_typedarray_span_t<> c, | |
| 1811 | js_typedarray_span_t<> mac, | |
| 1812 | std::optional<js_typedarray_span_t<>> ad, | |
| 1813 | js_typedarray_span_t<> npub, | |
| 1814 | js_typedarray_span_t<> k | |
| 1815 | ) { | |
| 1816 | assert(m.size_bytes() == c.size_bytes()); | |
| 1817 | assert(mac.size_bytes() == crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1818 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1819 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1820 | ||
| 1821 | uint8_t *ad_data = nullptr; | |
| 1822 | size_t ad_size = 0; | |
| 1823 | if (ad) { | |
| 1824 | ad_data = ad->data(); | |
| 1825 | ad_size = ad->size_bytes(); | |
| 1826 | } | |
| 1827 | ||
| 1828 | return crypto_aead_xchacha20poly1305_ietf_decrypt_detached( | |
| 1829 | m.data(), | |
| 1830 | nullptr, | |
| 1831 | c.data(), | |
| 1832 | c.size_bytes(), | |
| 1833 | mac.data(), | |
| 1834 | ad_data, | |
| 1835 | ad_size, | |
| 1836 | npub.data(), | |
| 1837 | k.data() | |
| 1838 | ); | |
| 1839 | } | |
| 1840 | ||
| 1841 | static inline void | |
| 1842 | sn_crypto_aead_chacha20poly1305_ietf_keygen( | |
| 1843 | js_env_t *, | |
| 1844 | js_receiver_t, | |
| 1845 | js_typedarray_span_t<> k | |
| 1846 | ) { | |
| 1847 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1848 | ||
| 1849 | crypto_aead_chacha20poly1305_ietf_keygen(k.data()); | |
| 1850 | } | |
| 1851 | ||
| 1852 | static inline uint64_t | |
| 1853 | sn_crypto_aead_chacha20poly1305_ietf_encrypt( | |
| 1854 | js_env_t *, | |
| 1855 | js_receiver_t, | |
| 1856 | js_typedarray_span_t<> c, | |
| 1857 | js_typedarray_span_t<> m, | |
| 1858 | std::optional<js_typedarray_span_t<>> ad, | |
| 1859 | js_typedarray_span_t<> npub, | |
| 1860 | js_typedarray_span_t<> k | |
| 1861 | ) { | |
| 1862 | assert(c.size_bytes() == m.size_bytes() + crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1863 | assert(c.size_bytes() <= 0xffffffff); | |
| 1864 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1865 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1866 | ||
| 1867 | uint8_t *ad_data = nullptr; | |
| 1868 | size_t ad_size = 0; | |
| 1869 | if (ad) { | |
| 1870 | ad_data = ad->data(); | |
| 1871 | ad_size = ad->size_bytes(); | |
| 1872 | } | |
| 1873 | ||
| 1874 | unsigned long long clen = 0; | |
| 1875 | int status = crypto_aead_chacha20poly1305_ietf_encrypt( | |
| 1876 | c.data(), | |
| 1877 | &clen, | |
| 1878 | m.data(), | |
| 1879 | m.size_bytes(), | |
| 1880 | ad_data, | |
| 1881 | ad_size, | |
| 1882 | nullptr, | |
| 1883 | npub.data(), | |
| 1884 | k.data() | |
| 1885 | ); | |
| 1886 | ||
| 1887 | if (status < 0) return status; | |
| 1888 | ||
| 1889 | return clen; | |
| 1890 | } | |
| 1891 | ||
| 1892 | static inline uint64_t | |
| 1893 | sn_crypto_aead_chacha20poly1305_ietf_decrypt( | |
| 1894 | js_env_t *, | |
| 1895 | js_receiver_t, | |
| 1896 | js_typedarray_span_t<> m, | |
| 1897 | js_typedarray_span_t<> c, | |
| 1898 | std::optional<js_typedarray_span_t<>> ad, | |
| 1899 | js_typedarray_span_t<> npub, | |
| 1900 | js_typedarray_span_t<> k | |
| 1901 | ) { | |
| 1902 | assert(m.size_bytes() == c.size_bytes() - crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1903 | assert(m.size_bytes() <= 0xffffffff); | |
| 1904 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1905 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1906 | ||
| 1907 | uint8_t *ad_data = nullptr; | |
| 1908 | size_t ad_size = 0; | |
| 1909 | if (ad) { | |
| 1910 | ad_data = ad->data(); | |
| 1911 | ad_size = ad->size_bytes(); | |
| 1912 | } | |
| 1913 | ||
| 1914 | unsigned long long mlen = 0; | |
| 1915 | int status = crypto_aead_chacha20poly1305_ietf_decrypt( | |
| 1916 | m.data(), | |
| 1917 | &mlen, | |
| 1918 | nullptr, | |
| 1919 | c.data(), | |
| 1920 | c.size_bytes(), | |
| 1921 | ad_data, | |
| 1922 | ad_size, | |
| 1923 | npub.data(), | |
| 1924 | k.data() | |
| 1925 | ); | |
| 1926 | ||
| 1927 | if (status < 0) return status; | |
| 1928 | ||
| 1929 | return mlen; | |
| 1930 | } | |
| 1931 | ||
| 1932 | static inline uint64_t | |
| 1933 | sn_crypto_aead_chacha20poly1305_ietf_encrypt_detached( | |
| 1934 | js_env_t *, | |
| 1935 | js_receiver_t, | |
| 1936 | js_typedarray_span_t<> c, | |
| 1937 | js_typedarray_span_t<> mac, | |
| 1938 | js_typedarray_span_t<> m, | |
| 1939 | std::optional<js_typedarray_span_t<>> ad, | |
| 1940 | js_typedarray_span_t<> npub, | |
| 1941 | js_typedarray_span_t<> k | |
| 1942 | ) { | |
| 1943 | assert(c.size_bytes() == m.size_bytes()); | |
| 1944 | assert(mac.size_bytes() == crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1945 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1946 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1947 | ||
| 1948 | uint8_t *ad_data = nullptr; | |
| 1949 | size_t ad_size = 0; | |
| 1950 | if (ad) { | |
| 1951 | ad_data = ad->data(); | |
| 1952 | ad_size = ad->size_bytes(); | |
| 1953 | } | |
| 1954 | ||
| 1955 | unsigned long long maclen = 0; | |
| 1956 | int status = crypto_aead_chacha20poly1305_ietf_encrypt_detached( | |
| 1957 | c.data(), | |
| 1958 | mac.data(), | |
| 1959 | &maclen, | |
| 1960 | m.data(), | |
| 1961 | m.size_bytes(), | |
| 1962 | ad_data, | |
| 1963 | ad_size, | |
| 1964 | nullptr, | |
| 1965 | npub.data(), | |
| 1966 | k.data() | |
| 1967 | ); | |
| 1968 | ||
| 1969 | if (status < 0) return status; | |
| 1970 | ||
| 1971 | return maclen; | |
| 1972 | } | |
| 1973 | ||
| 1974 | static inline int | |
| 1975 | sn_crypto_aead_chacha20poly1305_ietf_decrypt_detached( | |
| 1976 | js_env_t *, | |
| 1977 | js_receiver_t, | |
| 1978 | js_typedarray_span_t<> m, | |
| 1979 | js_typedarray_span_t<> c, | |
| 1980 | js_typedarray_span_t<> mac, | |
| 1981 | std::optional<js_typedarray_span_t<>> ad, | |
| 1982 | js_typedarray_span_t<> npub, | |
| 1983 | js_typedarray_span_t<> k | |
| 1984 | ) { | |
| 1985 | assert(m.size_bytes() == c.size_bytes()); | |
| 1986 | assert(mac.size_bytes() == crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1987 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1988 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1989 | ||
| 1990 | uint8_t *ad_data = nullptr; | |
| 1991 | size_t ad_size = 0; | |
| 1992 | if (ad) { | |
| 1993 | ad_data = ad->data(); | |
| 1994 | ad_size = ad->size_bytes(); | |
| 1995 | } | |
| 1996 | ||
| 1997 | return crypto_aead_chacha20poly1305_ietf_decrypt_detached( | |
| 1998 | m.data(), | |
| 1999 | nullptr, | |
| 2000 | c.data(), | |
| 2001 | c.size_bytes(), | |
| 2002 | mac.data(), | |
| 2003 | ad_data, | |
| 2004 | ad_size, | |
| 2005 | npub.data(), | |
| 2006 | k.data() | |
| 2007 | ); | |
| 2008 | } | |
| 2009 | ||
| 2010 | static inline void | |
| 2011 | sn_crypto_secretstream_xchacha20poly1305_keygen( | |
| 2012 | js_env_t *env, | |
| 2013 | js_receiver_t, | |
| 2014 | ||
| 2015 | js_arraybuffer_span_t k, | |
| 2016 | uint32_t k_offset, | |
| 2017 | uint32_t k_len | |
| 2018 | ) { | |
| 2019 | assert_bounds(k); | |
| 2020 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2021 | ||
| 2022 | crypto_secretstream_xchacha20poly1305_keygen(&k[k_offset]); | |
| 2023 | } | |
| 2024 | ||
| 2025 | static inline int | |
| 2026 | sn_crypto_secretstream_xchacha20poly1305_init_push( | |
| 2027 | js_env_t *env, | |
| 2028 | js_receiver_t, | |
| 2029 | ||
| 2030 | js_arraybuffer_span_t state, | |
| 2031 | uint32_t state_offset, | |
| 2032 | uint32_t state_len, | |
| 2033 | ||
| 2034 | js_arraybuffer_span_t header, | |
| 2035 | uint32_t header_offset, | |
| 2036 | uint32_t header_len, | |
| 2037 | ||
| 2038 | js_arraybuffer_span_t k, | |
| 2039 | uint32_t k_offset, | |
| 2040 | uint32_t k_len | |
| 2041 | ) { | |
| 2042 | assert_bounds(state); | |
| 2043 | assert_bounds(header); | |
| 2044 | assert_bounds(k); | |
| 2045 | ||
| 2046 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2047 | assert(header_len == crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 2048 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2049 | ||
| 2050 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2051 | ||
| 2052 | return crypto_secretstream_xchacha20poly1305_init_push(state_data, &header[header_offset], &k[k_offset]); | |
| 2053 | } | |
| 2054 | ||
| 2055 | static inline uint64_t | |
| 2056 | sn_crypto_secretstream_xchacha20poly1305_push( | |
| 2057 | js_env_t *env, | |
| 2058 | js_receiver_t, | |
| 2059 | ||
| 2060 | js_arraybuffer_span_t state, | |
| 2061 | uint32_t state_offset, | |
| 2062 | uint32_t state_len, | |
| 2063 | ||
| 2064 | js_arraybuffer_span_t c, | |
| 2065 | uint32_t c_offset, | |
| 2066 | uint32_t c_len, | |
| 2067 | ||
| 2068 | js_arraybuffer_span_t m, | |
| 2069 | uint32_t m_offset, | |
| 2070 | uint32_t m_len, | |
| 2071 | ||
| 2072 | js_arraybuffer_span_t ad, | |
| 2073 | uint32_t ad_offset, | |
| 2074 | uint32_t ad_len, | |
| 2075 | ||
| 2076 | uint32_t tag | |
| 2077 | ) { | |
| 2078 | assert_bounds(state); | |
| 2079 | assert_bounds(c); | |
| 2080 | assert_bounds(m); | |
| 2081 | ||
| 2082 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2083 | assert(c_len == m_len + crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2084 | assert(c_len <= 0xffffffff && "32bit integer"); | |
| 2085 | ||
| 2086 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2087 | ||
| 2088 | uint8_t *ad_data = NULL; | |
| 2089 | if (ad_len) { | |
| 2090 | assert_bounds(ad); | |
| 2091 | ad_data = &ad[ad_offset]; | |
| 2092 | } | |
| 2093 | ||
| 2094 | unsigned long long clen = 0; | |
| 2095 | int status = crypto_secretstream_xchacha20poly1305_push( | |
| 2096 | state_data, | |
| 2097 | &c[c_offset], | |
| 2098 | &clen, | |
| 2099 | &m[m_offset], | |
| 2100 | m_len, | |
| 2101 | ad_data, | |
| 2102 | ad_len, | |
| 2103 | tag | |
| 2104 | ); | |
| 2105 | ||
| 2106 | if (status < 0) return status; | |
| 2107 | ||
| 2108 | return clen; | |
| 2109 | } | |
| 2110 | ||
| 2111 | static inline int | |
| 2112 | sn_crypto_secretstream_xchacha20poly1305_init_pull( | |
| 2113 | js_env_t *, | |
| 2114 | js_receiver_t, | |
| 2115 | ||
| 2116 | js_arraybuffer_span_t state, | |
| 2117 | uint32_t state_offset, | |
| 2118 | uint32_t state_len, | |
| 2119 | ||
| 2120 | js_arraybuffer_span_t header, | |
| 2121 | uint32_t header_offset, | |
| 2122 | uint32_t header_len, | |
| 2123 | ||
| 2124 | js_arraybuffer_span_t k, | |
| 2125 | uint32_t k_offset, | |
| 2126 | uint32_t k_len | |
| 2127 | ) { | |
| 2128 | assert_bounds(state); | |
| 2129 | assert_bounds(header); | |
| 2130 | assert_bounds(k); | |
| 2131 | ||
| 2132 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2133 | assert(header_len == crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 2134 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2135 | ||
| 2136 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2137 | ||
| 2138 | return crypto_secretstream_xchacha20poly1305_init_pull(state_data, &header[header_offset], &k[k_offset]); | |
| 2139 | } | |
| 2140 | ||
| 2141 | static inline uint64_t | |
| 2142 | sn_crypto_secretstream_xchacha20poly1305_pull( | |
| 2143 | js_env_t *env, | |
| 2144 | js_receiver_t, | |
| 2145 | ||
| 2146 | js_arraybuffer_span_t state, | |
| 2147 | uint32_t state_offset, | |
| 2148 | uint32_t state_len, | |
| 2149 | ||
| 2150 | js_arraybuffer_span_t m, | |
| 2151 | uint32_t m_offset, | |
| 2152 | uint32_t m_len, | |
| 2153 | ||
| 2154 | js_arraybuffer_span_t tag, | |
| 2155 | uint32_t tag_offset, | |
| 2156 | uint32_t tag_len, | |
| 2157 | ||
| 2158 | js_arraybuffer_span_t c, | |
| 2159 | uint32_t c_offset, | |
| 2160 | uint32_t c_len, | |
| 2161 | ||
| 2162 | js_arraybuffer_span_t ad, | |
| 2163 | uint32_t ad_offset, | |
| 2164 | uint32_t ad_len | |
| 2165 | ) { | |
| 2166 | assert_bounds(state); | |
| 2167 | assert_bounds(m); | |
| 2168 | assert_bounds(tag); | |
| 2169 | assert_bounds(c); | |
| 2170 | ||
| 2171 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2172 | assert(c_len >= crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2173 | assert(tag_len == 1); | |
| 2174 | assert(m_len == c_len - crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2175 | assert(m_len <= 0xffffffff); | |
| 2176 | ||
| 2177 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2178 | ||
| 2179 | uint8_t *ad_data = NULL; | |
| 2180 | if (ad_len) { | |
| 2181 | assert_bounds(ad); | |
| 2182 | ad_data = &ad[ad_offset]; | |
| 2183 | } | |
| 2184 | ||
| 2185 | unsigned long long mlen = 0; | |
| 2186 | int status = crypto_secretstream_xchacha20poly1305_pull( | |
| 2187 | state_data, | |
| 2188 | &m[m_offset], | |
| 2189 | &mlen, | |
| 2190 | &tag[tag_offset], | |
| 2191 | &c[c_offset], | |
| 2192 | c_len, | |
| 2193 | ad_data, | |
| 2194 | ad_len | |
| 2195 | ); | |
| 2196 | ||
| 2197 | if (status < 0) return status; | |
| 2198 | ||
| 2199 | return mlen; | |
| 2200 | } | |
| 2201 | ||
| 2202 | static inline void | |
| 2203 | sn_crypto_secretstream_xchacha20poly1305_rekey( | |
| 2204 | js_env_t *, | |
| 2205 | js_receiver_t, | |
| 2206 | ||
| 2207 | js_arraybuffer_span_t state, | |
| 2208 | uint32_t state_offset, | |
| 2209 | uint32_t state_len | |
| 2210 | ) { | |
| 2211 | assert_bounds(state); | |
| 2212 | ||
| 2213 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2214 | ||
| 2215 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2216 | ||
| 2217 | crypto_secretstream_xchacha20poly1305_rekey(state_data); | |
| 2218 | } | |
| 2219 | ||
| 2220 | struct sn_async_task { | |
| 2221 | uv_work_t task; | |
| 2222 | js_env_t *env; | |
| 2223 | js_persistent_t<js_function_t<void, int>> cb; | |
| 2224 | js_deferred_teardown_t *teardown; | |
| 2225 | int code = 0; | |
| 2226 | bool exiting = false; | |
| 2227 | ||
| 2228 | sn_async_task(js_env_t *env) : env(env) { | |
| 2229 | int err; | |
| 2230 | err = js_add_teardown_callback<on_teardown, sn_async_task>(env, this, teardown); | |
| 2231 | assert(err == 0); | |
| 2232 | } | |
| 2233 | ||
| 2234 | ~sn_async_task() { | |
| 2235 | int err; | |
| 2236 | err = js_finish_teardown_callback(teardown); | |
| 2237 | assert(err == 0); | |
| 2238 | } | |
| 2239 | ||
| 2240 | private: | |
| 2241 | static void | |
| 2242 | on_teardown(js_deferred_teardown_t *teardown, sn_async_task *req) { | |
| 2243 | req->exiting = true; | |
| 2244 | } | |
| 2245 | }; | |
| 2246 | ||
| 2247 | struct sn_async_pwhash_request : sn_async_task { | |
| 2248 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2249 | std::span<uint8_t> out; | |
| 2250 | ||
| 2251 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2252 | std::span<char> pwd; | |
| 2253 | ||
| 2254 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 2255 | std::span<uint8_t> salt; | |
| 2256 | ||
| 2257 | uint64_t opslimit; | |
| 2258 | size_t memlimit; | |
| 2259 | int alg; | |
| 2260 | ||
| 2261 | sn_async_pwhash_request(js_env_t *env) : sn_async_task(env) {} | |
| 2262 | }; | |
| 2263 | ||
| 2264 | static void | |
| 2265 | async_pwhash_execute(uv_work_t *uv_req) { | |
| 2266 | auto req = reinterpret_cast<sn_async_pwhash_request *>(uv_req); | |
| 2267 | ||
| 2268 | req->code = crypto_pwhash( | |
| 2269 | req->out.data(), | |
| 2270 | req->out.size(), | |
| 2271 | req->pwd.data(), | |
| 2272 | req->pwd.size(), | |
| 2273 | req->salt.data(), | |
| 2274 | req->opslimit, | |
| 2275 | req->memlimit, | |
| 2276 | req->alg | |
| 2277 | ); | |
| 2278 | } | |
| 2279 | ||
| 2280 | static void | |
| 2281 | async_pwhash_complete(uv_work_t *uv_req, int status) { | |
| 2282 | int err; | |
| 2283 | ||
| 2284 | auto req = reinterpret_cast<sn_async_pwhash_request *>(uv_req); | |
| 2285 | ||
| 2286 | if (req->exiting) return delete req; | |
| 2287 | ||
| 2288 | js_handle_scope_t *scope; | |
| 2289 | err = js_open_handle_scope(req->env, &scope); | |
| 2290 | assert(err == 0); | |
| 2291 | ||
| 2292 | js_function_t<void, int> callback; | |
| 2293 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2294 | assert(err == 0); | |
| 2295 | ||
| 2296 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2297 | assert(err != js_pending_exception); | |
| 2298 | ||
| 2299 | err = js_close_handle_scope(req->env, scope); | |
| 2300 | assert(err == 0); | |
| 2301 | ||
| 2302 | delete req; | |
| 2303 | } | |
| 2304 | ||
| 2305 | static inline void | |
| 2306 | sn_crypto_pwhash_async( | |
| 2307 | js_env_t *env, | |
| 2308 | js_receiver_t, | |
| 2309 | ||
| 2310 | js_arraybuffer_t out, | |
| 2311 | uint32_t out_offset, | |
| 2312 | uint32_t out_len, | |
| 2313 | ||
| 2314 | js_arraybuffer_t pwd, | |
| 2315 | uint32_t pwd_offset, | |
| 2316 | uint32_t pwd_len, | |
| 2317 | ||
| 2318 | js_arraybuffer_t salt, | |
| 2319 | uint32_t salt_offset, | |
| 2320 | uint32_t salt_len, | |
| 2321 | ||
| 2322 | uint64_t opslimit, | |
| 2323 | uint64_t memlimit, | |
| 2324 | uint32_t alg, | |
| 2325 | ||
| 2326 | js_function_t<void, int> callback | |
| 2327 | ) { | |
| 2328 | int err; | |
| 2329 | ||
| 2330 | auto *req = new sn_async_pwhash_request(env); | |
| 2331 | ||
| 2332 | std::span<uint8_t> out_view; | |
| 2333 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2334 | assert(err == 0); | |
| 2335 | assert(out_offset + out_len <= out_view.size()); | |
| 2336 | ||
| 2337 | req->out = {&out_view[out_offset], out_len}; | |
| 2338 | ||
| 2339 | std::span<char> pwd_view; | |
| 2340 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2341 | assert(err == 0); | |
| 2342 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2343 | ||
| 2344 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2345 | ||
| 2346 | std::span<uint8_t> salt_view; | |
| 2347 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 2348 | assert(err == 0); | |
| 2349 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 2350 | ||
| 2351 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 2352 | ||
| 2353 | req->opslimit = opslimit; | |
| 2354 | req->memlimit = memlimit; | |
| 2355 | req->alg = alg; | |
| 2356 | ||
| 2357 | err = js_create_reference(env, out, req->out_ref); | |
| 2358 | assert(err == 0); | |
| 2359 | ||
| 2360 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2361 | assert(err == 0); | |
| 2362 | ||
| 2363 | err = js_create_reference(env, salt, req->salt_ref); | |
| 2364 | assert(err == 0); | |
| 2365 | ||
| 2366 | err = js_create_reference(env, callback, req->cb); | |
| 2367 | assert(err == 0); | |
| 2368 | ||
| 2369 | uv_loop_t *loop; | |
| 2370 | err = js_get_env_loop(env, &loop); | |
| 2371 | assert(err == 0); | |
| 2372 | ||
| 2373 | err = uv_queue_work(loop, &req->task, async_pwhash_execute, async_pwhash_complete); | |
| 2374 | assert(err == 0); | |
| 2375 | } | |
| 2376 | ||
| 2377 | struct sn_async_pwhash_str_request : sn_async_task { | |
| 2378 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2379 | std::span<char> out; | |
| 2380 | ||
| 2381 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2382 | std::span<char> pwd; | |
| 2383 | ||
| 2384 | uint64_t opslimit; | |
| 2385 | size_t memlimit; | |
| 2386 | ||
| 2387 | sn_async_pwhash_str_request(js_env_t *env) : sn_async_task(env) {} | |
| 2388 | }; | |
| 2389 | ||
| 2390 | static void | |
| 2391 | async_pwhash_str_execute(uv_work_t *uv_req) { | |
| 2392 | auto *req = reinterpret_cast<sn_async_pwhash_str_request *>(uv_req); | |
| 2393 | ||
| 2394 | req->code = crypto_pwhash_str( | |
| 2395 | req->out.data(), | |
| 2396 | req->pwd.data(), | |
| 2397 | req->pwd.size(), | |
| 2398 | req->opslimit, | |
| 2399 | req->memlimit | |
| 2400 | ); | |
| 2401 | } | |
| 2402 | ||
| 2403 | static void | |
| 2404 | async_pwhash_str_complete(uv_work_t *uv_req, int status) { | |
| 2405 | int err; | |
| 2406 | ||
| 2407 | auto *req = reinterpret_cast<sn_async_pwhash_str_request *>(uv_req); | |
| 2408 | ||
| 2409 | if (req->exiting) return delete req; | |
| 2410 | ||
| 2411 | js_handle_scope_t *scope; | |
| 2412 | err = js_open_handle_scope(req->env, &scope); | |
| 2413 | assert(err == 0); | |
| 2414 | ||
| 2415 | js_function_t<void, int> callback; | |
| 2416 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2417 | assert(err == 0); | |
| 2418 | ||
| 2419 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2420 | assert(err != js_pending_exception); | |
| 2421 | ||
| 2422 | err = js_close_handle_scope(req->env, scope); | |
| 2423 | assert(err == 0); | |
| 2424 | ||
| 2425 | delete req; | |
| 2426 | } | |
| 2427 | ||
| 2428 | static inline void | |
| 2429 | sn_crypto_pwhash_str_async( | |
| 2430 | js_env_t *env, | |
| 2431 | js_receiver_t, | |
| 2432 | ||
| 2433 | js_arraybuffer_t out, | |
| 2434 | uint32_t out_offset, | |
| 2435 | uint32_t out_len, | |
| 2436 | ||
| 2437 | js_arraybuffer_t pwd, | |
| 2438 | uint32_t pwd_offset, | |
| 2439 | uint32_t pwd_len, | |
| 2440 | ||
| 2441 | uint64_t opslimit, | |
| 2442 | uint64_t memlimit, | |
| 2443 | js_function_t<void, int> callback | |
| 2444 | ) { | |
| 2445 | int err; | |
| 2446 | ||
| 2447 | auto *req = new sn_async_pwhash_str_request(env); | |
| 2448 | ||
| 2449 | std::span<char> out_view; | |
| 2450 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2451 | assert(err == 0); | |
| 2452 | assert(out_offset + out_len <= out_view.size()); | |
| 2453 | ||
| 2454 | req->out = {&out_view[out_offset], out_len}; | |
| 2455 | ||
| 2456 | std::span<char> pwd_view; | |
| 2457 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2458 | assert(err == 0); | |
| 2459 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2460 | ||
| 2461 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2462 | ||
| 2463 | req->opslimit = opslimit; | |
| 2464 | req->memlimit = memlimit; | |
| 2465 | ||
| 2466 | err = js_create_reference(env, out, req->out_ref); | |
| 2467 | assert(err == 0); | |
| 2468 | ||
| 2469 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2470 | assert(err == 0); | |
| 2471 | ||
| 2472 | err = js_create_reference(env, callback, req->cb); | |
| 2473 | assert(err == 0); | |
| 2474 | ||
| 2475 | uv_loop_t *loop; | |
| 2476 | err = js_get_env_loop(env, &loop); | |
| 2477 | assert(err == 0); | |
| 2478 | ||
| 2479 | err = uv_queue_work(loop, &req->task, async_pwhash_str_execute, async_pwhash_str_complete); | |
| 2480 | assert(err == 0); | |
| 2481 | } | |
| 2482 | ||
| 2483 | struct sn_async_pwhash_str_verify_request : sn_async_task { | |
| 2484 | js_persistent_t<js_arraybuffer_t> str_ref; | |
| 2485 | std::span<char> str; | |
| 2486 | ||
| 2487 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2488 | std::span<char> pwd; | |
| 2489 | ||
| 2490 | sn_async_pwhash_str_verify_request(js_env_t *env) : sn_async_task(env) {} | |
| 2491 | }; | |
| 2492 | ||
| 2493 | static void | |
| 2494 | async_pwhash_str_verify_execute(uv_work_t *uv_req) { | |
| 2495 | auto *req = reinterpret_cast<sn_async_pwhash_str_verify_request *>(uv_req); | |
| 2496 | ||
| 2497 | req->code = crypto_pwhash_str_verify(req->str.data(), req->pwd.data(), req->pwd.size()); | |
| 2498 | } | |
| 2499 | ||
| 2500 | static void | |
| 2501 | async_pwhash_str_verify_complete(uv_work_t *uv_req, int status) { | |
| 2502 | int err; | |
| 2503 | ||
| 2504 | auto *req = reinterpret_cast<sn_async_pwhash_str_verify_request *>(uv_req); | |
| 2505 | ||
| 2506 | if (req->exiting) return delete req; | |
| 2507 | ||
| 2508 | js_handle_scope_t *scope; | |
| 2509 | err = js_open_handle_scope(req->env, &scope); | |
| 2510 | assert(err == 0); | |
| 2511 | ||
| 2512 | js_function_t<void, int> callback; | |
| 2513 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2514 | assert(err == 0); | |
| 2515 | ||
| 2516 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2517 | assert(err != js_pending_exception); | |
| 2518 | ||
| 2519 | err = js_close_handle_scope(req->env, scope); | |
| 2520 | assert(err == 0); | |
| 2521 | ||
| 2522 | delete req; | |
| 2523 | } | |
| 2524 | ||
| 2525 | static inline void | |
| 2526 | sn_crypto_pwhash_str_verify_async( | |
| 2527 | js_env_t *env, | |
| 2528 | js_receiver_t, | |
| 2529 | ||
| 2530 | js_arraybuffer_t str, | |
| 2531 | uint32_t str_offset, | |
| 2532 | uint32_t str_len, | |
| 2533 | ||
| 2534 | js_arraybuffer_t pwd, | |
| 2535 | uint32_t pwd_offset, | |
| 2536 | uint32_t pwd_len, | |
| 2537 | ||
| 2538 | js_function_t<void, int> callback | |
| 2539 | ) { | |
| 2540 | int err; | |
| 2541 | ||
| 2542 | auto *req = new sn_async_pwhash_str_verify_request(env); | |
| 2543 | ||
| 2544 | std::span<char> str_view; | |
| 2545 | err = js_get_arraybuffer_info(env, str, str_view); | |
| 2546 | assert(err == 0); | |
| 2547 | assert(str_offset + str_len <= str_view.size()); | |
| 2548 | ||
| 2549 | req->str = {&str_view[str_offset], str_len}; | |
| 2550 | ||
| 2551 | std::span<char> pwd_view; | |
| 2552 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2553 | assert(err == 0); | |
| 2554 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2555 | ||
| 2556 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2557 | ||
| 2558 | err = js_create_reference(env, str, req->str_ref); | |
| 2559 | assert(err == 0); | |
| 2560 | ||
| 2561 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2562 | assert(err == 0); | |
| 2563 | ||
| 2564 | err = js_create_reference(env, callback, req->cb); | |
| 2565 | assert(err == 0); | |
| 2566 | ||
| 2567 | uv_loop_t *loop; | |
| 2568 | err = js_get_env_loop(env, &loop); | |
| 2569 | assert(err == 0); | |
| 2570 | ||
| 2571 | err = uv_queue_work(loop, &req->task, async_pwhash_str_verify_execute, async_pwhash_str_verify_complete); | |
| 2572 | assert(err == 0); | |
| 2573 | } | |
| 2574 | ||
| 2575 | struct sn_async_pwhash_scryptsalsa208sha256_request : sn_async_task { | |
| 2576 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2577 | std::span<uint8_t> out; | |
| 2578 | ||
| 2579 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2580 | std::span<char> pwd; | |
| 2581 | ||
| 2582 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 2583 | std::span<uint8_t> salt; | |
| 2584 | ||
| 2585 | uint64_t opslimit; | |
| 2586 | size_t memlimit; | |
| 2587 | ||
| 2588 | sn_async_pwhash_scryptsalsa208sha256_request(js_env_t *env) : sn_async_task(env) {} | |
| 2589 | }; | |
| 2590 | ||
| 2591 | static void | |
| 2592 | async_pwhash_scryptsalsa208sha256_execute(uv_work_t *uv_req) { | |
| 2593 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_request *>(uv_req); | |
| 2594 | ||
| 2595 | req->code = crypto_pwhash_scryptsalsa208sha256( | |
| 2596 | req->out.data(), | |
| 2597 | req->out.size(), | |
| 2598 | req->pwd.data(), | |
| 2599 | req->pwd.size(), | |
| 2600 | req->salt.data(), | |
| 2601 | req->opslimit, | |
| 2602 | req->memlimit | |
| 2603 | ); | |
| 2604 | } | |
| 2605 | ||
| 2606 | static void | |
| 2607 | async_pwhash_scryptsalsa208sha256_complete(uv_work_t *uv_req, int status) { | |
| 2608 | int err; | |
| 2609 | ||
| 2610 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_request *>(uv_req); | |
| 2611 | ||
| 2612 | if (req->exiting) return delete req; | |
| 2613 | ||
| 2614 | js_handle_scope_t *scope; | |
| 2615 | err = js_open_handle_scope(req->env, &scope); | |
| 2616 | assert(err == 0); | |
| 2617 | ||
| 2618 | js_function_t<void, int> callback; | |
| 2619 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2620 | assert(err == 0); | |
| 2621 | ||
| 2622 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2623 | assert(err != js_pending_exception); | |
| 2624 | ||
| 2625 | err = js_close_handle_scope(req->env, scope); | |
| 2626 | assert(err == 0); | |
| 2627 | ||
| 2628 | delete req; | |
| 2629 | } | |
| 2630 | ||
| 2631 | static inline void | |
| 2632 | sn_crypto_pwhash_scryptsalsa208sha256_async( | |
| 2633 | js_env_t *env, | |
| 2634 | js_receiver_t, | |
| 2635 | ||
| 2636 | js_arraybuffer_t out, | |
| 2637 | uint32_t out_offset, | |
| 2638 | uint32_t out_len, | |
| 2639 | ||
| 2640 | js_arraybuffer_t pwd, | |
| 2641 | uint32_t pwd_offset, | |
| 2642 | uint32_t pwd_len, | |
| 2643 | ||
| 2644 | js_arraybuffer_t salt, | |
| 2645 | uint32_t salt_offset, | |
| 2646 | uint32_t salt_len, | |
| 2647 | ||
| 2648 | uint64_t opslimit, | |
| 2649 | uint64_t memlimit, | |
| 2650 | ||
| 2651 | js_function_t<void, int> callback | |
| 2652 | ) { | |
| 2653 | int err; | |
| 2654 | ||
| 2655 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_request(env); | |
| 2656 | ||
| 2657 | std::span<uint8_t> out_view; | |
| 2658 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2659 | assert(err == 0); | |
| 2660 | assert(out_offset + out_len <= out_view.size()); | |
| 2661 | ||
| 2662 | req->out = {&out_view[out_offset], out_len}; | |
| 2663 | ||
| 2664 | std::span<char> pwd_view; | |
| 2665 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2666 | assert(err == 0); | |
| 2667 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2668 | ||
| 2669 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2670 | ||
| 2671 | std::span<uint8_t> salt_view; | |
| 2672 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 2673 | assert(err == 0); | |
| 2674 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 2675 | assert(salt_len == crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 2676 | ||
| 2677 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 2678 | ||
| 2679 | req->opslimit = opslimit; | |
| 2680 | req->memlimit = memlimit; | |
| 2681 | ||
| 2682 | err = js_create_reference(env, out, req->out_ref); | |
| 2683 | assert(err == 0); | |
| 2684 | ||
| 2685 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2686 | assert(err == 0); | |
| 2687 | ||
| 2688 | err = js_create_reference(env, salt, req->salt_ref); | |
| 2689 | assert(err == 0); | |
| 2690 | ||
| 2691 | err = js_create_reference(env, callback, req->cb); | |
| 2692 | assert(err == 0); | |
| 2693 | ||
| 2694 | uv_loop_t *loop; | |
| 2695 | err = js_get_env_loop(env, &loop); | |
| 2696 | assert(err == 0); | |
| 2697 | ||
| 2698 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_execute, async_pwhash_scryptsalsa208sha256_complete); | |
| 2699 | assert(err == 0); | |
| 2700 | } | |
| 2701 | ||
| 2702 | struct sn_async_pwhash_scryptsalsa208sha256_str_request : sn_async_task { | |
| 2703 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2704 | std::span<char> out; | |
| 2705 | ||
| 2706 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2707 | std::span<char> pwd; | |
| 2708 | ||
| 2709 | uint64_t opslimit; | |
| 2710 | size_t memlimit; | |
| 2711 | ||
| 2712 | sn_async_pwhash_scryptsalsa208sha256_str_request(js_env_t *env) : sn_async_task(env) {} | |
| 2713 | }; | |
| 2714 | ||
| 2715 | static void | |
| 2716 | async_pwhash_scryptsalsa208sha256_str_execute(uv_work_t *uv_req) { | |
| 2717 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_request *>(uv_req); | |
| 2718 | ||
| 2719 | req->code = crypto_pwhash_scryptsalsa208sha256_str( | |
| 2720 | req->out.data(), | |
| 2721 | req->pwd.data(), | |
| 2722 | req->pwd.size(), | |
| 2723 | req->opslimit, | |
| 2724 | req->memlimit | |
| 2725 | ); | |
| 2726 | } | |
| 2727 | ||
| 2728 | static void | |
| 2729 | async_pwhash_scryptsalsa208sha256_str_complete(uv_work_t *uv_req, int status) { | |
| 2730 | int err; | |
| 2731 | ||
| 2732 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_request *>(uv_req); | |
| 2733 | ||
| 2734 | if (req->exiting) return delete req; | |
| 2735 | ||
| 2736 | js_handle_scope_t *scope; | |
| 2737 | err = js_open_handle_scope(req->env, &scope); | |
| 2738 | assert(err == 0); | |
| 2739 | ||
| 2740 | js_function_t<void, int> callback; | |
| 2741 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2742 | assert(err == 0); | |
| 2743 | ||
| 2744 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2745 | assert(err != js_pending_exception); | |
| 2746 | ||
| 2747 | err = js_close_handle_scope(req->env, scope); | |
| 2748 | assert(err == 0); | |
| 2749 | ||
| 2750 | delete req; | |
| 2751 | } | |
| 2752 | ||
| 2753 | static inline void | |
| 2754 | sn_crypto_pwhash_scryptsalsa208sha256_str_async( | |
| 2755 | js_env_t *env, | |
| 2756 | js_receiver_t, | |
| 2757 | ||
| 2758 | js_arraybuffer_t out, | |
| 2759 | uint32_t out_offset, | |
| 2760 | uint32_t out_len, | |
| 2761 | ||
| 2762 | js_arraybuffer_t pwd, | |
| 2763 | uint32_t pwd_offset, | |
| 2764 | uint32_t pwd_len, | |
| 2765 | ||
| 2766 | uint64_t opslimit, | |
| 2767 | uint64_t memlimit, | |
| 2768 | ||
| 2769 | js_function_t<void, int> callback | |
| 2770 | ) { | |
| 2771 | int err; | |
| 2772 | ||
| 2773 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_str_request(env); | |
| 2774 | ||
| 2775 | std::span<char> out_view; | |
| 2776 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2777 | assert(err == 0); | |
| 2778 | assert(out_offset + out_len <= out_view.size()); | |
| 2779 | ||
| 2780 | req->out = {&out_view[out_offset], out_len}; | |
| 2781 | ||
| 2782 | std::span<char> pwd_view; | |
| 2783 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2784 | assert(err == 0); | |
| 2785 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2786 | ||
| 2787 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2788 | ||
| 2789 | req->opslimit = opslimit; | |
| 2790 | req->memlimit = memlimit; | |
| 2791 | ||
| 2792 | err = js_create_reference(env, out, req->out_ref); | |
| 2793 | assert(err == 0); | |
| 2794 | ||
| 2795 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2796 | assert(err == 0); | |
| 2797 | ||
| 2798 | err = js_create_reference(env, callback, req->cb); | |
| 2799 | assert(err == 0); | |
| 2800 | ||
| 2801 | uv_loop_t *loop; | |
| 2802 | err = js_get_env_loop(env, &loop); | |
| 2803 | assert(err == 0); | |
| 2804 | ||
| 2805 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_str_execute, async_pwhash_scryptsalsa208sha256_str_complete); | |
| 2806 | assert(err == 0); | |
| 2807 | } | |
| 2808 | ||
| 2809 | struct sn_async_pwhash_scryptsalsa208sha256_str_verify_request : sn_async_task { | |
| 2810 | js_persistent_t<js_arraybuffer_t> str_ref; | |
| 2811 | std::span<char> str; | |
| 2812 | ||
| 2813 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2814 | std::span<char> pwd; | |
| 2815 | ||
| 2816 | sn_async_pwhash_scryptsalsa208sha256_str_verify_request(js_env_t *env) : sn_async_task(env) {} | |
| 2817 | }; | |
| 2818 | ||
| 2819 | static void | |
| 2820 | async_pwhash_scryptsalsa208sha256_str_verify_execute(uv_work_t *uv_req) { | |
| 2821 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_verify_request *>(uv_req); | |
| 2822 | ||
| 2823 | req->code = crypto_pwhash_scryptsalsa208sha256_str_verify(req->str.data(), req->pwd.data(), req->pwd.size()); | |
| 2824 | } | |
| 2825 | ||
| 2826 | static void | |
| 2827 | async_pwhash_scryptsalsa208sha256_str_verify_complete(uv_work_t *uv_req, int status) { | |
| 2828 | int err; | |
| 2829 | ||
| 2830 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_verify_request *>(uv_req); | |
| 2831 | ||
| 2832 | if (req->exiting) return delete req; | |
| 2833 | ||
| 2834 | js_handle_scope_t *scope; | |
| 2835 | err = js_open_handle_scope(req->env, &scope); | |
| 2836 | assert(err == 0); | |
| 2837 | ||
| 2838 | js_function_t<void, int> callback; | |
| 2839 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2840 | assert(err == 0); | |
| 2841 | ||
| 2842 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2843 | assert(err != js_pending_exception); | |
| 2844 | ||
| 2845 | err = js_close_handle_scope(req->env, scope); | |
| 2846 | assert(err == 0); | |
| 2847 | ||
| 2848 | delete req; | |
| 2849 | } | |
| 2850 | ||
| 2851 | static inline void | |
| 2852 | sn_crypto_pwhash_scryptsalsa208sha256_str_verify_async( | |
| 2853 | js_env_t *env, | |
| 2854 | js_receiver_t, | |
| 2855 | ||
| 2856 | js_arraybuffer_t str, | |
| 2857 | uint32_t str_offset, | |
| 2858 | uint32_t str_len, | |
| 2859 | ||
| 2860 | js_arraybuffer_t pwd, | |
| 2861 | uint32_t pwd_offset, | |
| 2862 | uint32_t pwd_len, | |
| 2863 | ||
| 2864 | js_function_t<void, int> callback | |
| 2865 | ) { | |
| 2866 | int err; | |
| 2867 | ||
| 2868 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_str_verify_request(env); | |
| 2869 | ||
| 2870 | std::span<char> str_view; | |
| 2871 | err = js_get_arraybuffer_info(env, str, str_view); | |
| 2872 | assert(err == 0); | |
| 2873 | assert(str_offset + str_len <= str_view.size()); | |
| 2874 | ||
| 2875 | req->str = {&str_view[str_offset], str_len}; | |
| 2876 | ||
| 2877 | std::span<char> pwd_view; | |
| 2878 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2879 | assert(err == 0); | |
| 2880 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2881 | ||
| 2882 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2883 | ||
| 2884 | err = js_create_reference(env, str, req->str_ref); | |
| 2885 | assert(err == 0); | |
| 2886 | ||
| 2887 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2888 | assert(err == 0); | |
| 2889 | ||
| 2890 | err = js_create_reference(env, callback, req->cb); | |
| 2891 | assert(err == 0); | |
| 2892 | ||
| 2893 | uv_loop_t *loop; | |
| 2894 | err = js_get_env_loop(env, &loop); | |
| 2895 | assert(err == 0); | |
| 2896 | ||
| 2897 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_str_verify_execute, async_pwhash_scryptsalsa208sha256_str_verify_complete); | |
| 2898 | assert(err == 0); | |
| 2899 | } | |
| 2900 | ||
| 2901 | struct sn_crypto_stream_xor_state { | |
| 2902 | unsigned char n[crypto_stream_NONCEBYTES]; | |
| 2903 | unsigned char k[crypto_stream_KEYBYTES]; | |
| 2904 | unsigned char next_block[64]; | |
| 2905 | int remainder; | |
| 2906 | uint64_t block_counter; | |
| 2907 | }; | |
| 2908 | ||
| 2909 | static inline void | |
| 2910 | sn_crypto_stream_xor_wrap_init( | |
| 2911 | js_env_t *, | |
| 2912 | js_receiver_t, | |
| 2913 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state, | |
| 2914 | js_typedarray_span_t<> n, | |
| 2915 | js_typedarray_span_t<> k | |
| 2916 | ) { | |
| 2917 | assert(n.size_bytes() == crypto_stream_NONCEBYTES); | |
| 2918 | assert(k.size_bytes() == crypto_stream_KEYBYTES); | |
| 2919 | ||
| 2920 | state->remainder = 0; | |
| 2921 | state->block_counter = 0; | |
| 2922 | memcpy(state->n, n.data(), crypto_stream_NONCEBYTES); | |
| 2923 | memcpy(state->k, k.data(), crypto_stream_KEYBYTES); | |
| 2924 | } | |
| 2925 | ||
| 2926 | static inline void | |
| 2927 | sn_crypto_stream_xor_wrap_update( | |
| 2928 | js_env_t *, | |
| 2929 | js_receiver_t, | |
| 2930 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state, | |
| 2931 | js_typedarray_span_t<> c, | |
| 2932 | js_typedarray_span_t<> m | |
| 2933 | ) { | |
| 2934 | assert(c.size_bytes() == m.size_bytes()); | |
| 2935 | ||
| 2936 | auto next_block = state->next_block; | |
| 2937 | ||
| 2938 | size_t m_size = m.size_bytes(); | |
| 2939 | auto *c_ptr = c.data(); | |
| 2940 | auto *m_ptr = m.data(); | |
| 2941 | ||
| 2942 | if (state->remainder) { | |
| 2943 | uint64_t offset = 0; | |
| 2944 | int rem = state->remainder; | |
| 2945 | ||
| 2946 | while (rem < 64 && offset < m_size) { | |
| 2947 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 2948 | ++offset; | |
| 2949 | ++rem; | |
| 2950 | } | |
| 2951 | ||
| 2952 | c_ptr += offset; | |
| 2953 | m_ptr += offset; | |
| 2954 | m_size -= offset; | |
| 2955 | state->remainder = (rem == 64) ? 0 : rem; | |
| 2956 | ||
| 2957 | if (m_size == 0) return; | |
| 2958 | } | |
| 2959 | ||
| 2960 | state->remainder = m_size & 63; | |
| 2961 | size_t main_len = m_size - state->remainder; | |
| 2962 | ||
| 2963 | crypto_stream_xsalsa20_xor_ic(c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k); | |
| 2964 | state->block_counter += main_len / 64; | |
| 2965 | ||
| 2966 | if (state->remainder) { | |
| 2967 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 2968 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 2969 | ||
| 2970 | crypto_stream_xsalsa20_xor_ic( | |
| 2971 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 2972 | ); | |
| 2973 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 2974 | ||
| 2975 | state->block_counter++; | |
| 2976 | } | |
| 2977 | } | |
| 2978 | ||
| 2979 | static inline void | |
| 2980 | sn_crypto_stream_xor_wrap_final( | |
| 2981 | js_env_t *, | |
| 2982 | js_receiver_t, | |
| 2983 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state | |
| 2984 | ) { | |
| 2985 | sodium_memzero(state->n, sizeof(state->n)); | |
| 2986 | sodium_memzero(state->k, sizeof(state->k)); | |
| 2987 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 2988 | state->remainder = 0; | |
| 2989 | } | |
| 2990 | ||
| 2991 | struct sn_crypto_stream_chacha20_xor_state { | |
| 2992 | unsigned char n[crypto_stream_chacha20_NONCEBYTES]; | |
| 2993 | unsigned char k[crypto_stream_chacha20_KEYBYTES]; | |
| 2994 | unsigned char next_block[64]; | |
| 2995 | int remainder; | |
| 2996 | uint64_t block_counter; | |
| 2997 | }; | |
| 2998 | ||
| 2999 | static inline void | |
| 3000 | sn_crypto_stream_chacha20_xor_wrap_init( | |
| 3001 | js_env_t *, | |
| 3002 | js_receiver_t, | |
| 3003 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state, | |
| 3004 | js_typedarray_span_t<> n, | |
| 3005 | js_typedarray_span_t<> k | |
| 3006 | ) { | |
| 3007 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 3008 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 3009 | ||
| 3010 | state->remainder = 0; | |
| 3011 | state->block_counter = 0; | |
| 3012 | memcpy(state->n, n.data(), crypto_stream_chacha20_NONCEBYTES); | |
| 3013 | memcpy(state->k, k.data(), crypto_stream_chacha20_KEYBYTES); | |
| 3014 | } | |
| 3015 | ||
| 3016 | static inline void | |
| 3017 | sn_crypto_stream_chacha20_xor_wrap_update( | |
| 3018 | js_env_t *, | |
| 3019 | js_receiver_t, | |
| 3020 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state, | |
| 3021 | js_typedarray_span_t<> c, | |
| 3022 | js_typedarray_span_t<> m | |
| 3023 | ) { | |
| 3024 | assert(c.size_bytes() == m.size_bytes()); | |
| 3025 | ||
| 3026 | auto *next_block = state->next_block; | |
| 3027 | ||
| 3028 | size_t m_size = m.size_bytes(); | |
| 3029 | auto *c_ptr = c.data(); | |
| 3030 | auto *m_ptr = m.data(); | |
| 3031 | ||
| 3032 | if (state->remainder) { | |
| 3033 | uint64_t offset = 0; | |
| 3034 | int rem = state->remainder; | |
| 3035 | ||
| 3036 | while (rem < 64 && offset < m_size) { | |
| 3037 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3038 | offset++; | |
| 3039 | rem++; | |
| 3040 | } | |
| 3041 | ||
| 3042 | c_ptr += offset; | |
| 3043 | m_ptr += offset; | |
| 3044 | m_size -= offset; | |
| 3045 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3046 | ||
| 3047 | if (m_size == 0) return; | |
| 3048 | } | |
| 3049 | ||
| 3050 | state->remainder = m_size & 63; | |
| 3051 | size_t main_len = m_size - state->remainder; | |
| 3052 | ||
| 3053 | crypto_stream_chacha20_xor_ic( | |
| 3054 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3055 | ); | |
| 3056 | ||
| 3057 | state->block_counter += main_len / 64; | |
| 3058 | ||
| 3059 | if (state->remainder) { | |
| 3060 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3061 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3062 | ||
| 3063 | crypto_stream_chacha20_xor_ic( | |
| 3064 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3065 | ); | |
| 3066 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3067 | ||
| 3068 | state->block_counter++; | |
| 3069 | } | |
| 3070 | } | |
| 3071 | ||
| 3072 | static inline void | |
| 3073 | sn_crypto_stream_chacha20_xor_wrap_final( | |
| 3074 | js_env_t *, | |
| 3075 | js_receiver_t, | |
| 3076 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state | |
| 3077 | ) { | |
| 3078 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3079 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3080 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3081 | state->remainder = 0; | |
| 3082 | } | |
| 3083 | ||
| 3084 | struct sn_crypto_stream_chacha20_ietf_xor_state { | |
| 3085 | unsigned char n[crypto_stream_chacha20_ietf_NONCEBYTES]; | |
| 3086 | unsigned char k[crypto_stream_chacha20_ietf_KEYBYTES]; | |
| 3087 | unsigned char next_block[64]; | |
| 3088 | int remainder; | |
| 3089 | uint64_t block_counter; | |
| 3090 | }; | |
| 3091 | ||
| 3092 | static inline void | |
| 3093 | sn_crypto_stream_chacha20_ietf_xor_wrap_init( | |
| 3094 | js_env_t *, | |
| 3095 | js_receiver_t, | |
| 3096 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state, | |
| 3097 | js_typedarray_span_t<> n, | |
| 3098 | js_typedarray_span_t<> k | |
| 3099 | ) { | |
| 3100 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 3101 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 3102 | ||
| 3103 | state->remainder = 0; | |
| 3104 | state->block_counter = 0; | |
| 3105 | memcpy(state->n, n.data(), crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 3106 | memcpy(state->k, k.data(), crypto_stream_chacha20_ietf_KEYBYTES); | |
| 3107 | } | |
| 3108 | ||
| 3109 | static inline void | |
| 3110 | sn_crypto_stream_chacha20_ietf_xor_wrap_update( | |
| 3111 | js_env_t *, | |
| 3112 | js_receiver_t, | |
| 3113 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state, | |
| 3114 | js_typedarray_span_t<> c, | |
| 3115 | js_typedarray_span_t<> m | |
| 3116 | ) { | |
| 3117 | assert(c.size_bytes() == m.size_bytes()); | |
| 3118 | ||
| 3119 | auto *next_block = state->next_block; | |
| 3120 | ||
| 3121 | size_t m_size = m.size_bytes(); | |
| 3122 | auto *c_ptr = c.data(); | |
| 3123 | auto *m_ptr = m.data(); | |
| 3124 | ||
| 3125 | if (state->remainder) { | |
| 3126 | uint64_t offset = 0; | |
| 3127 | int rem = state->remainder; | |
| 3128 | ||
| 3129 | while (rem < 64 && offset < m_size) { | |
| 3130 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3131 | offset++; | |
| 3132 | rem++; | |
| 3133 | } | |
| 3134 | ||
| 3135 | c_ptr += offset; | |
| 3136 | m_ptr += offset; | |
| 3137 | m_size -= offset; | |
| 3138 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3139 | ||
| 3140 | if (m_size == 0) return; | |
| 3141 | } | |
| 3142 | ||
| 3143 | state->remainder = m_size & 63; | |
| 3144 | size_t main_len = m_size - state->remainder; | |
| 3145 | ||
| 3146 | crypto_stream_chacha20_ietf_xor_ic( | |
| 3147 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3148 | ); | |
| 3149 | ||
| 3150 | state->block_counter += main_len / 64; | |
| 3151 | ||
| 3152 | if (state->remainder) { | |
| 3153 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3154 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3155 | ||
| 3156 | crypto_stream_chacha20_ietf_xor_ic( | |
| 3157 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3158 | ); | |
| 3159 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3160 | ||
| 3161 | state->block_counter++; | |
| 3162 | } | |
| 3163 | } | |
| 3164 | ||
| 3165 | static inline void | |
| 3166 | sn_crypto_stream_chacha20_ietf_xor_wrap_final( | |
| 3167 | js_env_t *, | |
| 3168 | js_receiver_t, | |
| 3169 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state | |
| 3170 | ) { | |
| 3171 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3172 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3173 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3174 | state->remainder = 0; | |
| 3175 | } | |
| 3176 | ||
| 3177 | struct sn_crypto_stream_xchacha20_xor_state { | |
| 3178 | unsigned char n[crypto_stream_xchacha20_NONCEBYTES]; | |
| 3179 | unsigned char k[crypto_stream_xchacha20_KEYBYTES]; | |
| 3180 | unsigned char next_block[64]; | |
| 3181 | int remainder; | |
| 3182 | uint64_t block_counter; | |
| 3183 | }; | |
| 3184 | ||
| 3185 | static inline void | |
| 3186 | sn_crypto_stream_xchacha20_xor_wrap_init( | |
| 3187 | js_env_t *, | |
| 3188 | js_receiver_t, | |
| 3189 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state, | |
| 3190 | js_typedarray_span_t<> n, | |
| 3191 | js_typedarray_span_t<> k | |
| 3192 | ) { | |
| 3193 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 3194 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 3195 | ||
| 3196 | state->remainder = 0; | |
| 3197 | state->block_counter = 0; | |
| 3198 | memcpy(state->n, n.data(), crypto_stream_xchacha20_NONCEBYTES); | |
| 3199 | memcpy(state->k, k.data(), crypto_stream_xchacha20_KEYBYTES); | |
| 3200 | } | |
| 3201 | ||
| 3202 | static inline void | |
| 3203 | sn_crypto_stream_xchacha20_xor_wrap_update( | |
| 3204 | js_env_t *, | |
| 3205 | js_receiver_t, | |
| 3206 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state, | |
| 3207 | js_typedarray_span_t<> c, | |
| 3208 | js_typedarray_span_t<> m | |
| 3209 | ) { | |
| 3210 | assert(c.size_bytes() == m.size_bytes()); | |
| 3211 | ||
| 3212 | auto *next_block = state->next_block; | |
| 3213 | ||
| 3214 | size_t m_size = m.size_bytes(); | |
| 3215 | auto *c_ptr = c.data(); | |
| 3216 | auto *m_ptr = m.data(); | |
| 3217 | ||
| 3218 | if (state->remainder) { | |
| 3219 | uint64_t offset = 0; | |
| 3220 | int rem = state->remainder; | |
| 3221 | ||
| 3222 | while (rem < 64 && offset < m_size) { | |
| 3223 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3224 | ++offset; | |
| 3225 | ++rem; | |
| 3226 | } | |
| 3227 | ||
| 3228 | c_ptr += offset; | |
| 3229 | m_ptr += offset; | |
| 3230 | m_size -= offset; | |
| 3231 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3232 | ||
| 3233 | if (m_size == 0) return; | |
| 3234 | } | |
| 3235 | ||
| 3236 | state->remainder = m_size & 63; | |
| 3237 | size_t main_len = m_size - state->remainder; | |
| 3238 | ||
| 3239 | crypto_stream_xchacha20_xor_ic( | |
| 3240 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3241 | ); | |
| 3242 | ||
| 3243 | state->block_counter += main_len / 64; | |
| 3244 | ||
| 3245 | if (state->remainder) { | |
| 3246 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3247 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3248 | ||
| 3249 | crypto_stream_xchacha20_xor_ic( | |
| 3250 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3251 | ); | |
| 3252 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3253 | ||
| 3254 | state->block_counter++; | |
| 3255 | } | |
| 3256 | } | |
| 3257 | ||
| 3258 | static inline void | |
| 3259 | sn_crypto_stream_xchacha20_xor_wrap_final( | |
| 3260 | js_env_t *, | |
| 3261 | js_receiver_t, | |
| 3262 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state | |
| 3263 | ) { | |
| 3264 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3265 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3266 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3267 | state->remainder = 0; | |
| 3268 | } | |
| 3269 | ||
| 3270 | struct sn_crypto_stream_salsa20_xor_state { | |
| 3271 | unsigned char n[crypto_stream_salsa20_NONCEBYTES]; | |
| 3272 | unsigned char k[crypto_stream_salsa20_KEYBYTES]; | |
| 3273 | unsigned char next_block[64]; | |
| 3274 | int remainder; | |
| 3275 | uint64_t block_counter; | |
| 3276 | }; | |
| 3277 | ||
| 3278 | static inline void | |
| 3279 | sn_crypto_stream_salsa20_xor_wrap_init( | |
| 3280 | js_env_t *, | |
| 3281 | js_receiver_t, | |
| 3282 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state, | |
| 3283 | js_typedarray_span_t<> n, | |
| 3284 | js_typedarray_span_t<> k | |
| 3285 | ) { | |
| 3286 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 3287 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 3288 | ||
| 3289 | state->remainder = 0; | |
| 3290 | state->block_counter = 0; | |
| 3291 | memcpy(state->n, n.data(), crypto_stream_salsa20_NONCEBYTES); | |
| 3292 | memcpy(state->k, k.data(), crypto_stream_salsa20_KEYBYTES); | |
| 3293 | } | |
| 3294 | ||
| 3295 | static inline void | |
| 3296 | sn_crypto_stream_salsa20_xor_wrap_update( | |
| 3297 | js_env_t *, | |
| 3298 | js_receiver_t, | |
| 3299 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state, | |
| 3300 | js_typedarray_span_t<> c, | |
| 3301 | js_typedarray_span_t<> m | |
| 3302 | ) { | |
| 3303 | assert(c.size_bytes() == m.size_bytes()); | |
| 3304 | ||
| 3305 | auto *next_block = state->next_block; | |
| 3306 | ||
| 3307 | size_t m_size = m.size_bytes(); | |
| 3308 | auto *c_ptr = c.data(); | |
| 3309 | auto *m_ptr = m.data(); | |
| 3310 | ||
| 3311 | if (state->remainder) { | |
| 3312 | uint64_t offset = 0; | |
| 3313 | int rem = state->remainder; | |
| 3314 | ||
| 3315 | while (rem < 64 && offset < m_size) { | |
| 3316 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3317 | ++offset; | |
| 3318 | ++rem; | |
| 3319 | } | |
| 3320 | ||
| 3321 | c_ptr += offset; | |
| 3322 | m_ptr += offset; | |
| 3323 | m_size -= offset; | |
| 3324 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3325 | ||
| 3326 | if (m_size == 0) return; | |
| 3327 | } | |
| 3328 | ||
| 3329 | state->remainder = m_size & 63; | |
| 3330 | size_t main_len = m_size - state->remainder; | |
| 3331 | ||
| 3332 | crypto_stream_salsa20_xor_ic( | |
| 3333 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3334 | ); | |
| 3335 | ||
| 3336 | state->block_counter += main_len / 64; | |
| 3337 | ||
| 3338 | if (state->remainder) { | |
| 3339 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3340 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3341 | ||
| 3342 | crypto_stream_salsa20_xor_ic( | |
| 3343 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3344 | ); | |
| 3345 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3346 | ||
| 3347 | state->block_counter++; | |
| 3348 | } | |
| 3349 | } | |
| 3350 | ||
| 3351 | static inline void | |
| 3352 | sn_crypto_stream_salsa20_xor_wrap_final( | |
| 3353 | js_env_t *, | |
| 3354 | js_receiver_t, | |
| 3355 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state | |
| 3356 | ) { | |
| 3357 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3358 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3359 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3360 | state->remainder = 0; | |
| 3361 | } | |
| 3362 | ||
| 3363 | // Experimental API | |
| 3364 | ||
| 3365 | static inline void | |
| 3366 | sn_extension_tweak_ed25519_base( | |
| 3367 | js_env_t *, | |
| 3368 | js_receiver_t, | |
| 3369 | js_typedarray_span_t<> n, | |
| 3370 | js_typedarray_span_t<> p, | |
| 3371 | js_typedarray_span_t<> ns | |
| 3372 | ) { | |
| 3373 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3374 | assert(p.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3375 | ||
| 3376 | sn__extension_tweak_ed25519_base(p.data(), n.data(), ns.data(), ns.size_bytes()); | |
| 3377 | } | |
| 3378 | ||
| 3379 | static inline int | |
| 3380 | sn_extension_tweak_ed25519_sign_detached( | |
| 3381 | js_env_t *, | |
| 3382 | js_receiver_t, | |
| 3383 | js_typedarray_span_t<> sig, | |
| 3384 | js_typedarray_span_t<> m, | |
| 3385 | js_typedarray_span_t<> scalar, | |
| 3386 | std::optional<js_typedarray_span_t<>> pk | |
| 3387 | ) { | |
| 3388 | assert(sig.size_bytes() == crypto_sign_BYTES); | |
| 3389 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3390 | ||
| 3391 | uint8_t *pk_data = nullptr; | |
| 3392 | if (pk) { | |
| 3393 | assert(pk->size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3394 | pk_data = pk->data(); | |
| 3395 | } | |
| 3396 | ||
| 3397 | return sn__extension_tweak_ed25519_sign_detached( | |
| 3398 | sig.data(), | |
| 3399 | nullptr, | |
| 3400 | m.data(), | |
| 3401 | m.size_bytes(), | |
| 3402 | scalar.data(), | |
| 3403 | pk_data | |
| 3404 | ); | |
| 3405 | } | |
| 3406 | ||
| 3407 | static inline void | |
| 3408 | sn_extension_tweak_ed25519_sk_to_scalar( | |
| 3409 | js_env_t *, | |
| 3410 | js_receiver_t, | |
| 3411 | js_typedarray_span_t<> n, | |
| 3412 | js_typedarray_span_t<> sk | |
| 3413 | ) { | |
| 3414 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3415 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 3416 | ||
| 3417 | sn__extension_tweak_ed25519_sk_to_scalar(n.data(), sk.data()); | |
| 3418 | } | |
| 3419 | ||
| 3420 | static inline void | |
| 3421 | sn_extension_tweak_ed25519_scalar( | |
| 3422 | js_env_t *, | |
| 3423 | js_receiver_t, | |
| 3424 | js_typedarray_span_t<> scalar_out, | |
| 3425 | js_typedarray_span_t<> scalar, | |
| 3426 | js_typedarray_span_t<> ns | |
| 3427 | ) { | |
| 3428 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3429 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3430 | ||
| 3431 | sn__extension_tweak_ed25519_scalar(scalar_out.data(), scalar.data(), ns.data(), ns.size_bytes()); | |
| 3432 | } | |
| 3433 | ||
| 3434 | static inline int | |
| 3435 | sn_extension_tweak_ed25519_pk( | |
| 3436 | js_env_t *, | |
| 3437 | js_receiver_t, | |
| 3438 | js_typedarray_span_t<> tpk, | |
| 3439 | js_typedarray_span_t<> pk, | |
| 3440 | js_typedarray_span_t<> ns | |
| 3441 | ) { | |
| 3442 | assert(tpk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3443 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3444 | ||
| 3445 | return sn__extension_tweak_ed25519_pk(tpk.data(), pk.data(), ns.data(), ns.size_bytes()); | |
| 3446 | } | |
| 3447 | ||
| 3448 | static inline void | |
| 3449 | sn_extension_tweak_ed25519_keypair( | |
| 3450 | js_env_t *, | |
| 3451 | js_receiver_t, | |
| 3452 | js_typedarray_span_t<> pk, | |
| 3453 | js_typedarray_span_t<> scalar_out, | |
| 3454 | js_typedarray_span_t<> scalar_in, | |
| 3455 | js_typedarray_span_t<> ns | |
| 3456 | ) { | |
| 3457 | assert(pk.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3458 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3459 | assert(scalar_in.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3460 | ||
| 3461 | sn__extension_tweak_ed25519_keypair( | |
| 3462 | pk.data(), | |
| 3463 | scalar_out.data(), | |
| 3464 | scalar_in.data(), | |
| 3465 | ns.data(), | |
| 3466 | ns.size_bytes() | |
| 3467 | ); | |
| 3468 | } | |
| 3469 | ||
| 3470 | static inline void | |
| 3471 | sn_extension_tweak_ed25519_scalar_add( | |
| 3472 | js_env_t *, | |
| 3473 | js_receiver_t, | |
| 3474 | js_typedarray_span_t<> scalar_out, | |
| 3475 | js_typedarray_span_t<> scalar, | |
| 3476 | js_typedarray_span_t<> n | |
| 3477 | ) { | |
| 3478 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3479 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3480 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3481 | ||
| 3482 | sn__extension_tweak_ed25519_scalar_add(scalar_out.data(), scalar.data(), n.data()); | |
| 3483 | } | |
| 3484 | ||
| 3485 | static inline int | |
| 3486 | sn_extension_tweak_ed25519_pk_add( | |
| 3487 | js_env_t *, | |
| 3488 | js_receiver_t, | |
| 3489 | js_typedarray_span_t<> tpk, | |
| 3490 | js_typedarray_span_t<> pk, | |
| 3491 | js_typedarray_span_t<> p | |
| 3492 | ) { | |
| 3493 | assert(tpk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3494 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3495 | assert(p.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3496 | ||
| 3497 | return sn__extension_tweak_ed25519_pk_add(tpk.data(), pk.data(), p.data()); | |
| 3498 | } | |
| 3499 | ||
| 3500 | static inline int | |
| 3501 | sn_extension_tweak_ed25519_keypair_add( | |
| 3502 | js_env_t *, | |
| 3503 | js_receiver_t, | |
| 3504 | js_typedarray_span_t<> pk, | |
| 3505 | js_typedarray_span_t<> scalar_out, | |
| 3506 | js_typedarray_span_t<> scalar_in, | |
| 3507 | js_typedarray_span_t<> tweak | |
| 3508 | ) { | |
| 3509 | assert(pk.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3510 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3511 | assert(scalar_in.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3512 | assert(tweak.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3513 | ||
| 3514 | return sn__extension_tweak_ed25519_keypair_add( | |
| 3515 | pk.data(), | |
| 3516 | scalar_out.data(), | |
| 3517 | scalar_in.data(), | |
| 3518 | tweak.data() | |
| 3519 | ); | |
| 3520 | } | |
| 3521 | ||
| 3522 | static inline int | |
| 3523 | sn_extension_pbkdf2_sha512( | |
| 3524 | js_env_t *, | |
| 3525 | js_receiver_t, | |
| 3526 | js_typedarray_span_t<> out, | |
| 3527 | js_typedarray_span_t<> passwd, | |
| 3528 | js_typedarray_span_t<> salt, | |
| 3529 | uint64_t iter, | |
| 3530 | uint64_t outlen | |
| 3531 | ) { | |
| 3532 | ||
| 3533 | assert(iter >= sn__extension_pbkdf2_sha512_ITERATIONS_MIN); | |
| 3534 | assert(outlen <= sn__extension_pbkdf2_sha512_BYTES_MAX); | |
| 3535 | assert(out.size_bytes() >= static_cast<size_t>(outlen)); | |
| 3536 | ||
| 3537 | return sn__extension_pbkdf2_sha512( | |
| 3538 | passwd.data(), | |
| 3539 | passwd.size_bytes(), | |
| 3540 | salt.data(), | |
| 3541 | salt.size_bytes(), | |
| 3542 | iter, | |
| 3543 | out.data(), | |
| 3544 | outlen | |
| 3545 | ); | |
| 3546 | } | |
| 3547 | ||
| 3548 | struct sn_async_pbkdf2_sha512_request : sn_async_task { | |
| 3549 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 3550 | std::span<uint8_t> out; | |
| 3551 | ||
| 3552 | size_t outlen; | |
| 3553 | ||
| 3554 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 3555 | std::span<uint8_t> pwd; | |
| 3556 | ||
| 3557 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 3558 | std::span<uint8_t> salt; | |
| 3559 | ||
| 3560 | uint64_t iter; | |
| 3561 | ||
| 3562 | sn_async_pbkdf2_sha512_request(js_env_t *env) : sn_async_task(env) {} | |
| 3563 | }; | |
| 3564 | ||
| 3565 | static void | |
| 3566 | async_pbkdf2_sha512_execute(uv_work_t *uv_req) { | |
| 3567 | auto *req = reinterpret_cast<sn_async_pbkdf2_sha512_request *>(uv_req); | |
| 3568 | ||
| 3569 | req->code = sn__extension_pbkdf2_sha512( | |
| 3570 | req->pwd.data(), | |
| 3571 | req->pwd.size(), | |
| 3572 | req->salt.data(), | |
| 3573 | req->salt.size(), | |
| 3574 | req->iter, | |
| 3575 | req->out.data(), | |
| 3576 | req->outlen | |
| 3577 | ); | |
| 3578 | } | |
| 3579 | ||
| 3580 | static void | |
| 3581 | async_pbkdf2_sha512_complete(uv_work_t *uv_req, int status) { | |
| 3582 | int err; | |
| 3583 | ||
| 3584 | auto *req = reinterpret_cast<sn_async_pbkdf2_sha512_request *>(uv_req); | |
| 3585 | ||
| 3586 | if (req->exiting) return delete req; | |
| 3587 | ||
| 3588 | js_handle_scope_t *scope; | |
| 3589 | err = js_open_handle_scope(req->env, &scope); | |
| 3590 | assert(err == 0); | |
| 3591 | ||
| 3592 | js_function_t<void, int> callback; | |
| 3593 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 3594 | assert(err == 0); | |
| 3595 | ||
| 3596 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 3597 | assert(err != js_pending_exception); | |
| 3598 | ||
| 3599 | err = js_close_handle_scope(req->env, scope); | |
| 3600 | assert(err == 0); | |
| 3601 | ||
| 3602 | delete req; | |
| 3603 | } | |
| 3604 | ||
| 3605 | static inline void | |
| 3606 | sn_extension_pbkdf2_sha512_async( | |
| 3607 | js_env_t *env, | |
| 3608 | js_receiver_t, | |
| 3609 | ||
| 3610 | js_arraybuffer_t out, | |
| 3611 | uint32_t out_offset, | |
| 3612 | uint32_t out_len, | |
| 3613 | ||
| 3614 | js_arraybuffer_t pwd, | |
| 3615 | uint32_t pwd_offset, | |
| 3616 | uint32_t pwd_len, | |
| 3617 | ||
| 3618 | js_arraybuffer_t salt, | |
| 3619 | uint32_t salt_offset, | |
| 3620 | uint32_t salt_len, | |
| 3621 | ||
| 3622 | uint64_t iter, | |
| 3623 | uint64_t outlen, | |
| 3624 | ||
| 3625 | js_function_t<void, int> callback | |
| 3626 | ) { | |
| 3627 | int err; | |
| 3628 | ||
| 3629 | auto *req = new sn_async_pbkdf2_sha512_request(env); | |
| 3630 | ||
| 3631 | std::span<uint8_t> out_view; | |
| 3632 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 3633 | assert(err == 0); | |
| 3634 | assert(out_offset + out_len <= out_view.size()); | |
| 3635 | ||
| 3636 | req->out = {&out_view[out_offset], out_len}; | |
| 3637 | ||
| 3638 | std::span<uint8_t> pwd_view; | |
| 3639 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 3640 | assert(err == 0); | |
| 3641 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 3642 | ||
| 3643 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 3644 | ||
| 3645 | std::span<uint8_t> salt_view; | |
| 3646 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 3647 | assert(err == 0); | |
| 3648 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 3649 | ||
| 3650 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 3651 | ||
| 3652 | req->iter = iter; | |
| 3653 | req->outlen = outlen; | |
| 3654 | ||
| 3655 | err = js_create_reference(env, out, req->out_ref); | |
| 3656 | assert(err == 0); | |
| 3657 | ||
| 3658 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 3659 | assert(err == 0); | |
| 3660 | ||
| 3661 | err = js_create_reference(env, salt, req->salt_ref); | |
| 3662 | assert(err == 0); | |
| 3663 | ||
| 3664 | err = js_create_reference(env, callback, req->cb); | |
| 3665 | assert(err == 0); | |
| 3666 | ||
| 3667 | uv_loop_t *loop; | |
| 3668 | err = js_get_env_loop(env, &loop); | |
| 3669 | assert(err == 0); | |
| 3670 | ||
| 3671 | err = uv_queue_work(loop, &req->task, async_pbkdf2_sha512_execute, async_pbkdf2_sha512_complete); | |
| 3672 | assert(err == 0); | |
| 3673 | } | |
| 3674 | ||
| 3675 | static js_value_t * | |
| 3676 | sodium_native_exports(js_env_t *env, js_value_t *exports) { | |
| 3677 | int err; | |
| 3678 | ||
| 3679 | err = sodium_init(); | |
| 3680 | assert(err >= 0); | |
| 3681 | ||
| 3682 | #define V_FUNCTION(name, fn) \ | |
| 3683 | err = js_set_property<fn>(env, exports, name); \ | |
| 3684 | assert(err == 0); | |
| 3685 | ||
| 3686 | #define V_FUNCTION_NOSCOPE(name, fn) \ | |
| 3687 | err = js_set_property<fn, js_function_options_t{.scoped = false}>(env, exports, name); \ | |
| 3688 | assert(err == 0); | |
| 3689 | ||
| 3690 | #define V_UINT32(name, constant) \ | |
| 3691 | err = js_set_property(env, exports, name, static_cast<uint32_t>(constant)); \ | |
| 3692 | assert(err == 0); | |
| 3693 | ||
| 3694 | #define V_UINT64(name, constant) \ | |
| 3695 | err = js_set_property(env, exports, name, static_cast<int64_t>(std::min<uint64_t>(constant, js_max_safe_integer))); \ | |
| 3696 | assert(err == 0); | |
| 3697 | ||
| 3698 | #define V_STRING(name, str) \ | |
| 3699 | err = js_set_property(env, exports, name, str); \ | |
| 3700 | assert(err == 0); | |
| 3701 | ||
| 3702 | // memory | |
| 3703 | ||
| 3704 | V_FUNCTION("sodium_memzero", sn_sodium_memzero); | |
| 3705 | V_FUNCTION("sodium_mlock", sn_sodium_mlock); | |
| 3706 | V_FUNCTION("sodium_munlock", sn_sodium_munlock); | |
| 3707 | V_FUNCTION("sodium_malloc", sn_sodium_malloc); | |
| 3708 | V_FUNCTION("sodium_free", sn_sodium_free); | |
| 3709 | V_FUNCTION("sodium_mprotect_noaccess", sn_sodium_mprotect_noaccess); | |
| 3710 | V_FUNCTION("sodium_mprotect_readonly", sn_sodium_mprotect_readonly); | |
| 3711 | V_FUNCTION("sodium_mprotect_readwrite", sn_sodium_mprotect_readwrite); | |
| 3712 | ||
| 3713 | // randombytes | |
| 3714 | ||
| 3715 | V_FUNCTION_NOSCOPE("randombytes_buf", sn_randombytes_buf); | |
| 3716 | V_FUNCTION_NOSCOPE("randombytes_buf_deterministic", sn_randombytes_buf_deterministic); | |
| 3717 | V_FUNCTION_NOSCOPE("randombytes_random", sn_randombytes_random); | |
| 3718 | V_FUNCTION_NOSCOPE("randombytes_uniform", sn_randombytes_uniform); | |
| 3719 | ||
| 3720 | V_UINT32("randombytes_SEEDBYTES", randombytes_SEEDBYTES); | |
| 3721 | ||
| 3722 | // sodium helpers | |
| 3723 | ||
| 3724 | V_FUNCTION("sodium_memcmp", sn_sodium_memcmp); | |
| 3725 | V_FUNCTION("sodium_increment", sn_sodium_increment); | |
| 3726 | V_FUNCTION("sodium_add", sn_sodium_add); | |
| 3727 | V_FUNCTION("sodium_sub", sn_sodium_sub); | |
| 3728 | V_FUNCTION("sodium_compare", sn_sodium_compare); | |
| 3729 | V_FUNCTION("sodium_is_zero", sn_sodium_is_zero); | |
| 3730 | V_FUNCTION("sodium_pad", sn_sodium_pad); | |
| 3731 | V_FUNCTION("sodium_unpad", sn_sodium_unpad); | |
| 3732 | ||
| 3733 | // crypto_aead | |
| 3734 | ||
| 3735 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_keygen", sn_crypto_aead_xchacha20poly1305_ietf_keygen); | |
| 3736 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_encrypt", sn_crypto_aead_xchacha20poly1305_ietf_encrypt); | |
| 3737 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_decrypt", sn_crypto_aead_xchacha20poly1305_ietf_decrypt); | |
| 3738 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_encrypt_detached", sn_crypto_aead_xchacha20poly1305_ietf_encrypt_detached); | |
| 3739 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_decrypt_detached", sn_crypto_aead_xchacha20poly1305_ietf_decrypt_detached); | |
| 3740 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_ABYTES", crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 3741 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_KEYBYTES", crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 3742 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_NPUBBYTES", crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 3743 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_NSECBYTES", crypto_aead_xchacha20poly1305_ietf_NSECBYTES); | |
| 3744 | V_UINT64("crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX", crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX); | |
| 3745 | ||
| 3746 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_keygen", sn_crypto_aead_chacha20poly1305_ietf_keygen); | |
| 3747 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_encrypt", sn_crypto_aead_chacha20poly1305_ietf_encrypt); | |
| 3748 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_decrypt", sn_crypto_aead_chacha20poly1305_ietf_decrypt); | |
| 3749 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_encrypt_detached", sn_crypto_aead_chacha20poly1305_ietf_encrypt_detached); | |
| 3750 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_decrypt_detached", sn_crypto_aead_chacha20poly1305_ietf_decrypt_detached); | |
| 3751 | V_UINT32("crypto_aead_chacha20poly1305_ietf_ABYTES", crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 3752 | V_UINT32("crypto_aead_chacha20poly1305_ietf_KEYBYTES", crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 3753 | V_UINT32("crypto_aead_chacha20poly1305_ietf_NPUBBYTES", crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 3754 | V_UINT32("crypto_aead_chacha20poly1305_ietf_NSECBYTES", crypto_aead_chacha20poly1305_ietf_NSECBYTES); | |
| 3755 | V_UINT64("crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX", crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX); | |
| 3756 | ||
| 3757 | // crypto_auth | |
| 3758 | ||
| 3759 | V_FUNCTION("crypto_auth", sn_crypto_auth); | |
| 3760 | V_FUNCTION("crypto_auth_verify", sn_crypto_auth_verify); | |
| 3761 | V_UINT32("crypto_auth_BYTES", crypto_auth_BYTES); | |
| 3762 | V_UINT32("crypto_auth_KEYBYTES", crypto_auth_KEYBYTES); | |
| 3763 | V_STRING("crypto_auth_PRIMITIVE", crypto_auth_PRIMITIVE); | |
| 3764 | ||
| 3765 | // crypto_box | |
| 3766 | ||
| 3767 | V_FUNCTION("crypto_box_keypair", sn_crypto_box_keypair); | |
| 3768 | V_FUNCTION("crypto_box_seed_keypair", sn_crypto_box_seed_keypair); | |
| 3769 | V_FUNCTION("crypto_box_easy", sn_crypto_box_easy); | |
| 3770 | V_FUNCTION("crypto_box_open_easy", sn_crypto_box_open_easy); | |
| 3771 | V_FUNCTION("crypto_box_detached", sn_crypto_box_detached); | |
| 3772 | V_FUNCTION("crypto_box_open_detached", sn_crypto_box_open_detached); | |
| 3773 | V_FUNCTION("crypto_box_seal", sn_crypto_box_seal); | |
| 3774 | V_FUNCTION_NOSCOPE("crypto_box_seal_open", sn_crypto_box_seal_open); | |
| 3775 | ||
| 3776 | V_UINT32("crypto_box_SEEDBYTES", crypto_box_SEEDBYTES); | |
| 3777 | V_UINT32("crypto_box_PUBLICKEYBYTES", crypto_box_PUBLICKEYBYTES); | |
| 3778 | V_UINT32("crypto_box_SECRETKEYBYTES", crypto_box_SECRETKEYBYTES); | |
| 3779 | V_UINT32("crypto_box_NONCEBYTES", crypto_box_NONCEBYTES); | |
| 3780 | V_UINT32("crypto_box_MACBYTES", crypto_box_MACBYTES); | |
| 3781 | V_UINT32("crypto_box_SEALBYTES", crypto_box_SEALBYTES); | |
| 3782 | V_STRING("crypto_box_PRIMITIVE", crypto_box_PRIMITIVE); | |
| 3783 | ||
| 3784 | // crypto_core | |
| 3785 | ||
| 3786 | V_FUNCTION("crypto_core_ed25519_is_valid_point", sn_crypto_core_ed25519_is_valid_point); | |
| 3787 | V_FUNCTION("crypto_core_ed25519_from_uniform", sn_crypto_core_ed25519_from_uniform); | |
| 3788 | V_FUNCTION("crypto_core_ed25519_add", sn_crypto_core_ed25519_add); | |
| 3789 | V_FUNCTION("crypto_core_ed25519_sub", sn_crypto_core_ed25519_sub); | |
| 3790 | V_FUNCTION("crypto_core_ed25519_scalar_random", sn_crypto_core_ed25519_scalar_random); | |
| 3791 | V_FUNCTION("crypto_core_ed25519_scalar_reduce", sn_crypto_core_ed25519_scalar_reduce); | |
| 3792 | V_FUNCTION("crypto_core_ed25519_scalar_invert", sn_crypto_core_ed25519_scalar_invert); | |
| 3793 | V_FUNCTION("crypto_core_ed25519_scalar_negate", sn_crypto_core_ed25519_scalar_negate); | |
| 3794 | V_FUNCTION("crypto_core_ed25519_scalar_complement", sn_crypto_core_ed25519_scalar_complement); | |
| 3795 | V_FUNCTION("crypto_core_ed25519_scalar_add", sn_crypto_core_ed25519_scalar_add); | |
| 3796 | V_FUNCTION("crypto_core_ed25519_scalar_sub", sn_crypto_core_ed25519_scalar_sub); | |
| 3797 | V_UINT32("crypto_core_ed25519_BYTES", crypto_core_ed25519_BYTES); | |
| 3798 | V_UINT32("crypto_core_ed25519_UNIFORMBYTES", crypto_core_ed25519_UNIFORMBYTES); | |
| 3799 | V_UINT32("crypto_core_ed25519_SCALARBYTES", crypto_core_ed25519_SCALARBYTES); | |
| 3800 | V_UINT32("crypto_core_ed25519_NONREDUCEDSCALARBYTES", crypto_core_ed25519_NONREDUCEDSCALARBYTES); | |
| 3801 | ||
| 3802 | // crypto_kdf | |
| 3803 | ||
| 3804 | V_FUNCTION("crypto_kdf_keygen", sn_crypto_kdf_keygen); | |
| 3805 | V_FUNCTION("crypto_kdf_derive_from_key", sn_crypto_kdf_derive_from_key); | |
| 3806 | V_UINT32("crypto_kdf_BYTES_MIN", crypto_kdf_BYTES_MIN); | |
| 3807 | V_UINT32("crypto_kdf_BYTES_MAX", crypto_kdf_BYTES_MAX); | |
| 3808 | V_UINT32("crypto_kdf_CONTEXTBYTES", crypto_kdf_CONTEXTBYTES); | |
| 3809 | V_UINT32("crypto_kdf_KEYBYTES", crypto_kdf_KEYBYTES); | |
| 3810 | V_STRING("crypto_kdf_PRIMITIVE", crypto_kdf_PRIMITIVE); | |
| 3811 | ||
| 3812 | // crypto_kx | |
| 3813 | ||
| 3814 | V_FUNCTION("crypto_kx_keypair", sn_crypto_kx_keypair); | |
| 3815 | V_FUNCTION("crypto_kx_seed_keypair", sn_crypto_kx_seed_keypair); | |
| 3816 | V_FUNCTION("crypto_kx_client_session_keys", sn_crypto_kx_client_session_keys); | |
| 3817 | V_FUNCTION("crypto_kx_server_session_keys", sn_crypto_kx_server_session_keys); | |
| 3818 | V_UINT32("crypto_kx_PUBLICKEYBYTES", crypto_kx_PUBLICKEYBYTES); | |
| 3819 | V_UINT32("crypto_kx_SECRETKEYBYTES", crypto_kx_SECRETKEYBYTES); | |
| 3820 | V_UINT32("crypto_kx_SEEDBYTES", crypto_kx_SEEDBYTES); | |
| 3821 | V_UINT32("crypto_kx_SESSIONKEYBYTES", crypto_kx_SESSIONKEYBYTES); | |
| 3822 | V_STRING("crypto_kx_PRIMITIVE", crypto_kx_PRIMITIVE); | |
| 3823 | ||
| 3824 | // crypto_generichash | |
| 3825 | ||
| 3826 | V_FUNCTION_NOSCOPE("crypto_generichash", sn_crypto_generichash); | |
| 3827 | V_FUNCTION("crypto_generichash_batch", sn_crypto_generichash_batch); | |
| 3828 | V_FUNCTION_NOSCOPE("crypto_generichash_keygen", sn_crypto_generichash_keygen); | |
| 3829 | V_FUNCTION_NOSCOPE("crypto_generichash_init", sn_crypto_generichash_init); | |
| 3830 | V_FUNCTION_NOSCOPE("crypto_generichash_update", sn_crypto_generichash_update); | |
| 3831 | V_FUNCTION_NOSCOPE("crypto_generichash_final", sn_crypto_generichash_final); | |
| 3832 | ||
| 3833 | V_UINT32("crypto_generichash_STATEBYTES", sizeof(crypto_generichash_state)); | |
| 3834 | V_STRING("crypto_generichash_PRIMITIVE", crypto_generichash_PRIMITIVE); | |
| 3835 | V_UINT32("crypto_generichash_BYTES_MIN", crypto_generichash_BYTES_MIN); | |
| 3836 | V_UINT32("crypto_generichash_BYTES_MAX", crypto_generichash_BYTES_MAX); | |
| 3837 | V_UINT32("crypto_generichash_BYTES", crypto_generichash_BYTES); | |
| 3838 | V_UINT32("crypto_generichash_KEYBYTES_MIN", crypto_generichash_KEYBYTES_MIN); | |
| 3839 | V_UINT32("crypto_generichash_KEYBYTES_MAX", crypto_generichash_KEYBYTES_MAX); | |
| 3840 | V_UINT32("crypto_generichash_KEYBYTES", crypto_generichash_KEYBYTES); | |
| 3841 | ||
| 3842 | // crypto_hash | |
| 3843 | ||
| 3844 | V_FUNCTION("crypto_hash", sn_crypto_hash); | |
| 3845 | V_UINT32("crypto_hash_BYTES", crypto_hash_BYTES); | |
| 3846 | V_STRING("crypto_hash_PRIMITIVE", crypto_hash_PRIMITIVE); | |
| 3847 | ||
| 3848 | V_FUNCTION("crypto_hash_sha256", sn_crypto_hash_sha256); | |
| 3849 | V_FUNCTION("crypto_hash_sha256_init", sn_crypto_hash_sha256_init); | |
| 3850 | V_FUNCTION("crypto_hash_sha256_update", sn_crypto_hash_sha256_update); | |
| 3851 | V_FUNCTION("crypto_hash_sha256_final", sn_crypto_hash_sha256_final); | |
| 3852 | V_UINT32("crypto_hash_sha256_STATEBYTES", sizeof(crypto_hash_sha256_state)); | |
| 3853 | V_UINT32("crypto_hash_sha256_BYTES", crypto_hash_sha256_BYTES); | |
| 3854 | ||
| 3855 | V_FUNCTION("crypto_hash_sha512", sn_crypto_hash_sha512); | |
| 3856 | V_FUNCTION("crypto_hash_sha512_init", sn_crypto_hash_sha512_init); | |
| 3857 | V_FUNCTION("crypto_hash_sha512_update", sn_crypto_hash_sha512_update); | |
| 3858 | V_FUNCTION("crypto_hash_sha512_final", sn_crypto_hash_sha512_final); | |
| 3859 | V_UINT32("crypto_hash_sha512_STATEBYTES", sizeof(crypto_hash_sha512_state)); | |
| 3860 | V_UINT32("crypto_hash_sha512_BYTES", crypto_hash_sha512_BYTES); | |
| 3861 | ||
| 3862 | // crypto_onetimeauth | |
| 3863 | ||
| 3864 | V_FUNCTION("crypto_onetimeauth", sn_crypto_onetimeauth); | |
| 3865 | V_FUNCTION("crypto_onetimeauth_verify", sn_crypto_onetimeauth_verify); | |
| 3866 | V_FUNCTION("crypto_onetimeauth_init", sn_crypto_onetimeauth_init); | |
| 3867 | V_FUNCTION("crypto_onetimeauth_update", sn_crypto_onetimeauth_update); | |
| 3868 | V_FUNCTION("crypto_onetimeauth_final", sn_crypto_onetimeauth_final); | |
| 3869 | V_UINT32("crypto_onetimeauth_STATEBYTES", sizeof(crypto_onetimeauth_state)); | |
| 3870 | V_UINT32("crypto_onetimeauth_BYTES", crypto_onetimeauth_BYTES); | |
| 3871 | V_UINT32("crypto_onetimeauth_KEYBYTES", crypto_onetimeauth_KEYBYTES); | |
| 3872 | V_STRING("crypto_onetimeauth_PRIMITIVE", crypto_onetimeauth_PRIMITIVE); | |
| 3873 | ||
| 3874 | // crypto_pwhash | |
| 3875 | ||
| 3876 | V_FUNCTION("crypto_pwhash", sn_crypto_pwhash); | |
| 3877 | V_FUNCTION("crypto_pwhash_str", sn_crypto_pwhash_str); | |
| 3878 | V_FUNCTION("crypto_pwhash_str_verify", sn_crypto_pwhash_str_verify); | |
| 3879 | V_FUNCTION("crypto_pwhash_str_needs_rehash", sn_crypto_pwhash_str_needs_rehash); | |
| 3880 | V_FUNCTION("crypto_pwhash_async", sn_crypto_pwhash_async); | |
| 3881 | V_FUNCTION("crypto_pwhash_str_async", sn_crypto_pwhash_str_async); | |
| 3882 | V_FUNCTION("crypto_pwhash_str_verify_async", sn_crypto_pwhash_str_verify_async); | |
| 3883 | V_UINT32("crypto_pwhash_ALG_ARGON2I13", crypto_pwhash_ALG_ARGON2I13); | |
| 3884 | V_UINT32("crypto_pwhash_ALG_ARGON2ID13", crypto_pwhash_ALG_ARGON2ID13); | |
| 3885 | V_UINT32("crypto_pwhash_ALG_DEFAULT", crypto_pwhash_ALG_DEFAULT); | |
| 3886 | V_UINT32("crypto_pwhash_BYTES_MIN", crypto_pwhash_BYTES_MIN); | |
| 3887 | V_UINT32("crypto_pwhash_BYTES_MAX", crypto_pwhash_BYTES_MAX); | |
| 3888 | V_UINT32("crypto_pwhash_PASSWD_MIN", crypto_pwhash_PASSWD_MIN); | |
| 3889 | V_UINT32("crypto_pwhash_PASSWD_MAX", crypto_pwhash_PASSWD_MAX); | |
| 3890 | V_UINT32("crypto_pwhash_SALTBYTES", crypto_pwhash_SALTBYTES); | |
| 3891 | V_UINT32("crypto_pwhash_STRBYTES", crypto_pwhash_STRBYTES); | |
| 3892 | V_STRING("crypto_pwhash_STRPREFIX", crypto_pwhash_STRPREFIX); | |
| 3893 | V_UINT32("crypto_pwhash_OPSLIMIT_MIN", crypto_pwhash_OPSLIMIT_MIN); | |
| 3894 | V_UINT32("crypto_pwhash_OPSLIMIT_MAX", crypto_pwhash_OPSLIMIT_MAX); | |
| 3895 | V_UINT64("crypto_pwhash_MEMLIMIT_MIN", crypto_pwhash_MEMLIMIT_MIN); | |
| 3896 | V_UINT64("crypto_pwhash_MEMLIMIT_MAX", crypto_pwhash_MEMLIMIT_MAX); | |
| 3897 | V_UINT32("crypto_pwhash_OPSLIMIT_INTERACTIVE", crypto_pwhash_OPSLIMIT_INTERACTIVE); | |
| 3898 | V_UINT64("crypto_pwhash_MEMLIMIT_INTERACTIVE", crypto_pwhash_MEMLIMIT_INTERACTIVE); | |
| 3899 | V_UINT32("crypto_pwhash_OPSLIMIT_MODERATE", crypto_pwhash_OPSLIMIT_MODERATE); | |
| 3900 | V_UINT64("crypto_pwhash_MEMLIMIT_MODERATE", crypto_pwhash_MEMLIMIT_MODERATE); | |
| 3901 | V_UINT32("crypto_pwhash_OPSLIMIT_SENSITIVE", crypto_pwhash_OPSLIMIT_SENSITIVE); | |
| 3902 | V_UINT64("crypto_pwhash_MEMLIMIT_SENSITIVE", crypto_pwhash_MEMLIMIT_SENSITIVE); | |
| 3903 | V_STRING("crypto_pwhash_PRIMITIVE", crypto_pwhash_PRIMITIVE); | |
| 3904 | ||
| 3905 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256", sn_crypto_pwhash_scryptsalsa208sha256); | |
| 3906 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str", sn_crypto_pwhash_scryptsalsa208sha256_str); | |
| 3907 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_verify", sn_crypto_pwhash_scryptsalsa208sha256_str_verify); | |
| 3908 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_needs_rehash", sn_crypto_pwhash_scryptsalsa208sha256_str_needs_rehash); | |
| 3909 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_async", sn_crypto_pwhash_scryptsalsa208sha256_async); | |
| 3910 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_async", sn_crypto_pwhash_scryptsalsa208sha256_str_async) | |
| 3911 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_verify_async", sn_crypto_pwhash_scryptsalsa208sha256_str_verify_async); | |
| 3912 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_BYTES_MIN", crypto_pwhash_scryptsalsa208sha256_BYTES_MIN); | |
| 3913 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_BYTES_MAX", crypto_pwhash_scryptsalsa208sha256_BYTES_MAX); | |
| 3914 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN", crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN); | |
| 3915 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX", crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX); | |
| 3916 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_SALTBYTES", crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 3917 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_STRBYTES", crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 3918 | V_STRING("crypto_pwhash_scryptsalsa208sha256_STRPREFIX", crypto_pwhash_scryptsalsa208sha256_STRPREFIX); | |
| 3919 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 3920 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 3921 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 3922 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 3923 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); | |
| 3924 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); | |
| 3925 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE); | |
| 3926 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE); | |
| 3927 | ||
| 3928 | // crypto_scalarmult | |
| 3929 | ||
| 3930 | V_FUNCTION("crypto_scalarmult_base", sn_crypto_scalarmult_base); | |
| 3931 | V_FUNCTION("crypto_scalarmult", sn_crypto_scalarmult); | |
| 3932 | V_STRING("crypto_scalarmult_PRIMITIVE", crypto_scalarmult_PRIMITIVE); | |
| 3933 | V_UINT32("crypto_scalarmult_BYTES", crypto_scalarmult_BYTES); | |
| 3934 | V_UINT32("crypto_scalarmult_SCALARBYTES", crypto_scalarmult_SCALARBYTES); | |
| 3935 | ||
| 3936 | V_FUNCTION("crypto_scalarmult_ed25519_base", sn_crypto_scalarmult_ed25519_base); | |
| 3937 | V_FUNCTION("crypto_scalarmult_ed25519", sn_crypto_scalarmult_ed25519); | |
| 3938 | V_FUNCTION("crypto_scalarmult_ed25519_base_noclamp", sn_crypto_scalarmult_ed25519_base_noclamp); | |
| 3939 | V_FUNCTION("crypto_scalarmult_ed25519_noclamp", sn_crypto_scalarmult_ed25519_noclamp); | |
| 3940 | V_UINT32("crypto_scalarmult_ed25519_BYTES", crypto_scalarmult_ed25519_BYTES); | |
| 3941 | V_UINT32("crypto_scalarmult_ed25519_SCALARBYTES", crypto_scalarmult_ed25519_SCALARBYTES); | |
| 3942 | ||
| 3943 | // crypto_secretbox | |
| 3944 | ||
| 3945 | V_FUNCTION("crypto_secretbox_easy", sn_crypto_secretbox_easy); | |
| 3946 | V_FUNCTION("crypto_secretbox_open_easy", sn_crypto_secretbox_open_easy); | |
| 3947 | V_FUNCTION("crypto_secretbox_detached", sn_crypto_secretbox_detached); | |
| 3948 | V_FUNCTION("crypto_secretbox_open_detached", sn_crypto_secretbox_open_detached); | |
| 3949 | V_UINT32("crypto_secretbox_KEYBYTES", crypto_secretbox_KEYBYTES); | |
| 3950 | V_UINT32("crypto_secretbox_NONCEBYTES", crypto_secretbox_NONCEBYTES); | |
| 3951 | V_UINT32("crypto_secretbox_MACBYTES", crypto_secretbox_MACBYTES); | |
| 3952 | V_STRING("crypto_secretbox_PRIMITIVE", crypto_secretbox_PRIMITIVE); | |
| 3953 | ||
| 3954 | // crypto_secretstream | |
| 3955 | ||
| 3956 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_keygen", sn_crypto_secretstream_xchacha20poly1305_keygen); | |
| 3957 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_init_push", sn_crypto_secretstream_xchacha20poly1305_init_push); | |
| 3958 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_init_pull", sn_crypto_secretstream_xchacha20poly1305_init_pull); | |
| 3959 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_push", sn_crypto_secretstream_xchacha20poly1305_push); | |
| 3960 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_pull", sn_crypto_secretstream_xchacha20poly1305_pull); | |
| 3961 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_rekey", sn_crypto_secretstream_xchacha20poly1305_rekey); | |
| 3962 | ||
| 3963 | V_UINT32("crypto_secretstream_xchacha20poly1305_STATEBYTES", sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 3964 | V_UINT32("crypto_secretstream_xchacha20poly1305_ABYTES", crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 3965 | V_UINT32("crypto_secretstream_xchacha20poly1305_HEADERBYTES", crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 3966 | V_UINT32("crypto_secretstream_xchacha20poly1305_KEYBYTES", crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 3967 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAGBYTES", 1); | |
| 3968 | V_UINT64("crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX", crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX); | |
| 3969 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_MESSAGE", crypto_secretstream_xchacha20poly1305_TAG_MESSAGE); | |
| 3970 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_PUSH", crypto_secretstream_xchacha20poly1305_TAG_PUSH); | |
| 3971 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_REKEY", crypto_secretstream_xchacha20poly1305_TAG_REKEY); | |
| 3972 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_FINAL", crypto_secretstream_xchacha20poly1305_TAG_FINAL); | |
| 3973 | ||
| 3974 | // crypto_shorthash | |
| 3975 | ||
| 3976 | V_FUNCTION("crypto_shorthash", sn_crypto_shorthash); | |
| 3977 | V_UINT32("crypto_shorthash_BYTES", crypto_shorthash_BYTES); | |
| 3978 | V_UINT32("crypto_shorthash_KEYBYTES", crypto_shorthash_KEYBYTES); | |
| 3979 | V_STRING("crypto_shorthash_PRIMITIVE", crypto_shorthash_PRIMITIVE); | |
| 3980 | ||
| 3981 | // crypto_sign | |
| 3982 | ||
| 3983 | V_FUNCTION("crypto_sign_keypair", sn_crypto_sign_keypair); | |
| 3984 | V_FUNCTION("crypto_sign_seed_keypair", sn_crypto_sign_seed_keypair); | |
| 3985 | V_FUNCTION("crypto_sign", sn_crypto_sign); | |
| 3986 | V_FUNCTION("crypto_sign_open", sn_crypto_sign_open); | |
| 3987 | V_FUNCTION("crypto_sign_detached", sn_crypto_sign_detached); | |
| 3988 | V_FUNCTION_NOSCOPE("crypto_sign_verify_detached", sn_crypto_sign_verify_detached); | |
| 3989 | V_FUNCTION("crypto_sign_ed25519_sk_to_pk", sn_crypto_sign_ed25519_sk_to_pk); | |
| 3990 | V_FUNCTION("crypto_sign_ed25519_pk_to_curve25519", sn_crypto_sign_ed25519_pk_to_curve25519); | |
| 3991 | V_FUNCTION("crypto_sign_ed25519_sk_to_curve25519", sn_crypto_sign_ed25519_sk_to_curve25519); | |
| 3992 | ||
| 3993 | V_UINT32("crypto_sign_SEEDBYTES", crypto_sign_SEEDBYTES); | |
| 3994 | V_UINT32("crypto_sign_PUBLICKEYBYTES", crypto_sign_PUBLICKEYBYTES); | |
| 3995 | V_UINT32("crypto_sign_SECRETKEYBYTES", crypto_sign_SECRETKEYBYTES); | |
| 3996 | V_UINT32("crypto_sign_BYTES", crypto_sign_BYTES); | |
| 3997 | ||
| 3998 | // crypto_stream | |
| 3999 | ||
| 4000 | V_FUNCTION("crypto_stream", sn_crypto_stream); | |
| 4001 | V_UINT32("crypto_stream_KEYBYTES", crypto_stream_KEYBYTES); | |
| 4002 | V_UINT32("crypto_stream_NONCEBYTES", crypto_stream_NONCEBYTES); | |
| 4003 | V_STRING("crypto_stream_PRIMITIVE", crypto_stream_PRIMITIVE); | |
| 4004 | ||
| 4005 | V_FUNCTION_NOSCOPE("crypto_stream_xor", sn_crypto_stream_xor); | |
| 4006 | V_FUNCTION("crypto_stream_xor_init", sn_crypto_stream_xor_wrap_init); | |
| 4007 | V_FUNCTION("crypto_stream_xor_update", sn_crypto_stream_xor_wrap_update); | |
| 4008 | V_FUNCTION("crypto_stream_xor_final", sn_crypto_stream_xor_wrap_final); | |
| 4009 | V_UINT32("crypto_stream_xor_STATEBYTES", sizeof(sn_crypto_stream_xor_state)); | |
| 4010 | ||
| 4011 | V_FUNCTION("crypto_stream_chacha20", sn_crypto_stream_chacha20); | |
| 4012 | V_UINT32("crypto_stream_chacha20_KEYBYTES", crypto_stream_chacha20_KEYBYTES); | |
| 4013 | V_UINT32("crypto_stream_chacha20_NONCEBYTES", crypto_stream_chacha20_NONCEBYTES); | |
| 4014 | V_UINT64("crypto_stream_chacha20_MESSAGEBYTES_MAX", crypto_stream_chacha20_MESSAGEBYTES_MAX); | |
| 4015 | ||
| 4016 | V_FUNCTION("crypto_stream_chacha20_xor", sn_crypto_stream_chacha20_xor); | |
| 4017 | V_FUNCTION("crypto_stream_chacha20_xor_ic", sn_crypto_stream_chacha20_xor_ic); | |
| 4018 | V_FUNCTION("crypto_stream_chacha20_xor_init", sn_crypto_stream_chacha20_xor_wrap_init); | |
| 4019 | V_FUNCTION("crypto_stream_chacha20_xor_update", sn_crypto_stream_chacha20_xor_wrap_update); | |
| 4020 | V_FUNCTION("crypto_stream_chacha20_xor_final", sn_crypto_stream_chacha20_xor_wrap_final); | |
| 4021 | V_UINT32("crypto_stream_chacha20_xor_STATEBYTES", sizeof(sn_crypto_stream_chacha20_xor_state)); | |
| 4022 | ||
| 4023 | V_FUNCTION("crypto_stream_chacha20_ietf", sn_crypto_stream_chacha20_ietf); | |
| 4024 | V_UINT32("crypto_stream_chacha20_ietf_KEYBYTES", crypto_stream_chacha20_ietf_KEYBYTES); | |
| 4025 | V_UINT32("crypto_stream_chacha20_ietf_NONCEBYTES", crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 4026 | V_UINT64("crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX", crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX); | |
| 4027 | V_UINT32("crypto_stream_chacha20_ietf_xor_STATEBYTES", sizeof(sn_crypto_stream_chacha20_ietf_xor_state)); | |
| 4028 | ||
| 4029 | V_FUNCTION("crypto_stream_chacha20_ietf_xor", sn_crypto_stream_chacha20_ietf_xor); | |
| 4030 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_ic", sn_crypto_stream_chacha20_ietf_xor_ic); | |
| 4031 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_init", sn_crypto_stream_chacha20_ietf_xor_wrap_init); | |
| 4032 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_update", sn_crypto_stream_chacha20_ietf_xor_wrap_update); | |
| 4033 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_final", sn_crypto_stream_chacha20_ietf_xor_wrap_final); | |
| 4034 | ||
| 4035 | V_FUNCTION("crypto_stream_xchacha20", sn_crypto_stream_xchacha20); | |
| 4036 | V_UINT32("crypto_stream_xchacha20_KEYBYTES", crypto_stream_xchacha20_KEYBYTES); | |
| 4037 | V_UINT32("crypto_stream_xchacha20_NONCEBYTES", crypto_stream_xchacha20_NONCEBYTES); | |
| 4038 | V_UINT64("crypto_stream_xchacha20_MESSAGEBYTES_MAX", crypto_stream_xchacha20_MESSAGEBYTES_MAX); | |
| 4039 | ||
| 4040 | V_FUNCTION("crypto_stream_xchacha20_xor", sn_crypto_stream_xchacha20_xor); | |
| 4041 | V_FUNCTION("crypto_stream_xchacha20_xor_ic", sn_crypto_stream_xchacha20_xor_ic); | |
| 4042 | V_FUNCTION("crypto_stream_xchacha20_xor_init", sn_crypto_stream_xchacha20_xor_wrap_init); | |
| 4043 | V_FUNCTION("crypto_stream_xchacha20_xor_update", sn_crypto_stream_xchacha20_xor_wrap_update); | |
| 4044 | V_FUNCTION("crypto_stream_xchacha20_xor_final", sn_crypto_stream_xchacha20_xor_wrap_final); | |
| 4045 | V_FUNCTION("crypto_stream_xchacha20", sn_crypto_stream_xchacha20); | |
| 4046 | V_UINT32("crypto_stream_xchacha20_xor_STATEBYTES", sizeof(sn_crypto_stream_xchacha20_xor_state)); | |
| 4047 | ||
| 4048 | V_FUNCTION("crypto_stream_salsa20", sn_crypto_stream_salsa20); | |
| 4049 | V_UINT32("crypto_stream_salsa20_KEYBYTES", crypto_stream_salsa20_KEYBYTES); | |
| 4050 | V_UINT32("crypto_stream_salsa20_NONCEBYTES", crypto_stream_salsa20_NONCEBYTES); | |
| 4051 | V_UINT64("crypto_stream_salsa20_MESSAGEBYTES_MAX", crypto_stream_salsa20_MESSAGEBYTES_MAX); | |
| 4052 | ||
| 4053 | V_FUNCTION("crypto_stream_salsa20_xor", sn_crypto_stream_salsa20_xor); | |
| 4054 | V_FUNCTION("crypto_stream_salsa20_xor_ic", sn_crypto_stream_salsa20_xor_ic); | |
| 4055 | V_FUNCTION("crypto_stream_salsa20_xor_init", sn_crypto_stream_salsa20_xor_wrap_init); | |
| 4056 | V_FUNCTION("crypto_stream_salsa20_xor_update", sn_crypto_stream_salsa20_xor_wrap_update); | |
| 4057 | V_FUNCTION("crypto_stream_salsa20_xor_final", sn_crypto_stream_salsa20_xor_wrap_final); | |
| 4058 | V_UINT32("crypto_stream_salsa20_xor_STATEBYTES", sizeof(sn_crypto_stream_salsa20_xor_state)); | |
| 4059 | ||
| 4060 | // extensions | |
| 4061 | ||
| 4062 | // tweak | |
| 4063 | ||
| 4064 | V_FUNCTION("extension_tweak_ed25519_base", sn_extension_tweak_ed25519_base); | |
| 4065 | V_FUNCTION("extension_tweak_ed25519_sign_detached", sn_extension_tweak_ed25519_sign_detached); | |
| 4066 | V_FUNCTION("extension_tweak_ed25519_sk_to_scalar", sn_extension_tweak_ed25519_sk_to_scalar); | |
| 4067 | V_FUNCTION("extension_tweak_ed25519_scalar", sn_extension_tweak_ed25519_scalar); | |
| 4068 | V_FUNCTION("extension_tweak_ed25519_pk", sn_extension_tweak_ed25519_pk); | |
| 4069 | V_FUNCTION("extension_tweak_ed25519_keypair", sn_extension_tweak_ed25519_keypair); | |
| 4070 | V_FUNCTION("extension_tweak_ed25519_scalar_add", sn_extension_tweak_ed25519_scalar_add); | |
| 4071 | V_FUNCTION("extension_tweak_ed25519_pk_add", sn_extension_tweak_ed25519_pk_add); | |
| 4072 | V_FUNCTION("extension_tweak_ed25519_keypair_add", sn_extension_tweak_ed25519_keypair_add); | |
| 4073 | V_UINT32("extension_tweak_ed25519_BYTES", sn__extension_tweak_ed25519_BYTES); | |
| 4074 | V_UINT32("extension_tweak_ed25519_SCALARBYTES", sn__extension_tweak_ed25519_SCALARBYTES); | |
| 4075 | ||
| 4076 | // pbkdf2 | |
| 4077 | ||
| 4078 | V_FUNCTION("extension_pbkdf2_sha512", sn_extension_pbkdf2_sha512); | |
| 4079 | V_FUNCTION("extension_pbkdf2_sha512_async", sn_extension_pbkdf2_sha512_async); | |
| 4080 | V_UINT32("extension_pbkdf2_sha512_SALTBYTES", sn__extension_pbkdf2_sha512_SALTBYTES); | |
| 4081 | V_UINT32("extension_pbkdf2_sha512_HASHBYTES", sn__extension_pbkdf2_sha512_HASHBYTES); | |
| 4082 | V_UINT32("extension_pbkdf2_sha512_ITERATIONS_MIN", sn__extension_pbkdf2_sha512_ITERATIONS_MIN); | |
| 4083 | V_UINT64("extension_pbkdf2_sha512_BYTES_MAX", sn__extension_pbkdf2_sha512_BYTES_MAX); | |
| 4084 | ||
| 4085 | #undef V_FUNCTION | |
| 4086 | #undef V_FUNCTION_NOSCOPE | |
| 4087 | #undef V_UINT32 | |
| 4088 | #undef V_UINT64 | |
| 4089 | #undef V_STRING | |
| 4090 | ||
| 4091 | return exports; | |
| 4092 | } | |
| 4093 | ||
| 4094 | BARE_MODULE(sodium_native, sodium_native_exports) |
| … | … | @@ -0,0 +1,2 @@ |
| 1 | require.addon = require('require-addon') | |
| 2 | module.exports = require.addon('.', __filename) |
| … | … | @@ -0,0 +1,90 @@ |
| 1 | /*- | |
| 2 | * Copyright 2005,2007,2009 Colin Percival | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 24 | * SUCH DAMAGE. | |
| 25 | */ | |
| 26 | ||
| 27 | /* | |
| 28 | * Adapted from libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf-sha256.c | |
| 29 | */ | |
| 30 | ||
| 31 | #include <string.h> | |
| 32 | #include <sodium.h> | |
| 33 | ||
| 34 | #include "pbkdf2.h" | |
| 35 | ||
| 36 | /** | |
| 37 | * pbkdf2_sha512(passwd, passwdlen, salt, saltlen, c, buf, dkLen): | |
| 38 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and | |
| 39 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). | |
| 40 | */ | |
| 41 | int | |
| 42 | sn__extension_pbkdf2_sha512(const unsigned char *passwd, size_t passwdlen, | |
| 43 | const unsigned char *salt, size_t saltlen, uint64_t c, | |
| 44 | unsigned char *buf, size_t dkLen) | |
| 45 | { | |
| 46 | crypto_auth_hmacsha512_state PShctx, hctx; | |
| 47 | size_t i; | |
| 48 | unsigned char ivec[4]; | |
| 49 | unsigned char U[64]; | |
| 50 | unsigned char T[64]; | |
| 51 | uint64_t j; | |
| 52 | unsigned int k; | |
| 53 | size_t clen; | |
| 54 | ||
| 55 | if (dkLen > sn__extension_pbkdf2_sha512_BYTES_MAX) { | |
| 56 | return -1; | |
| 57 | } | |
| 58 | ||
| 59 | crypto_auth_hmacsha512_init(&PShctx, passwd, passwdlen); | |
| 60 | crypto_auth_hmacsha512_update(&PShctx, salt, saltlen); | |
| 61 | ||
| 62 | for (i = 0; i * crypto_auth_hmacsha512_BYTES < dkLen; i++) { | |
| 63 | SN_PBKDF2_STORE32_BE(ivec, (uint32_t)(i + 1)); | |
| 64 | memcpy(&hctx, &PShctx, sizeof(crypto_auth_hmacsha512_state)); | |
| 65 | crypto_auth_hmacsha512_update(&hctx, ivec, 4); | |
| 66 | crypto_auth_hmacsha512_final(&hctx, U); | |
| 67 | ||
| 68 | memcpy(T, U, crypto_auth_hmacsha512_BYTES); | |
| 69 | /* LCOV_EXCL_START */ | |
| 70 | for (j = 2; j <= c; j++) { | |
| 71 | crypto_auth_hmacsha512_init(&hctx, passwd, passwdlen); | |
| 72 | crypto_auth_hmacsha512_update(&hctx, U, crypto_auth_hmacsha512_BYTES); | |
| 73 | crypto_auth_hmacsha512_final(&hctx, U); | |
| 74 | ||
| 75 | for (k = 0; k < crypto_auth_hmacsha512_BYTES; k++) { | |
| 76 | T[k] ^= U[k]; | |
| 77 | } | |
| 78 | } | |
| 79 | /* LCOV_EXCL_STOP */ | |
| 80 | ||
| 81 | clen = dkLen - i * crypto_auth_hmacsha512_BYTES; | |
| 82 | if (clen > crypto_auth_hmacsha512_BYTES) { | |
| 83 | clen = crypto_auth_hmacsha512_BYTES; | |
| 84 | } | |
| 85 | memcpy(&buf[i * crypto_auth_hmacsha512_BYTES], T, clen); | |
| 86 | } | |
| 87 | sodium_memzero((void *) &PShctx, sizeof PShctx); | |
| 88 | ||
| 89 | return 0; | |
| 90 | } |
| … | … | @@ -0,0 +1,60 @@ |
| 1 | /*- | |
| 2 | * Copyright 2005,2007,2009 Colin Percival | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 24 | * SUCH DAMAGE. | |
| 25 | * | |
| 26 | */ | |
| 27 | ||
| 28 | /* | |
| 29 | * Adapted from libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf-sha256.c | |
| 30 | */ | |
| 31 | #ifdef __cplusplus | |
| 32 | extern "C" { | |
| 33 | #endif | |
| 34 | ||
| 35 | #include <sodium.h> | |
| 36 | ||
| 37 | #define SN_PBKDF2_STORE32_BE(buf, n32) \ | |
| 38 | buf[0] = n32 >> 24 & 0xff; \ | |
| 39 | buf[1] = n32 >> 16 & 0xff; \ | |
| 40 | buf[2] = n32 >> 8 & 0xff; \ | |
| 41 | buf[3] = n32 >> 0 & 0xff; | |
| 42 | ||
| 43 | #define sn__extension_pbkdf2_sha512_SALTBYTES 16U | |
| 44 | ||
| 45 | #define sn__extension_pbkdf2_sha512_HASHBYTES crypto_hash_sha512_BYTES | |
| 46 | ||
| 47 | #define sn__extension_pbkdf2_sha512_ITERATIONS_MIN 1U | |
| 48 | ||
| 49 | #define sn__extension_pbkdf2_sha512_BYTES_MAX 0x3fffffffc0ULL | |
| 50 | ||
| 51 | /** | |
| 52 | * extension_pbkdf2_sha512(passwd, passwdlen, salt, saltlen, c, buf, dkLen): | |
| 53 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and | |
| 54 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). | |
| 55 | */ | |
| 56 | int sn__extension_pbkdf2_sha512(const unsigned char *, size_t, const unsigned char *, size_t, | |
| 57 | uint64_t, unsigned char *, size_t); | |
| 58 | #ifdef __cplusplus | |
| 59 | }; | |
| 60 | #endif |
| … | … | @@ -0,0 +1,206 @@ |
| 1 | #include "tweak.h" | |
| 2 | ||
| 3 | /* | |
| 4 | *EXPERIMENTAL API* | |
| 5 | ||
| 6 | This module is an experimental implementation of a key tweaking protocol | |
| 7 | over ed25519 keys. The signature algorithm has been reimplemented from | |
| 8 | libsodium, but the nonce generation algorithm is *non-standard*. | |
| 9 | ||
| 10 | Use at your own risk | |
| 11 | */ | |
| 12 | ||
| 13 | static void _extension_tweak_nonce (unsigned char *nonce, const unsigned char *n, | |
| 14 | const unsigned char *m, unsigned long long mlen) | |
| 15 | { | |
| 16 | // dom2(x, y) with x = 0 (not prehashed) and y = "crypto_tweak_ed25519" | |
| 17 | static const unsigned char TWEAK_PREFIX[32 + 2 + 20] = { | |
| 18 | 'S', 'i', 'g', 'E', 'd', '2', '5', '5', '1', '9', ' ', | |
| 19 | 'n', 'o', ' ', 'E', 'd', '2', '5', '5', '1', '9', ' ', | |
| 20 | 'c', 'o', 'l', 'l', 'i', 's', 'i', 'o', 'n', 's', 0, | |
| 21 | 20, 'c', 'r', 'y', 'p', 't', 'o', '_', 't', 'w', 'e', | |
| 22 | 'a', 'k', '_', 'e', 'd', '2', '5', '5', '1', '9' | |
| 23 | }; | |
| 24 | ||
| 25 | crypto_hash_sha512_state hs; | |
| 26 | ||
| 27 | crypto_hash_sha512_init(&hs); | |
| 28 | crypto_hash_sha512_update(&hs, TWEAK_PREFIX, sizeof TWEAK_PREFIX); | |
| 29 | crypto_hash_sha512_update(&hs, n, 32); | |
| 30 | crypto_hash_sha512_update(&hs, m, mlen); | |
| 31 | crypto_hash_sha512_final(&hs, nonce); | |
| 32 | } | |
| 33 | ||
| 34 | static inline void | |
| 35 | _crypto_sign_ed25519_clamp(unsigned char k[32]) | |
| 36 | { | |
| 37 | k[0] &= 248; | |
| 38 | k[31] &= 127; | |
| 39 | k[31] |= 64; | |
| 40 | } | |
| 41 | ||
| 42 | static void _extension_tweak_ed25519(unsigned char *q, unsigned char *n, | |
| 43 | const unsigned char *ns, unsigned long long nslen) | |
| 44 | { | |
| 45 | sodium_memzero(q, sizeof q); | |
| 46 | ||
| 47 | crypto_hash(n, ns, nslen); | |
| 48 | n[31] &= 127; // clear highest bit | |
| 49 | ||
| 50 | crypto_scalarmult_ed25519_base_noclamp(q, n); | |
| 51 | ||
| 52 | // hash tweak until we get a valid tweaked q | |
| 53 | while (crypto_core_ed25519_is_valid_point(q) != 1) { | |
| 54 | crypto_hash(n, n, 32); | |
| 55 | n[31] &= 127; // clear highest bit | |
| 56 | ||
| 57 | crypto_scalarmult_ed25519_base_noclamp(q, n); | |
| 58 | } | |
| 59 | } | |
| 60 | ||
| 61 | void sn__extension_tweak_ed25519_base(unsigned char *pk, unsigned char *scalar, | |
| 62 | const unsigned char *ns, unsigned long long nslen) | |
| 63 | { | |
| 64 | unsigned char n64[64]; | |
| 65 | ||
| 66 | _extension_tweak_ed25519(pk, n64, ns, nslen); | |
| 67 | ||
| 68 | SN_TWEAK_COPY_32(scalar, n64) | |
| 69 | } | |
| 70 | ||
| 71 | int sn__extension_tweak_ed25519_sign_detached(unsigned char *sig, unsigned long long *siglen_p, | |
| 72 | const unsigned char *m, unsigned long long mlen, | |
| 73 | const unsigned char *n, unsigned char *pk) | |
| 74 | { | |
| 75 | crypto_hash_sha512_state hs; | |
| 76 | ||
| 77 | unsigned char nonce[64]; | |
| 78 | unsigned char R[32]; | |
| 79 | unsigned char hram[64]; | |
| 80 | unsigned char _pk[32]; | |
| 81 | ||
| 82 | // check if pk was passed | |
| 83 | if (pk == NULL) { | |
| 84 | pk = _pk; | |
| 85 | ||
| 86 | // derive pk from scalar | |
| 87 | if (crypto_scalarmult_ed25519_base_noclamp(pk, n) != 0) { | |
| 88 | return -1; | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | _extension_tweak_nonce(nonce, n, m, mlen); | |
| 93 | crypto_core_ed25519_scalar_reduce(nonce, nonce); | |
| 94 | ||
| 95 | // R = G ^ nonce : curve point from nonce | |
| 96 | if (crypto_scalarmult_ed25519_base_noclamp(R, nonce) != 0) { | |
| 97 | return -1; | |
| 98 | } | |
| 99 | ||
| 100 | // generate challenge as h(ram) = hash(R, pk, message) | |
| 101 | crypto_hash_sha512_init(&hs); | |
| 102 | crypto_hash_sha512_update(&hs, R, 32); | |
| 103 | crypto_hash_sha512_update(&hs, pk, 32); | |
| 104 | crypto_hash_sha512_update(&hs, m, mlen); | |
| 105 | ||
| 106 | crypto_hash_sha512_final(&hs, hram); | |
| 107 | ||
| 108 | crypto_core_ed25519_scalar_reduce(hram, hram); | |
| 109 | ||
| 110 | // sig = nonce + n * h(ram) | |
| 111 | crypto_core_ed25519_scalar_mul(sig, hram, n); | |
| 112 | crypto_core_ed25519_scalar_add(sig + 32, nonce, sig); | |
| 113 | ||
| 114 | SN_TWEAK_COPY_32(sig, R) | |
| 115 | ||
| 116 | if (siglen_p != NULL) { | |
| 117 | *siglen_p = 64U; | |
| 118 | } | |
| 119 | ||
| 120 | return 0; | |
| 121 | } | |
| 122 | ||
| 123 | // tweak a secret key | |
| 124 | void sn__extension_tweak_ed25519_sk_to_scalar(unsigned char *n, const unsigned char *sk) | |
| 125 | { | |
| 126 | unsigned char n64[64]; | |
| 127 | ||
| 128 | // get sk scalar from seed, cf. crypto_sign_keypair_seed | |
| 129 | crypto_hash(n64, sk, 32); | |
| 130 | _crypto_sign_ed25519_clamp(n64); | |
| 131 | ||
| 132 | SN_TWEAK_COPY_32(n, n64) | |
| 133 | } | |
| 134 | ||
| 135 | // tweak a secret key | |
| 136 | void sn__extension_tweak_ed25519_scalar(unsigned char *scalar_out, | |
| 137 | const unsigned char *scalar, | |
| 138 | const unsigned char *ns, | |
| 139 | unsigned long long nslen) | |
| 140 | { | |
| 141 | unsigned char n[64]; | |
| 142 | unsigned char q[32]; | |
| 143 | ||
| 144 | _extension_tweak_ed25519(q, n, ns, nslen); | |
| 145 | crypto_core_ed25519_scalar_add(scalar_out, scalar, n); | |
| 146 | } | |
| 147 | ||
| 148 | // tweak a public key | |
| 149 | int sn__extension_tweak_ed25519_pk(unsigned char *tpk, | |
| 150 | const unsigned char *pk, | |
| 151 | const unsigned char *ns, | |
| 152 | unsigned long long nslen) | |
| 153 | { | |
| 154 | unsigned char n[64]; | |
| 155 | unsigned char q[32]; | |
| 156 | ||
| 157 | _extension_tweak_ed25519(q, n, ns, nslen); | |
| 158 | return crypto_core_ed25519_add(tpk, q, pk); | |
| 159 | } | |
| 160 | ||
| 161 | ||
| 162 | void sn__extension_tweak_ed25519_keypair(unsigned char *pk, unsigned char *scalar_out, | |
| 163 | unsigned char *scalar, const unsigned char *ns, | |
| 164 | unsigned long long nslen) | |
| 165 | { | |
| 166 | unsigned char n64[64]; | |
| 167 | ||
| 168 | crypto_hash(n64, ns, nslen); | |
| 169 | n64[31] &= 127; // clear highest bit | |
| 170 | ||
| 171 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, n64); | |
| 172 | crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 173 | ||
| 174 | // hash tweak until we get a valid tweaked point | |
| 175 | while (crypto_core_ed25519_is_valid_point(pk) != 1) { | |
| 176 | crypto_hash(n64, n64, 32); | |
| 177 | n64[31] &= 127; // clear highest bit | |
| 178 | ||
| 179 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, n64); | |
| 180 | crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 181 | } | |
| 182 | } | |
| 183 | ||
| 184 | // add tweak to scalar | |
| 185 | void sn__extension_tweak_ed25519_scalar_add(unsigned char *scalar_out, | |
| 186 | const unsigned char *scalar, | |
| 187 | const unsigned char *n) | |
| 188 | { | |
| 189 | crypto_core_ed25519_scalar_add(scalar_out, scalar, n); | |
| 190 | } | |
| 191 | ||
| 192 | // add tweak point to public key | |
| 193 | int sn__extension_tweak_ed25519_pk_add(unsigned char *tpk, | |
| 194 | const unsigned char *pk, | |
| 195 | const unsigned char *q) | |
| 196 | { | |
| 197 | return crypto_core_ed25519_add(tpk, pk, q); | |
| 198 | } | |
| 199 | ||
| 200 | ||
| 201 | int sn__extension_tweak_ed25519_keypair_add(unsigned char *pk, unsigned char *scalar_out, | |
| 202 | unsigned char *scalar, const unsigned char *tweak) | |
| 203 | { | |
| 204 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, tweak); | |
| 205 | return crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 206 | } |
| … | … | @@ -0,0 +1,62 @@ |
| 1 | #ifdef __cplusplus | |
| 2 | extern "C" { | |
| 3 | #endif | |
| 4 | ||
| 5 | #include <sodium.h> | |
| 6 | ||
| 7 | // copy 32 bytes using int64_t pointers | |
| 8 | #define SN_TWEAK_COPY_32(a, b) \ | |
| 9 | { \ | |
| 10 | long long *dst = (long long *) a; \ | |
| 11 | long long *src = (long long *) b; \ | |
| 12 | dst[0] = src[0]; \ | |
| 13 | dst[1] = src[1]; \ | |
| 14 | dst[2] = src[2]; \ | |
| 15 | dst[3] = src[3]; \ | |
| 16 | } | |
| 17 | ||
| 18 | #define sn__extension_tweak_ed25519_BYTES crypto_sign_ed25519_PUBLICKEYBYTES | |
| 19 | ||
| 20 | #define sn__extension_tweak_ed25519_SCALARBYTES crypto_scalarmult_ed25519_SCALARBYTES | |
| 21 | ||
| 22 | int sn__extension_tweak_ed25519_sign_detached(unsigned char *sig, unsigned long long *siglen_p, | |
| 23 | const unsigned char *m, unsigned long long mlen, | |
| 24 | const unsigned char *n, unsigned char *pk); | |
| 25 | ||
| 26 | void sn__extension_tweak_ed25519_base(unsigned char *pk, unsigned char *scalar, | |
| 27 | const unsigned char *ns, unsigned long long nslen); | |
| 28 | ||
| 29 | void sn__extension_tweak_ed25519_sk_to_scalar(unsigned char *scalar, const unsigned char *sk); | |
| 30 | ||
| 31 | // tweak a secret key | |
| 32 | void sn__extension_tweak_ed25519_scalar(unsigned char *scalar_out, | |
| 33 | const unsigned char *scalar, | |
| 34 | const unsigned char *ns, | |
| 35 | unsigned long long nslen); | |
| 36 | ||
| 37 | // tweak a public key | |
| 38 | int sn__extension_tweak_ed25519_pk(unsigned char *tpk, | |
| 39 | const unsigned char *pk, | |
| 40 | const unsigned char *ns, | |
| 41 | unsigned long long nslen); | |
| 42 | ||
| 43 | void sn__extension_tweak_ed25519_keypair(unsigned char *pk, unsigned char *scalar_out, | |
| 44 | unsigned char *scalar, const unsigned char *ns, | |
| 45 | unsigned long long nslen); | |
| 46 | ||
| 47 | // add tweak scalar to private key | |
| 48 | void sn__extension_tweak_ed25519_scalar_add(unsigned char *scalar_out, | |
| 49 | const unsigned char *scalar, | |
| 50 | const unsigned char *n); | |
| 51 | ||
| 52 | // add tweak point to public key | |
| 53 | int sn__extension_tweak_ed25519_pk_add(unsigned char *tpk, | |
| 54 | const unsigned char *pk, | |
| 55 | const unsigned char *q); | |
| 56 | ||
| 57 | int sn__extension_tweak_ed25519_keypair_add(unsigned char *pk, unsigned char *scalar_out, | |
| 58 | unsigned char *scalar, const unsigned char *tweak); | |
| 59 | ||
| 60 | #ifdef __cplusplus | |
| 61 | }; | |
| 62 | #endif |
| … | … | @@ -0,0 +1,3231 @@ |
| 1 | const assert = require('assert') | |
| 2 | const binding = require('./binding') | |
| 3 | const { isNode } = require('which-runtime') | |
| 4 | ||
| 5 | const OPTIONAL = Buffer.from(new ArrayBuffer(0)) | |
| 6 | ||
| 7 | module.exports = exports = { ...binding } | |
| 8 | ||
| 9 | // memory | |
| 10 | ||
| 11 | exports.sodium_memzero = function (buf) { | |
| 12 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 13 | ||
| 14 | binding.sodium_memzero(buf) | |
| 15 | } | |
| 16 | ||
| 17 | exports.sodium_mlock = function (buf) { | |
| 18 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 19 | ||
| 20 | const res = binding.sodium_mlock(buf) | |
| 21 | ||
| 22 | if (res !== 0) throw new Error('memory lock failed') | |
| 23 | } | |
| 24 | ||
| 25 | exports.sodium_munlock = function (buf) { | |
| 26 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 27 | ||
| 28 | const res = binding.sodium_munlock(buf) | |
| 29 | ||
| 30 | if (res !== 0) throw new Error('memory unlock failed') | |
| 31 | } | |
| 32 | ||
| 33 | exports.sodium_malloc = function (size) { | |
| 34 | assert(size >= 0, 'invalid size') | |
| 35 | ||
| 36 | const buf = Buffer.from(binding.sodium_malloc(size)) | |
| 37 | buf.secure = true | |
| 38 | return buf | |
| 39 | } | |
| 40 | ||
| 41 | exports.sodium_free = function (buf) { | |
| 42 | if (!buf || !buf.secure) return | |
| 43 | ||
| 44 | binding.sodium_free(buf.buffer) | |
| 45 | } | |
| 46 | ||
| 47 | exports.sodium_mprotect_noaccess = function (buf) { | |
| 48 | const res = binding.sodium_mprotect_noaccess(buf.buffer) | |
| 49 | ||
| 50 | if (res !== 0) throw new Error('failed to lock buffer') | |
| 51 | } | |
| 52 | ||
| 53 | exports.sodium_mprotect_readonly = function (buf) { | |
| 54 | const res = binding.sodium_mprotect_readonly(buf.buffer) | |
| 55 | ||
| 56 | if (res !== 0) throw new Error('failed to unlock buffer') | |
| 57 | } | |
| 58 | ||
| 59 | exports.sodium_mprotect_readwrite = function (buf) { | |
| 60 | const res = binding.sodium_mprotect_readwrite(buf.buffer) | |
| 61 | ||
| 62 | if (res !== 0) throw new Error('failed to unlock buffer') | |
| 63 | } | |
| 64 | ||
| 65 | // crypto_randombytes | |
| 66 | ||
| 67 | exports.randombytes_buf = function (buffer) { | |
| 68 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 69 | ||
| 70 | binding.randombytes_buf(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 71 | } | |
| 72 | ||
| 73 | exports.randombytes_buf_deterministic = function (buffer, seed) { | |
| 74 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 75 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 76 | assert( | |
| 77 | seed.byteLength === binding.randombytes_SEEDBYTES, | |
| 78 | "seed must be 'randombytes_SEEDBYTES' bytes" | |
| 79 | ) | |
| 80 | ||
| 81 | binding.randombytes_buf_deterministic( | |
| 82 | buffer.buffer, | |
| 83 | buffer.byteOffset, | |
| 84 | buffer.byteLength, | |
| 85 | ||
| 86 | seed.buffer, | |
| 87 | seed.byteOffset, | |
| 88 | seed.byteLength | |
| 89 | ) | |
| 90 | } | |
| 91 | ||
| 92 | // sodium_helpers | |
| 93 | ||
| 94 | exports.sodium_memcmp = function (a, b) { | |
| 95 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 96 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 97 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 98 | ||
| 99 | return binding.sodium_memcmp(a, b) | |
| 100 | } | |
| 101 | ||
| 102 | exports.sodium_add = function (a, b) { | |
| 103 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 104 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 105 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 106 | ||
| 107 | binding.sodium_add(a, b) | |
| 108 | } | |
| 109 | ||
| 110 | exports.sodium_sub = function (a, b) { | |
| 111 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 112 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 113 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 114 | ||
| 115 | binding.sodium_sub(a, b) | |
| 116 | } | |
| 117 | ||
| 118 | exports.sodium_compare = function (a, b) { | |
| 119 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 120 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 121 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 122 | ||
| 123 | return binding.sodium_compare(a, b) | |
| 124 | } | |
| 125 | ||
| 126 | exports.sodium_is_zero = function (buffer, length) { | |
| 127 | if (length === undefined) length = buffer.byteLength | |
| 128 | ||
| 129 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 130 | assert(length >= 0 && length <= buffer.byteLength, 'invalid length') | |
| 131 | ||
| 132 | return binding.sodium_is_zero(buffer, length) | |
| 133 | } | |
| 134 | ||
| 135 | exports.sodium_pad = function (buffer, unpaddedBuflen, blockSize) { | |
| 136 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 137 | assert(unpaddedBuflen <= buffer.byteLength, 'unpadded length cannot exceed buffer length') | |
| 138 | assert(blockSize <= buffer.byteLength, 'block size cannot exceed buffer length') | |
| 139 | assert(blockSize >= 1, 'block size must be at least 1 byte') | |
| 140 | assert( | |
| 141 | buffer.byteLength >= unpaddedBuflen + (blockSize - (unpaddedBuflen % blockSize)), | |
| 142 | 'buf not long enough' | |
| 143 | ) | |
| 144 | ||
| 145 | return binding.sodium_pad(buffer, unpaddedBuflen, blockSize) | |
| 146 | } | |
| 147 | ||
| 148 | exports.sodium_unpad = function (buffer, paddedBuflen, blockSize) { | |
| 149 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 150 | assert(paddedBuflen <= buffer.byteLength, 'unpadded length cannot exceed buffer length') | |
| 151 | assert(blockSize <= buffer.byteLength, 'block size cannot exceed buffer length') | |
| 152 | assert(blockSize >= 1, 'block size must be at least 1 byte') | |
| 153 | ||
| 154 | return binding.sodium_unpad(buffer, paddedBuflen, blockSize) | |
| 155 | } | |
| 156 | ||
| 157 | // crypto_sign | |
| 158 | ||
| 159 | exports.crypto_sign_keypair = function (pk, sk) { | |
| 160 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 161 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 162 | assert( | |
| 163 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 164 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 165 | ) | |
| 166 | assert( | |
| 167 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 168 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 169 | ) | |
| 170 | ||
| 171 | const res = binding.crypto_sign_keypair(pk, sk) | |
| 172 | ||
| 173 | if (res !== 0) throw new Error('status: ' + res) | |
| 174 | } | |
| 175 | ||
| 176 | exports.crypto_sign_seed_keypair = function (pk, sk, seed) { | |
| 177 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 178 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 179 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 180 | assert( | |
| 181 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 182 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 183 | ) | |
| 184 | assert( | |
| 185 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 186 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 187 | ) | |
| 188 | assert( | |
| 189 | seed.byteLength === binding.crypto_sign_SEEDBYTES, | |
| 190 | "seed must be 'crypto_sign_SEEDBYTES' bytes" | |
| 191 | ) | |
| 192 | ||
| 193 | const res = binding.crypto_sign_seed_keypair(pk, sk, seed) | |
| 194 | ||
| 195 | if (res !== 0) throw new Error('status: ' + res) | |
| 196 | } | |
| 197 | ||
| 198 | exports.crypto_sign = function (sm, m, sk) { | |
| 199 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 200 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 201 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 202 | assert( | |
| 203 | sm.byteLength === binding.crypto_sign_BYTES + m.byteLength, | |
| 204 | "sm must be 'm.byteLength + crypto_sign_BYTES' bytes" | |
| 205 | ) | |
| 206 | assert( | |
| 207 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 208 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 209 | ) | |
| 210 | ||
| 211 | const res = binding.crypto_sign(sm, m, sk) | |
| 212 | ||
| 213 | if (res !== 0) throw new Error('status: ' + res) | |
| 214 | } | |
| 215 | ||
| 216 | exports.crypto_sign_open = function (m, sm, pk) { | |
| 217 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 218 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 219 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 220 | assert( | |
| 221 | sm.byteLength >= binding.crypto_sign_BYTES, | |
| 222 | "sm must be at least 'crypto_sign_BYTES' bytes" | |
| 223 | ) | |
| 224 | assert( | |
| 225 | m.byteLength === sm.byteLength - binding.crypto_sign_BYTES, | |
| 226 | "m must be 'sm.byteLength - crypto_sign_BYTES' bytes" | |
| 227 | ) | |
| 228 | assert( | |
| 229 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 230 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 231 | ) | |
| 232 | ||
| 233 | const res = binding.crypto_sign_open(m, sm, pk) | |
| 234 | ||
| 235 | if (res !== 0) throw new Error('status: ' + res) | |
| 236 | } | |
| 237 | ||
| 238 | exports.crypto_sign_open = function (m, sm, pk) { | |
| 239 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 240 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 241 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 242 | assert( | |
| 243 | sm.byteLength >= binding.crypto_sign_BYTES, | |
| 244 | "sm must be at least 'crypto_sign_BYTES' bytes" | |
| 245 | ) | |
| 246 | assert( | |
| 247 | m.byteLength === sm.byteLength - binding.crypto_sign_BYTES, | |
| 248 | "m must be 'sm.byteLength - crypto_sign_BYTES' bytes" | |
| 249 | ) | |
| 250 | assert( | |
| 251 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 252 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 253 | ) | |
| 254 | ||
| 255 | return binding.crypto_sign_open(m, sm, pk) | |
| 256 | } | |
| 257 | ||
| 258 | exports.crypto_sign_detached = function (sig, m, sk) { | |
| 259 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 260 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 261 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 262 | assert(sig.byteLength === binding.crypto_sign_BYTES, "sig must be 'crypto_sign_BYTES' bytes") | |
| 263 | assert( | |
| 264 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 265 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 266 | ) | |
| 267 | ||
| 268 | const res = binding.crypto_sign_detached(sig, m, sk) | |
| 269 | ||
| 270 | if (res !== 0) throw new Error('status: ' + res) | |
| 271 | } | |
| 272 | ||
| 273 | exports.crypto_sign_verify_detached = function (sig, m, pk) { | |
| 274 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 275 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 276 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 277 | assert( | |
| 278 | sig.byteLength >= binding.crypto_sign_BYTES, | |
| 279 | "sig must be at least 'crypto_sign_BYTES' bytes" | |
| 280 | ) | |
| 281 | assert( | |
| 282 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 283 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 284 | ) | |
| 285 | ||
| 286 | return binding.crypto_sign_verify_detached( | |
| 287 | sig.buffer, | |
| 288 | sig.byteOffset, | |
| 289 | sig.byteLength, | |
| 290 | ||
| 291 | m.buffer, | |
| 292 | m.byteOffset, | |
| 293 | m.byteLength, | |
| 294 | ||
| 295 | pk.buffer, | |
| 296 | pk.byteOffset, | |
| 297 | pk.byteLength | |
| 298 | ) | |
| 299 | } | |
| 300 | ||
| 301 | exports.crypto_sign_ed25519_sk_to_pk = function (pk, sk) { | |
| 302 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 303 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 304 | assert( | |
| 305 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 306 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 307 | ) | |
| 308 | assert( | |
| 309 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 310 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 311 | ) | |
| 312 | ||
| 313 | const res = binding.crypto_sign_ed25519_sk_to_pk(pk, sk) | |
| 314 | ||
| 315 | if (res !== 0) throw new Error('status: ' + res) | |
| 316 | } | |
| 317 | ||
| 318 | exports.crypto_sign_ed25519_pk_to_curve25519 = function (x25519pk, ed25519pk) { | |
| 319 | assert(ArrayBuffer.isView(x25519pk), 'x25519pk must be a typed array') | |
| 320 | assert(ArrayBuffer.isView(ed25519pk), 'ed25519pk must be a typed array') | |
| 321 | assert( | |
| 322 | x25519pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 323 | "x25519pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 324 | ) | |
| 325 | assert( | |
| 326 | ed25519pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 327 | "ed25519pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 328 | ) | |
| 329 | ||
| 330 | const res = binding.crypto_sign_ed25519_pk_to_curve25519(x25519pk, ed25519pk) | |
| 331 | ||
| 332 | if (res !== 0) throw new Error('status: ' + res) | |
| 333 | } | |
| 334 | ||
| 335 | exports.crypto_sign_ed25519_sk_to_curve25519 = function (x25519sk, ed25519sk) { | |
| 336 | assert(ArrayBuffer.isView(x25519sk), 'x25519sk must be a typed array') | |
| 337 | assert(ArrayBuffer.isView(ed25519sk), 'ed25519sk must be a typed array') | |
| 338 | assert( | |
| 339 | x25519sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 340 | "x25519sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 341 | ) | |
| 342 | ||
| 343 | const edLen = ed25519sk.byteLength | |
| 344 | ||
| 345 | assert( | |
| 346 | edLen === binding.crypto_sign_SECRETKEYBYTES || edLen === binding.crypto_box_SECRETKEYBYTES, | |
| 347 | "ed25519sk must be 'crypto_sign_SECRETKEYBYTES' or 'crypto_sign_SECRETKEYBYTES - crypto_sign_PUBLICKEYBYTES' bytes" | |
| 348 | ) | |
| 349 | ||
| 350 | const res = binding.crypto_sign_ed25519_sk_to_curve25519(x25519sk, ed25519sk) | |
| 351 | ||
| 352 | if (res !== 0) throw new Error('status: ' + res) | |
| 353 | } | |
| 354 | ||
| 355 | // crypto_box | |
| 356 | ||
| 357 | exports.crypto_box_keypair = function (pk, sk) { | |
| 358 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 359 | assert( | |
| 360 | pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 361 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 362 | ) | |
| 363 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 364 | assert( | |
| 365 | sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 366 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 367 | ) | |
| 368 | ||
| 369 | const res = binding.crypto_box_keypair(pk, sk) | |
| 370 | ||
| 371 | if (res !== 0) throw new Error('status: ' + res) | |
| 372 | } | |
| 373 | ||
| 374 | exports.crypto_box_seed_keypair = function (pk, sk, seed) { | |
| 375 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 376 | assert( | |
| 377 | pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 378 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 379 | ) | |
| 380 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 381 | assert( | |
| 382 | sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 383 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 384 | ) | |
| 385 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 386 | assert( | |
| 387 | seed.byteLength === binding.crypto_box_SEEDBYTES, | |
| 388 | "seed must be 'crypto_box_SEEDBYTES' bytes" | |
| 389 | ) | |
| 390 | ||
| 391 | const res = binding.crypto_box_seed_keypair(pk, sk, seed) | |
| 392 | ||
| 393 | if (res !== 0) throw new Error('status: ' + res) | |
| 394 | } | |
| 395 | ||
| 396 | exports.crypto_box_easy = function (c, m, n, pk, sk) { | |
| 397 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 398 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 399 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 400 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 401 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 402 | assert( | |
| 403 | c.byteLength === m.byteLength + exports.crypto_box_MACBYTES, | |
| 404 | "c must be 'm.byteLength + crypto_box_MACBYTES' bytes" | |
| 405 | ) | |
| 406 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 407 | assert( | |
| 408 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 409 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 410 | ) | |
| 411 | assert( | |
| 412 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 413 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 414 | ) | |
| 415 | ||
| 416 | const res = binding.crypto_box_easy(c, m, n, pk, sk) | |
| 417 | ||
| 418 | if (res !== 0) throw new Error('status: ' + res) | |
| 419 | } | |
| 420 | ||
| 421 | exports.crypto_box_detached = function (c, mac, m, n, pk, sk) { | |
| 422 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 423 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 424 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 425 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 426 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 427 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 428 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 429 | assert(mac.byteLength === exports.crypto_box_MACBYTES, "mac must be 'crypto_box_MACBYTES' bytes") | |
| 430 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 431 | assert( | |
| 432 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 433 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 434 | ) | |
| 435 | assert( | |
| 436 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 437 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 438 | ) | |
| 439 | ||
| 440 | const res = binding.crypto_box_detached(c, mac, m, n, pk, sk) | |
| 441 | ||
| 442 | if (res !== 0) throw new Error('status: ' + res) | |
| 443 | } | |
| 444 | ||
| 445 | exports.crypto_box_open_easy = function (m, c, n, pk, sk) { | |
| 446 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 447 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 448 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 449 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 450 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 451 | assert( | |
| 452 | c.byteLength >= exports.crypto_box_MACBYTES, | |
| 453 | "c must be at least 'crypto_box_MACBYTES' bytes" | |
| 454 | ) | |
| 455 | assert( | |
| 456 | m.byteLength === c.byteLength - exports.crypto_box_MACBYTES, | |
| 457 | "m must be 'c.byteLength - crypto_box_MACBYTES' bytes" | |
| 458 | ) | |
| 459 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 460 | assert( | |
| 461 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 462 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 463 | ) | |
| 464 | assert( | |
| 465 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 466 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 467 | ) | |
| 468 | ||
| 469 | return binding.crypto_box_open_easy(m, c, n, pk, sk) | |
| 470 | } | |
| 471 | ||
| 472 | exports.crypto_box_open_detached = function (m, c, mac, n, pk, sk) { | |
| 473 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 474 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 475 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 476 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 477 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 478 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 479 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 480 | assert(mac.byteLength === exports.crypto_box_MACBYTES, "mac must be 'crypto_box_MACBYTES' bytes") | |
| 481 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 482 | assert( | |
| 483 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 484 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 485 | ) | |
| 486 | assert( | |
| 487 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 488 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 489 | ) | |
| 490 | ||
| 491 | return binding.crypto_box_open_detached(m, c, mac, n, pk, sk) | |
| 492 | } | |
| 493 | ||
| 494 | exports.crypto_box_seal = function (c, m, pk) { | |
| 495 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 496 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 497 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 498 | assert( | |
| 499 | c.byteLength === m.byteLength + exports.crypto_box_SEALBYTES, | |
| 500 | "c must be 'm.byteLength + crypto_box_SEALBYTES' bytes" | |
| 501 | ) | |
| 502 | assert( | |
| 503 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 504 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 505 | ) | |
| 506 | ||
| 507 | const res = binding.crypto_box_seal(c, m, pk) | |
| 508 | ||
| 509 | if (res !== 0) throw new Error('status: ' + res) | |
| 510 | } | |
| 511 | ||
| 512 | exports.crypto_box_seal_open = function (m, c, pk, sk) { | |
| 513 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 514 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 515 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 516 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 517 | assert( | |
| 518 | c.byteLength >= exports.crypto_box_SEALBYTES, | |
| 519 | "c must be at least 'crypto_box_SEALBYTES' bytes" | |
| 520 | ) | |
| 521 | assert( | |
| 522 | m.byteLength === c.byteLength - exports.crypto_box_SEALBYTES, | |
| 523 | "m must be 'c.byteLength - crypto_box_SEALBYTES' bytes" | |
| 524 | ) | |
| 525 | assert( | |
| 526 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 527 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 528 | ) | |
| 529 | assert( | |
| 530 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 531 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 532 | ) | |
| 533 | ||
| 534 | return binding.crypto_box_seal_open( | |
| 535 | m.buffer, | |
| 536 | m.byteOffset, | |
| 537 | m.byteLength, | |
| 538 | ||
| 539 | c.buffer, | |
| 540 | c.byteOffset, | |
| 541 | c.byteLength, | |
| 542 | ||
| 543 | pk.buffer, | |
| 544 | pk.byteOffset, | |
| 545 | pk.byteLength, | |
| 546 | ||
| 547 | sk.buffer, | |
| 548 | sk.byteOffset, | |
| 549 | sk.byteLength | |
| 550 | ) | |
| 551 | } | |
| 552 | ||
| 553 | // crypto_secretbox | |
| 554 | ||
| 555 | exports.crypto_secretbox_easy = function (c, m, n, k) { | |
| 556 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 557 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 558 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 559 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 560 | assert( | |
| 561 | c.byteLength === m.byteLength + binding.crypto_secretbox_MACBYTES, | |
| 562 | "c must be 'm.byteLength + crypto_secretbox_MACBYTES' bytes" | |
| 563 | ) | |
| 564 | assert( | |
| 565 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 566 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 567 | ) | |
| 568 | assert( | |
| 569 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 570 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 571 | ) | |
| 572 | ||
| 573 | const res = binding.crypto_secretbox_easy(c, m, n, k) | |
| 574 | ||
| 575 | if (res !== 0) throw new Error('status: ' + res) | |
| 576 | } | |
| 577 | ||
| 578 | exports.crypto_secretbox_open_easy = function (m, c, n, k) { | |
| 579 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 580 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 581 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 582 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 583 | assert( | |
| 584 | c.byteLength >= binding.crypto_secretbox_MACBYTES, | |
| 585 | "c must be at least 'crypto_secretbox_MACBYTES' bytes" | |
| 586 | ) | |
| 587 | assert( | |
| 588 | m.byteLength === c.byteLength - binding.crypto_secretbox_MACBYTES, | |
| 589 | "m must be 'c.byteLength - crypto_secretbox_MACBYTES' bytes" | |
| 590 | ) | |
| 591 | assert( | |
| 592 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 593 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 594 | ) | |
| 595 | assert( | |
| 596 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 597 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 598 | ) | |
| 599 | ||
| 600 | return binding.crypto_secretbox_open_easy(m, c, n, k) | |
| 601 | } | |
| 602 | ||
| 603 | exports.crypto_secretbox_detached = function (c, mac, m, n, k) { | |
| 604 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 605 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 606 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 607 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 608 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 609 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 610 | assert( | |
| 611 | mac.byteLength === binding.crypto_secretbox_MACBYTES, | |
| 612 | "mac must be 'crypto_secretbox_MACBYTES' bytes" | |
| 613 | ) | |
| 614 | assert( | |
| 615 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 616 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 617 | ) | |
| 618 | assert( | |
| 619 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 620 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 621 | ) | |
| 622 | ||
| 623 | const res = binding.crypto_secretbox_detached(c, mac, m, n, k) | |
| 624 | ||
| 625 | if (res !== 0) throw new Error('status: ' + res) | |
| 626 | } | |
| 627 | ||
| 628 | exports.crypto_secretbox_open_detached = function (m, c, mac, n, k) { | |
| 629 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 630 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 631 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 632 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 633 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 634 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 635 | assert( | |
| 636 | mac.byteLength === binding.crypto_secretbox_MACBYTES, | |
| 637 | "mac must be 'crypto_secretbox_MACBYTES' bytes" | |
| 638 | ) | |
| 639 | assert( | |
| 640 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 641 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 642 | ) | |
| 643 | assert( | |
| 644 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 645 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 646 | ) | |
| 647 | ||
| 648 | return binding.crypto_secretbox_open_detached(m, c, mac, n, k) | |
| 649 | } | |
| 650 | ||
| 651 | // crypto_generichash | |
| 652 | ||
| 653 | exports.crypto_generichash = function (output, input, key) { | |
| 654 | if (!key) key = OPTIONAL | |
| 655 | ||
| 656 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 657 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 658 | assert( | |
| 659 | output.byteLength >= binding.crypto_generichash_BYTES_MIN && | |
| 660 | output.byteLength <= binding.crypto_generichash_BYTES_MAX, | |
| 661 | 'output must be between crypto_generichash_BYTES_MIN and crypto_generichash_BYTES_MAX bytes' | |
| 662 | ) | |
| 663 | ||
| 664 | if (key !== OPTIONAL) { | |
| 665 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 666 | assert( | |
| 667 | key.byteLength >= binding.crypto_generichash_KEYBYTES_MIN && | |
| 668 | key.byteLength <= binding.crypto_generichash_KEYBYTES_MAX, | |
| 669 | 'key must be between crypto_generichash_KEYBYTES_MIN and crypto_generichash_KEYBYTES_MAX bytes' | |
| 670 | ) | |
| 671 | } | |
| 672 | ||
| 673 | const res = binding.crypto_generichash( | |
| 674 | output.buffer, | |
| 675 | output.byteOffset, | |
| 676 | output.byteLength, | |
| 677 | ||
| 678 | input.buffer, | |
| 679 | input.byteOffset, | |
| 680 | input.byteLength, | |
| 681 | ||
| 682 | key.buffer, | |
| 683 | key.byteOffset, | |
| 684 | key.byteLength | |
| 685 | ) | |
| 686 | ||
| 687 | if (res !== 0) throw new Error('status: ' + res) | |
| 688 | } | |
| 689 | ||
| 690 | exports.crypto_generichash_batch = function (output, batch, key) { | |
| 691 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 692 | ||
| 693 | if (isNode || batch.length < 4) { | |
| 694 | const res = binding.crypto_generichash_batch(output, batch, !!key, key || OPTIONAL) | |
| 695 | if (res !== 0) throw new Error('status: ' + res) | |
| 696 | } else { | |
| 697 | const state = Buffer.alloc(binding.crypto_generichash_STATEBYTES) | |
| 698 | ||
| 699 | exports.crypto_generichash_init(state, key, output.byteLength) | |
| 700 | ||
| 701 | for (const buf of batch) { | |
| 702 | exports.crypto_generichash_update(state, buf) | |
| 703 | } | |
| 704 | ||
| 705 | exports.crypto_generichash_final(state, output) | |
| 706 | } | |
| 707 | } | |
| 708 | ||
| 709 | exports.crypto_generichash_keygen = function (key) { | |
| 710 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 711 | assert( | |
| 712 | key.byteLength === binding.crypto_generichash_KEYBYTES, | |
| 713 | "key must be 'crypto_generichash_KEYBYTES' bytes" | |
| 714 | ) | |
| 715 | ||
| 716 | const res = binding.crypto_generichash_keygen(key.buffer, key.byteOffset, key.byteLength) | |
| 717 | ||
| 718 | if (res !== 0) throw new Error('status: ' + res) | |
| 719 | } | |
| 720 | ||
| 721 | exports.crypto_generichash_init = function (state, key, outputLength) { | |
| 722 | if (!key) key = OPTIONAL | |
| 723 | ||
| 724 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 725 | assert( | |
| 726 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 727 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 728 | ) | |
| 729 | ||
| 730 | const res = binding.crypto_generichash_init( | |
| 731 | state.buffer, | |
| 732 | state.byteOffset, | |
| 733 | state.byteLength, | |
| 734 | ||
| 735 | key.buffer, | |
| 736 | key.byteOffset, | |
| 737 | key.byteLength, | |
| 738 | ||
| 739 | outputLength | |
| 740 | ) | |
| 741 | ||
| 742 | if (res !== 0) throw new Error('status: ' + res) | |
| 743 | } | |
| 744 | ||
| 745 | exports.crypto_generichash_update = function (state, input) { | |
| 746 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 747 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 748 | assert( | |
| 749 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 750 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 751 | ) | |
| 752 | ||
| 753 | const res = binding.crypto_generichash_update( | |
| 754 | state.buffer, | |
| 755 | state.byteOffset, | |
| 756 | state.byteLength, | |
| 757 | ||
| 758 | input.buffer, | |
| 759 | input.byteOffset, | |
| 760 | input.byteLength | |
| 761 | ) | |
| 762 | ||
| 763 | if (res !== 0) throw new Error('status: ' + res) | |
| 764 | } | |
| 765 | ||
| 766 | exports.crypto_generichash_final = function (state, output) { | |
| 767 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 768 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 769 | assert( | |
| 770 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 771 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 772 | ) | |
| 773 | ||
| 774 | const res = binding.crypto_generichash_final( | |
| 775 | state.buffer, | |
| 776 | state.byteOffset, | |
| 777 | state.byteLength, | |
| 778 | ||
| 779 | output.buffer, | |
| 780 | output.byteOffset, | |
| 781 | output.byteLength | |
| 782 | ) | |
| 783 | ||
| 784 | if (res !== 0) throw new Error('status: ' + res) | |
| 785 | } | |
| 786 | ||
| 787 | // secretstream | |
| 788 | ||
| 789 | exports.crypto_secretstream_xchacha20poly1305_keygen = function (k) { | |
| 790 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 791 | assert( | |
| 792 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 793 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 794 | ) | |
| 795 | ||
| 796 | binding.crypto_secretstream_xchacha20poly1305_keygen(k.buffer, k.byteOffset, k.byteLength) | |
| 797 | } | |
| 798 | ||
| 799 | exports.crypto_secretstream_xchacha20poly1305_init_push = function (state, header, k) { | |
| 800 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 801 | assert(ArrayBuffer.isView(header), 'header must be a typed array') | |
| 802 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 803 | assert( | |
| 804 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 805 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 806 | ) | |
| 807 | assert( | |
| 808 | header.byteLength === binding.crypto_secretstream_xchacha20poly1305_HEADERBYTES, | |
| 809 | "header must be 'crypto_secretstream_xchacha20poly1305_HEADERBYTES' bytes" | |
| 810 | ) | |
| 811 | assert( | |
| 812 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 813 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 814 | ) | |
| 815 | ||
| 816 | const res = binding.crypto_secretstream_xchacha20poly1305_init_push( | |
| 817 | state.buffer, | |
| 818 | state.byteOffset, | |
| 819 | state.byteLength, | |
| 820 | ||
| 821 | header.buffer, | |
| 822 | header.byteOffset, | |
| 823 | header.byteLength, | |
| 824 | ||
| 825 | k.buffer, | |
| 826 | k.byteOffset, | |
| 827 | k.byteLength | |
| 828 | ) | |
| 829 | ||
| 830 | if (res !== 0) throw new Error('status: ' + res) | |
| 831 | } | |
| 832 | ||
| 833 | exports.crypto_secretstream_xchacha20poly1305_init_pull = function (state, header, k) { | |
| 834 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 835 | assert(ArrayBuffer.isView(header), 'header must be a typed array') | |
| 836 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 837 | assert( | |
| 838 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 839 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 840 | ) | |
| 841 | assert( | |
| 842 | header.byteLength === binding.crypto_secretstream_xchacha20poly1305_HEADERBYTES, | |
| 843 | "header must be 'crypto_secretstream_xchacha20poly1305_HEADERBYTES' bytes" | |
| 844 | ) | |
| 845 | assert( | |
| 846 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 847 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 848 | ) | |
| 849 | ||
| 850 | const res = binding.crypto_secretstream_xchacha20poly1305_init_pull( | |
| 851 | state.buffer, | |
| 852 | state.byteOffset, | |
| 853 | state.byteLength, | |
| 854 | ||
| 855 | header.buffer, | |
| 856 | header.byteOffset, | |
| 857 | header.byteLength, | |
| 858 | ||
| 859 | k.buffer, | |
| 860 | k.byteOffset, | |
| 861 | k.byteLength | |
| 862 | ) | |
| 863 | ||
| 864 | if (res !== 0) throw new Error('status: ' + res) | |
| 865 | } | |
| 866 | ||
| 867 | exports.crypto_secretstream_xchacha20poly1305_push = function (state, c, m, ad, tag) { | |
| 868 | if (!ad) ad = OPTIONAL | |
| 869 | ||
| 870 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 871 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 872 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 873 | assert( | |
| 874 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 875 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 876 | ) | |
| 877 | assert( | |
| 878 | c.byteLength === m.byteLength + binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 879 | "c must be 'm.byteLength + crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 880 | ) | |
| 881 | ||
| 882 | const res = binding.crypto_secretstream_xchacha20poly1305_push( | |
| 883 | state.buffer, | |
| 884 | state.byteOffset, | |
| 885 | state.byteLength, | |
| 886 | ||
| 887 | c.buffer, | |
| 888 | c.byteOffset, | |
| 889 | c.byteLength, | |
| 890 | ||
| 891 | m.buffer, | |
| 892 | m.byteOffset, | |
| 893 | m.byteLength, | |
| 894 | ||
| 895 | ad.buffer, | |
| 896 | ad.byteOffset, | |
| 897 | ad.byteLength, | |
| 898 | ||
| 899 | tag | |
| 900 | ) | |
| 901 | ||
| 902 | if (res < 0) throw new Error('push failed') | |
| 903 | ||
| 904 | return res | |
| 905 | } | |
| 906 | ||
| 907 | exports.crypto_secretstream_xchacha20poly1305_pull = function (state, m, tag, c, ad) { | |
| 908 | if (!ad) ad = OPTIONAL | |
| 909 | ||
| 910 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 911 | assert( | |
| 912 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 913 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 914 | ) | |
| 915 | assert(ArrayBuffer.isView(tag), 'tag must be a typed array') | |
| 916 | assert(tag.byteLength === 1, 'tag must be 1 byte') | |
| 917 | ||
| 918 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 919 | assert( | |
| 920 | c.byteLength >= binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 921 | "c must be at least 'crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 922 | ) | |
| 923 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 924 | assert( | |
| 925 | m.byteLength === c.byteLength - binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 926 | "m must be 'c.byteLength - crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 927 | ) | |
| 928 | ||
| 929 | const res = binding.crypto_secretstream_xchacha20poly1305_pull( | |
| 930 | state.buffer, | |
| 931 | state.byteOffset, | |
| 932 | state.byteLength, | |
| 933 | ||
| 934 | m.buffer, | |
| 935 | m.byteOffset, | |
| 936 | m.byteLength, | |
| 937 | ||
| 938 | tag.buffer, | |
| 939 | tag.byteOffset, | |
| 940 | tag.byteLength, | |
| 941 | ||
| 942 | c.buffer, | |
| 943 | c.byteOffset, | |
| 944 | c.byteLength, | |
| 945 | ||
| 946 | ad.buffer, | |
| 947 | ad.byteOffset, | |
| 948 | ad.byteLength | |
| 949 | ) | |
| 950 | ||
| 951 | if (res < 0) throw new Error('pull failed') | |
| 952 | ||
| 953 | return res | |
| 954 | } | |
| 955 | ||
| 956 | exports.crypto_secretstream_xchacha20poly1305_rekey = function (state) { | |
| 957 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 958 | assert( | |
| 959 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 960 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 961 | ) | |
| 962 | ||
| 963 | binding.crypto_secretstream_xchacha20poly1305_rekey( | |
| 964 | state.buffer, | |
| 965 | state.byteOffset, | |
| 966 | state.byteLength | |
| 967 | ) | |
| 968 | } | |
| 969 | ||
| 970 | // crypto_stream | |
| 971 | ||
| 972 | exports.crypto_stream = function (c, n, k) { | |
| 973 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 974 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 975 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 976 | assert( | |
| 977 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 978 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 979 | ) | |
| 980 | assert( | |
| 981 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 982 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 983 | ) | |
| 984 | ||
| 985 | const res = binding.crypto_stream(c, n, k) | |
| 986 | ||
| 987 | if (res !== 0) throw new Error('status: ' + res) | |
| 988 | } | |
| 989 | ||
| 990 | exports.crypto_stream_xor = function (c, m, n, k) { | |
| 991 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 992 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 993 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 994 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 995 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 996 | assert( | |
| 997 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 998 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 999 | ) | |
| 1000 | assert( | |
| 1001 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 1002 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 1003 | ) | |
| 1004 | ||
| 1005 | const res = binding.crypto_stream_xor( | |
| 1006 | c.buffer, | |
| 1007 | c.byteOffset, | |
| 1008 | c.byteLength, | |
| 1009 | ||
| 1010 | m.buffer, | |
| 1011 | m.byteOffset, | |
| 1012 | m.byteLength, | |
| 1013 | ||
| 1014 | n.buffer, | |
| 1015 | n.byteOffset, | |
| 1016 | n.byteLength, | |
| 1017 | ||
| 1018 | k.buffer, | |
| 1019 | k.byteOffset, | |
| 1020 | k.byteLength | |
| 1021 | ) | |
| 1022 | ||
| 1023 | if (res !== 0) throw new Error('status: ' + res) | |
| 1024 | } | |
| 1025 | ||
| 1026 | exports.crypto_stream_chacha20 = function (c, n, k) { | |
| 1027 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1028 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1029 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1030 | assert( | |
| 1031 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1032 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1033 | ) | |
| 1034 | assert( | |
| 1035 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1036 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1037 | ) | |
| 1038 | ||
| 1039 | const res = binding.crypto_stream_chacha20(c, n, k) | |
| 1040 | ||
| 1041 | if (res !== 0) throw new Error('status: ' + res) | |
| 1042 | } | |
| 1043 | ||
| 1044 | exports.crypto_stream_chacha20_xor = function (c, m, n, k) { | |
| 1045 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1046 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1047 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1048 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1049 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1050 | assert( | |
| 1051 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1052 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1053 | ) | |
| 1054 | assert( | |
| 1055 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1056 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1057 | ) | |
| 1058 | ||
| 1059 | const res = binding.crypto_stream_chacha20_xor(c, m, n, k) | |
| 1060 | ||
| 1061 | if (res !== 0) throw new Error('status: ' + res) | |
| 1062 | } | |
| 1063 | ||
| 1064 | exports.crypto_stream_chacha20_xor_ic = function (c, m, n, ic, k) { | |
| 1065 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1066 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1067 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1068 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1069 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1070 | assert( | |
| 1071 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1072 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1073 | ) | |
| 1074 | assert( | |
| 1075 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1076 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1077 | ) | |
| 1078 | ||
| 1079 | const res = binding.crypto_stream_chacha20_xor_ic(c, m, n, ic, k) | |
| 1080 | ||
| 1081 | if (res !== 0) throw new Error('status: ' + res) | |
| 1082 | } | |
| 1083 | ||
| 1084 | exports.crypto_stream_chacha20_ietf = function (c, n, k) { | |
| 1085 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1086 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1087 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1088 | assert( | |
| 1089 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1090 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1091 | ) | |
| 1092 | assert( | |
| 1093 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1094 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1095 | ) | |
| 1096 | ||
| 1097 | const res = binding.crypto_stream_chacha20_ietf(c, n, k) | |
| 1098 | ||
| 1099 | if (res !== 0) throw new Error('status: ' + res) | |
| 1100 | } | |
| 1101 | ||
| 1102 | exports.crypto_stream_chacha20_ietf_xor = function (c, m, n, k) { | |
| 1103 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1104 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1105 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1106 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1107 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1108 | assert( | |
| 1109 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1110 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1111 | ) | |
| 1112 | assert( | |
| 1113 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1114 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1115 | ) | |
| 1116 | ||
| 1117 | const res = binding.crypto_stream_chacha20_ietf_xor(c, m, n, k) | |
| 1118 | ||
| 1119 | if (res !== 0) throw new Error('status: ' + res) | |
| 1120 | } | |
| 1121 | ||
| 1122 | exports.crypto_stream_chacha20_ietf_xor_ic = function (c, m, n, ic, k) { | |
| 1123 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1124 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1125 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1126 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1127 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1128 | assert( | |
| 1129 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1130 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1131 | ) | |
| 1132 | assert( | |
| 1133 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1134 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1135 | ) | |
| 1136 | ||
| 1137 | const res = binding.crypto_stream_chacha20_ietf_xor_ic(c, m, n, ic, k) | |
| 1138 | ||
| 1139 | if (res !== 0) throw new Error('status: ' + res) | |
| 1140 | } | |
| 1141 | ||
| 1142 | exports.crypto_stream_xchacha20 = function (c, n, k) { | |
| 1143 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1144 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1145 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1146 | assert( | |
| 1147 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1148 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1149 | ) | |
| 1150 | assert( | |
| 1151 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1152 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1153 | ) | |
| 1154 | ||
| 1155 | const res = binding.crypto_stream_xchacha20(c, n, k) | |
| 1156 | ||
| 1157 | if (res !== 0) throw new Error('status: ' + res) | |
| 1158 | } | |
| 1159 | ||
| 1160 | exports.crypto_stream_xchacha20_xor = function (c, m, n, k) { | |
| 1161 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1162 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1163 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1164 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1165 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1166 | assert( | |
| 1167 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1168 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1169 | ) | |
| 1170 | assert( | |
| 1171 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1172 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1173 | ) | |
| 1174 | ||
| 1175 | const res = binding.crypto_stream_xchacha20_xor(c, m, n, k) | |
| 1176 | ||
| 1177 | if (res !== 0) throw new Error('status: ' + res) | |
| 1178 | } | |
| 1179 | ||
| 1180 | exports.crypto_stream_xchacha20_xor_ic = function (c, m, n, ic, k) { | |
| 1181 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1182 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1183 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1184 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1185 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1186 | assert( | |
| 1187 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1188 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1189 | ) | |
| 1190 | assert( | |
| 1191 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1192 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1193 | ) | |
| 1194 | ||
| 1195 | const res = binding.crypto_stream_xchacha20_xor_ic(c, m, n, ic, k) | |
| 1196 | ||
| 1197 | if (res !== 0) throw new Error('status: ' + res) | |
| 1198 | } | |
| 1199 | ||
| 1200 | exports.crypto_stream_salsa20 = function (c, n, k) { | |
| 1201 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1202 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1203 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1204 | assert( | |
| 1205 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1206 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1207 | ) | |
| 1208 | assert( | |
| 1209 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1210 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1211 | ) | |
| 1212 | ||
| 1213 | const res = binding.crypto_stream_salsa20(c, n, k) | |
| 1214 | ||
| 1215 | if (res !== 0) throw new Error('status: ' + res) | |
| 1216 | } | |
| 1217 | ||
| 1218 | exports.crypto_stream_salsa20_xor = function (c, m, n, k) { | |
| 1219 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1220 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1221 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1222 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1223 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1224 | assert( | |
| 1225 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1226 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1227 | ) | |
| 1228 | assert( | |
| 1229 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1230 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1231 | ) | |
| 1232 | ||
| 1233 | const res = binding.crypto_stream_salsa20_xor(c, m, n, k) | |
| 1234 | ||
| 1235 | if (res !== 0) throw new Error('status: ' + res) | |
| 1236 | } | |
| 1237 | ||
| 1238 | exports.crypto_stream_salsa20_xor_ic = function (c, m, n, ic, k) { | |
| 1239 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1240 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1241 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1242 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1243 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1244 | assert( | |
| 1245 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1246 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1247 | ) | |
| 1248 | assert( | |
| 1249 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1250 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1251 | ) | |
| 1252 | ||
| 1253 | const res = binding.crypto_stream_salsa20_xor_ic(c, m, n, ic, k) | |
| 1254 | ||
| 1255 | if (res !== 0) throw new Error('status: ' + res) | |
| 1256 | } | |
| 1257 | ||
| 1258 | // crypto_auth | |
| 1259 | ||
| 1260 | exports.crypto_auth = function (out, input, k) { | |
| 1261 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1262 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1263 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1264 | assert(out.byteLength === binding.crypto_auth_BYTES, "out must be 'crypto_auth_BYTES' bytes") | |
| 1265 | assert(k.byteLength === binding.crypto_auth_KEYBYTES, "k must be 'crypto_auth_KEYBYTES' bytes") | |
| 1266 | ||
| 1267 | const res = binding.crypto_auth(out, input, k) | |
| 1268 | ||
| 1269 | if (res !== 0) throw new Error('status: ' + res) | |
| 1270 | } | |
| 1271 | ||
| 1272 | exports.crypto_auth_verify = function (h, input, k) { | |
| 1273 | assert(ArrayBuffer.isView(h), 'h must be a typed array') | |
| 1274 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1275 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1276 | assert(h.byteLength === binding.crypto_auth_BYTES, "h must be 'crypto_auth_BYTES' bytes") | |
| 1277 | assert(k.byteLength === binding.crypto_auth_KEYBYTES, "k must be 'crypto_auth_KEYBYTES' bytes") | |
| 1278 | ||
| 1279 | return binding.crypto_auth_verify(h, input, k) | |
| 1280 | } | |
| 1281 | ||
| 1282 | // crypto_onetimeauth | |
| 1283 | ||
| 1284 | exports.crypto_onetimeauth = function (out, input, k) { | |
| 1285 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1286 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1287 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1288 | assert( | |
| 1289 | out.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1290 | "out must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1291 | ) | |
| 1292 | assert( | |
| 1293 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1294 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1295 | ) | |
| 1296 | ||
| 1297 | const res = binding.crypto_onetimeauth(out, input, k) | |
| 1298 | ||
| 1299 | if (res !== 0) throw new Error('status: ' + res) | |
| 1300 | } | |
| 1301 | ||
| 1302 | exports.crypto_onetimeauth_init = function (state, k) { | |
| 1303 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1304 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1305 | assert( | |
| 1306 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1307 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1308 | ) | |
| 1309 | assert( | |
| 1310 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1311 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1312 | ) | |
| 1313 | ||
| 1314 | const res = binding.crypto_onetimeauth_init(state, k) | |
| 1315 | ||
| 1316 | if (res !== 0) throw new Error('status: ' + res) | |
| 1317 | } | |
| 1318 | ||
| 1319 | exports.crypto_onetimeauth_update = function (state, input) { | |
| 1320 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1321 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1322 | assert( | |
| 1323 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1324 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1325 | ) | |
| 1326 | ||
| 1327 | const res = binding.crypto_onetimeauth_update(state, input) | |
| 1328 | ||
| 1329 | if (res !== 0) throw new Error('status: ' + res) | |
| 1330 | } | |
| 1331 | ||
| 1332 | exports.crypto_onetimeauth_final = function (state, out) { | |
| 1333 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1334 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1335 | assert( | |
| 1336 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1337 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1338 | ) | |
| 1339 | assert( | |
| 1340 | out.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1341 | "out must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1342 | ) | |
| 1343 | ||
| 1344 | const res = binding.crypto_onetimeauth_final(state, out) | |
| 1345 | ||
| 1346 | if (res !== 0) throw new Error('status: ' + res) | |
| 1347 | } | |
| 1348 | ||
| 1349 | exports.crypto_onetimeauth_verify = function (h, input, k) { | |
| 1350 | assert(ArrayBuffer.isView(h), 'h must be a typed array') | |
| 1351 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1352 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1353 | assert( | |
| 1354 | h.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1355 | "h must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1356 | ) | |
| 1357 | assert( | |
| 1358 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1359 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1360 | ) | |
| 1361 | ||
| 1362 | return binding.crypto_onetimeauth_verify(h, input, k) | |
| 1363 | } | |
| 1364 | ||
| 1365 | // crypto_pwhash | |
| 1366 | ||
| 1367 | exports.crypto_pwhash = function (out, passwd, salt, opslimit, memlimit, alg) { | |
| 1368 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1369 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1370 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1371 | assert( | |
| 1372 | out.byteLength >= binding.crypto_pwhash_BYTES_MIN, | |
| 1373 | "out must be at least 'crypto_pwhash_BYTES_MIN' bytes" | |
| 1374 | ) | |
| 1375 | assert( | |
| 1376 | out.byteLength <= binding.crypto_pwhash_BYTES_MAX, | |
| 1377 | "out must be at most 'crypto_pwhash_BYTES_MAX' bytes" | |
| 1378 | ) | |
| 1379 | assert( | |
| 1380 | salt.byteLength === binding.crypto_pwhash_SALTBYTES, | |
| 1381 | "salt must be 'crypto_pwhash_SALTBYTES' bytes" | |
| 1382 | ) | |
| 1383 | assert( | |
| 1384 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1385 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1386 | ) | |
| 1387 | assert( | |
| 1388 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1389 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1390 | ) | |
| 1391 | assert( | |
| 1392 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1393 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1394 | ) | |
| 1395 | assert( | |
| 1396 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1397 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1398 | ) | |
| 1399 | assert(alg >= 1 && alg <= 2, 'alg must be either Argon2i 1.3 or Argon2id 1.3') | |
| 1400 | ||
| 1401 | const res = binding.crypto_pwhash(out, passwd, salt, opslimit, memlimit, alg) | |
| 1402 | ||
| 1403 | if (res !== 0) throw new Error('status: ' + res) | |
| 1404 | } | |
| 1405 | ||
| 1406 | exports.crypto_pwhash_async = function ( | |
| 1407 | out, | |
| 1408 | passwd, | |
| 1409 | salt, | |
| 1410 | opslimit, | |
| 1411 | memlimit, | |
| 1412 | alg, | |
| 1413 | callback = undefined | |
| 1414 | ) { | |
| 1415 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1416 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1417 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1418 | assert( | |
| 1419 | out.byteLength >= binding.crypto_pwhash_BYTES_MIN, | |
| 1420 | "out must be at least 'crypto_pwhash_BYTES_MIN' bytes" | |
| 1421 | ) | |
| 1422 | assert( | |
| 1423 | out.byteLength <= binding.crypto_pwhash_BYTES_MAX, | |
| 1424 | "out must be at most 'crypto_pwhash_BYTES_MAX' bytes" | |
| 1425 | ) | |
| 1426 | assert( | |
| 1427 | salt.byteLength === binding.crypto_pwhash_SALTBYTES, | |
| 1428 | "salt must be 'crypto_pwhash_SALTBYTES' bytes" | |
| 1429 | ) | |
| 1430 | assert( | |
| 1431 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1432 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1433 | ) | |
| 1434 | assert( | |
| 1435 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1436 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1437 | ) | |
| 1438 | assert( | |
| 1439 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1440 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1441 | ) | |
| 1442 | assert( | |
| 1443 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1444 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1445 | ) | |
| 1446 | assert(alg >= 1 && alg <= 2, 'alg must be either Argon2i 1.3 or Argon2id 1.3') | |
| 1447 | ||
| 1448 | const [done, promise] = checkStatus(callback) | |
| 1449 | ||
| 1450 | binding.crypto_pwhash_async( | |
| 1451 | out.buffer, | |
| 1452 | out.byteOffset, | |
| 1453 | out.byteLength, | |
| 1454 | ||
| 1455 | passwd.buffer, | |
| 1456 | passwd.byteOffset, | |
| 1457 | passwd.byteLength, | |
| 1458 | ||
| 1459 | salt.buffer, | |
| 1460 | salt.byteOffset, | |
| 1461 | salt.byteLength, | |
| 1462 | ||
| 1463 | opslimit, | |
| 1464 | memlimit, | |
| 1465 | alg, | |
| 1466 | ||
| 1467 | done | |
| 1468 | ) | |
| 1469 | ||
| 1470 | return promise | |
| 1471 | } | |
| 1472 | ||
| 1473 | exports.crypto_pwhash_str = function (out, passwd, opslimit, memlimit) { | |
| 1474 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1475 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1476 | assert( | |
| 1477 | out.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1478 | "out must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1479 | ) | |
| 1480 | assert(typeof opslimit === 'number', 'opslimit must be a number') | |
| 1481 | assert( | |
| 1482 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1483 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1484 | ) | |
| 1485 | assert( | |
| 1486 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1487 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1488 | ) | |
| 1489 | assert(typeof memlimit === 'number', 'memlimit must be a number') | |
| 1490 | assert( | |
| 1491 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1492 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1493 | ) | |
| 1494 | assert( | |
| 1495 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1496 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1497 | ) | |
| 1498 | ||
| 1499 | const res = binding.crypto_pwhash_str(out, passwd, opslimit, memlimit) | |
| 1500 | ||
| 1501 | if (res !== 0) throw new Error('status: ' + res) | |
| 1502 | } | |
| 1503 | ||
| 1504 | exports.crypto_pwhash_str_async = function (out, passwd, opslimit, memlimit, callback = undefined) { | |
| 1505 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1506 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1507 | assert( | |
| 1508 | out.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1509 | "out must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1510 | ) | |
| 1511 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1512 | assert(typeof opslimit === 'number', 'opslimit must be a number') | |
| 1513 | assert( | |
| 1514 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1515 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1516 | ) | |
| 1517 | assert( | |
| 1518 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1519 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1520 | ) | |
| 1521 | assert(typeof memlimit === 'number', 'memlimit must be a number') | |
| 1522 | assert( | |
| 1523 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1524 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1525 | ) | |
| 1526 | assert( | |
| 1527 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1528 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1529 | ) | |
| 1530 | ||
| 1531 | const [done, promise] = checkStatus(callback) | |
| 1532 | ||
| 1533 | binding.crypto_pwhash_str_async( | |
| 1534 | out.buffer, | |
| 1535 | out.byteOffset, | |
| 1536 | out.byteLength, | |
| 1537 | ||
| 1538 | passwd.buffer, | |
| 1539 | passwd.byteOffset, | |
| 1540 | passwd.byteLength, | |
| 1541 | ||
| 1542 | opslimit, | |
| 1543 | memlimit, | |
| 1544 | ||
| 1545 | done | |
| 1546 | ) | |
| 1547 | ||
| 1548 | return promise | |
| 1549 | } | |
| 1550 | ||
| 1551 | exports.crypto_pwhash_str_verify = function (str, passwd) { | |
| 1552 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1553 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1554 | assert( | |
| 1555 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1556 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1557 | ) | |
| 1558 | ||
| 1559 | return binding.crypto_pwhash_str_verify(str, passwd) | |
| 1560 | } | |
| 1561 | ||
| 1562 | exports.crypto_pwhash_str_verify_async = function (str, passwd, callback = undefined) { | |
| 1563 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1564 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1565 | assert( | |
| 1566 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1567 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1568 | ) | |
| 1569 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1570 | ||
| 1571 | const [done, promise] = checkStatus(callback, true) | |
| 1572 | ||
| 1573 | binding.crypto_pwhash_str_verify_async( | |
| 1574 | str.buffer, | |
| 1575 | str.byteOffset, | |
| 1576 | str.byteLength, | |
| 1577 | ||
| 1578 | passwd.buffer, | |
| 1579 | passwd.byteOffset, | |
| 1580 | passwd.byteLength, | |
| 1581 | ||
| 1582 | done | |
| 1583 | ) | |
| 1584 | ||
| 1585 | return promise | |
| 1586 | } | |
| 1587 | ||
| 1588 | exports.crypto_pwhash_str_needs_rehash = function (str, opslimit, memlimit) { | |
| 1589 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1590 | assert( | |
| 1591 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1592 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1593 | ) | |
| 1594 | assert( | |
| 1595 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1596 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1597 | ) | |
| 1598 | assert( | |
| 1599 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1600 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1601 | ) | |
| 1602 | assert( | |
| 1603 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1604 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1605 | ) | |
| 1606 | assert( | |
| 1607 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1608 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1609 | ) | |
| 1610 | ||
| 1611 | return binding.crypto_pwhash_str_needs_rehash(str, opslimit, memlimit) | |
| 1612 | } | |
| 1613 | ||
| 1614 | exports.crypto_pwhash_scryptsalsa208sha256 = function (out, passwd, salt, opslimit, memlimit) { | |
| 1615 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1616 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1617 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1618 | assert( | |
| 1619 | out.byteLength >= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN, | |
| 1620 | "out must be at least 'crypto_pwhash_scryptsalsa208sha256_BYTES_MIN' bytes" | |
| 1621 | ) | |
| 1622 | assert( | |
| 1623 | out.byteLength <= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX, | |
| 1624 | "out must be at most 'crypto_pwhash_scryptsalsa208sha256_BYTES_MAX' bytes" | |
| 1625 | ) | |
| 1626 | assert( | |
| 1627 | salt.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_SALTBYTES, | |
| 1628 | "salt must be 'crypto_pwhash_scryptsalsa208sha256_SALTBYTES' bytes" | |
| 1629 | ) | |
| 1630 | assert( | |
| 1631 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1632 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1633 | ) | |
| 1634 | assert( | |
| 1635 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1636 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1637 | ) | |
| 1638 | assert( | |
| 1639 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1640 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1641 | ) | |
| 1642 | assert( | |
| 1643 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1644 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1645 | ) | |
| 1646 | ||
| 1647 | const res = binding.crypto_pwhash_scryptsalsa208sha256(out, passwd, salt, opslimit, memlimit) | |
| 1648 | ||
| 1649 | if (res !== 0) throw new Error('status: ' + res) | |
| 1650 | } | |
| 1651 | ||
| 1652 | exports.crypto_pwhash_scryptsalsa208sha256_async = function ( | |
| 1653 | out, | |
| 1654 | passwd, | |
| 1655 | salt, | |
| 1656 | opslimit, | |
| 1657 | memlimit, | |
| 1658 | callback = undefined | |
| 1659 | ) { | |
| 1660 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1661 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1662 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1663 | assert( | |
| 1664 | out.byteLength >= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN, | |
| 1665 | "out must be at least 'crypto_pwhash_scryptsalsa208sha256_BYTES_MIN' bytes" | |
| 1666 | ) | |
| 1667 | assert( | |
| 1668 | out.byteLength <= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX, | |
| 1669 | "out must be at most 'crypto_pwhash_scryptsalsa208sha256_BYTES_MAX' bytes" | |
| 1670 | ) | |
| 1671 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1672 | assert( | |
| 1673 | salt.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_SALTBYTES, | |
| 1674 | "salt must be 'crypto_pwhash_scryptsalsa208sha256_SALTBYTES' bytes" | |
| 1675 | ) | |
| 1676 | assert( | |
| 1677 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1678 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1679 | ) | |
| 1680 | assert( | |
| 1681 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1682 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1683 | ) | |
| 1684 | assert( | |
| 1685 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1686 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1687 | ) | |
| 1688 | assert( | |
| 1689 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1690 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1691 | ) | |
| 1692 | ||
| 1693 | const [done, promise] = checkStatus(callback) | |
| 1694 | ||
| 1695 | binding.crypto_pwhash_scryptsalsa208sha256_async( | |
| 1696 | out.buffer, | |
| 1697 | out.byteOffset, | |
| 1698 | out.byteLength, | |
| 1699 | ||
| 1700 | passwd.buffer, | |
| 1701 | passwd.byteOffset, | |
| 1702 | passwd.byteLength, | |
| 1703 | ||
| 1704 | salt.buffer, | |
| 1705 | salt.byteOffset, | |
| 1706 | salt.byteLength, | |
| 1707 | ||
| 1708 | opslimit, | |
| 1709 | memlimit, | |
| 1710 | ||
| 1711 | done | |
| 1712 | ) | |
| 1713 | ||
| 1714 | return promise | |
| 1715 | } | |
| 1716 | ||
| 1717 | exports.crypto_pwhash_scryptsalsa208sha256_str_async = function ( | |
| 1718 | out, | |
| 1719 | passwd, | |
| 1720 | opslimit, | |
| 1721 | memlimit, | |
| 1722 | callback = undefined | |
| 1723 | ) { | |
| 1724 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1725 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1726 | assert( | |
| 1727 | out.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1728 | "out must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1729 | ) | |
| 1730 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1731 | assert( | |
| 1732 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1733 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1734 | ) | |
| 1735 | assert( | |
| 1736 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1737 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1738 | ) | |
| 1739 | assert( | |
| 1740 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1741 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1742 | ) | |
| 1743 | assert( | |
| 1744 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1745 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1746 | ) | |
| 1747 | ||
| 1748 | const [done, promise] = checkStatus(callback) | |
| 1749 | ||
| 1750 | binding.crypto_pwhash_scryptsalsa208sha256_str_async( | |
| 1751 | out.buffer, | |
| 1752 | out.byteOffset, | |
| 1753 | out.byteLength, | |
| 1754 | ||
| 1755 | passwd.buffer, | |
| 1756 | passwd.byteOffset, | |
| 1757 | passwd.byteLength, | |
| 1758 | ||
| 1759 | opslimit, | |
| 1760 | memlimit, | |
| 1761 | ||
| 1762 | done | |
| 1763 | ) | |
| 1764 | ||
| 1765 | return promise | |
| 1766 | } | |
| 1767 | ||
| 1768 | exports.crypto_pwhash_scryptsalsa208sha256_str = function (out, passwd, opslimit, memlimit) { | |
| 1769 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1770 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1771 | assert( | |
| 1772 | out.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1773 | "out must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1774 | ) | |
| 1775 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1776 | assert( | |
| 1777 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1778 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1779 | ) | |
| 1780 | assert( | |
| 1781 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1782 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1783 | ) | |
| 1784 | assert( | |
| 1785 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1786 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1787 | ) | |
| 1788 | assert( | |
| 1789 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1790 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1791 | ) | |
| 1792 | ||
| 1793 | const res = binding.crypto_pwhash_scryptsalsa208sha256_str(out, passwd, opslimit, memlimit) | |
| 1794 | ||
| 1795 | if (res !== 0) throw new Error('status: ' + res) | |
| 1796 | } | |
| 1797 | ||
| 1798 | exports.crypto_pwhash_scryptsalsa208sha256_str_verify_async = function ( | |
| 1799 | str, | |
| 1800 | passwd, | |
| 1801 | callback = undefined | |
| 1802 | ) { | |
| 1803 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1804 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1805 | assert( | |
| 1806 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1807 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1808 | ) | |
| 1809 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1810 | ||
| 1811 | const [done, promise] = checkStatus(callback, true) | |
| 1812 | ||
| 1813 | binding.crypto_pwhash_scryptsalsa208sha256_str_verify_async( | |
| 1814 | str.buffer, | |
| 1815 | str.byteOffset, | |
| 1816 | str.byteLength, | |
| 1817 | ||
| 1818 | passwd.buffer, | |
| 1819 | passwd.byteOffset, | |
| 1820 | passwd.byteLength, | |
| 1821 | ||
| 1822 | done | |
| 1823 | ) | |
| 1824 | ||
| 1825 | return promise | |
| 1826 | } | |
| 1827 | ||
| 1828 | exports.crypto_pwhash_scryptsalsa208sha256_str_verify = function (str, passwd) { | |
| 1829 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1830 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1831 | assert( | |
| 1832 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1833 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1834 | ) | |
| 1835 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1836 | ||
| 1837 | return binding.crypto_pwhash_scryptsalsa208sha256_str_verify(str, passwd) | |
| 1838 | } | |
| 1839 | ||
| 1840 | exports.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash = function (str, opslimit, memlimit) { | |
| 1841 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1842 | assert( | |
| 1843 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1844 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1845 | ) | |
| 1846 | assert( | |
| 1847 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1848 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1849 | ) | |
| 1850 | assert( | |
| 1851 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1852 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1853 | ) | |
| 1854 | assert( | |
| 1855 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1856 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1857 | ) | |
| 1858 | assert( | |
| 1859 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1860 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1861 | ) | |
| 1862 | ||
| 1863 | return binding.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(str, opslimit, memlimit) | |
| 1864 | } | |
| 1865 | ||
| 1866 | // crypto_kx | |
| 1867 | ||
| 1868 | exports.crypto_kx_keypair = function (pk, sk) { | |
| 1869 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 1870 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 1871 | assert( | |
| 1872 | pk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1873 | "pk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1874 | ) | |
| 1875 | assert( | |
| 1876 | sk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1877 | "sk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1878 | ) | |
| 1879 | ||
| 1880 | const res = binding.crypto_kx_keypair(pk, sk) | |
| 1881 | ||
| 1882 | if (res !== 0) throw new Error('status: ' + res) | |
| 1883 | } | |
| 1884 | ||
| 1885 | exports.crypto_kx_seed_keypair = function (pk, sk, seed) { | |
| 1886 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 1887 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 1888 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 1889 | assert( | |
| 1890 | pk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1891 | "pk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1892 | ) | |
| 1893 | assert( | |
| 1894 | sk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1895 | "sk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1896 | ) | |
| 1897 | assert( | |
| 1898 | seed.byteLength === binding.crypto_kx_SEEDBYTES, | |
| 1899 | "seed must be 'crypto_kx_SEEDBYTES' bytes" | |
| 1900 | ) | |
| 1901 | ||
| 1902 | const res = binding.crypto_kx_seed_keypair(pk, sk, seed) | |
| 1903 | ||
| 1904 | if (res !== 0) throw new Error('status: ' + res) | |
| 1905 | } | |
| 1906 | ||
| 1907 | exports.crypto_kx_client_session_keys = function (rx, tx, clientPk, clientSk, serverPk) { | |
| 1908 | if (!rx) rx = undefined | |
| 1909 | if (!tx) tx = undefined | |
| 1910 | ||
| 1911 | assert(rx || tx, 'at least one session key must be specified') | |
| 1912 | ||
| 1913 | if (rx) { | |
| 1914 | assert(ArrayBuffer.isView(rx), 'rx must be a typed array') | |
| 1915 | assert( | |
| 1916 | rx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1917 | "rx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1918 | ) | |
| 1919 | } | |
| 1920 | ||
| 1921 | if (tx) { | |
| 1922 | assert(ArrayBuffer.isView(tx), 'tx must be a typed array') | |
| 1923 | assert( | |
| 1924 | tx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1925 | "tx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1926 | ) | |
| 1927 | } | |
| 1928 | ||
| 1929 | assert(ArrayBuffer.isView(clientPk), 'clientPk must be a typed array') | |
| 1930 | assert(ArrayBuffer.isView(clientSk), 'clientSk must be a typed array') | |
| 1931 | assert(ArrayBuffer.isView(serverPk), 'serverPk must be a typed array') | |
| 1932 | assert( | |
| 1933 | clientPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1934 | "clientPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1935 | ) | |
| 1936 | assert( | |
| 1937 | clientSk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1938 | "clientSk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1939 | ) | |
| 1940 | assert( | |
| 1941 | serverPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1942 | "serverPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1943 | ) | |
| 1944 | ||
| 1945 | const res = binding.crypto_kx_client_session_keys(rx, tx, clientPk, clientSk, serverPk) | |
| 1946 | ||
| 1947 | if (res !== 0) throw new Error('status: ' + res) | |
| 1948 | } | |
| 1949 | ||
| 1950 | exports.crypto_kx_server_session_keys = function (rx, tx, serverPk, serverSk, clientPk) { | |
| 1951 | if (!rx) rx = undefined | |
| 1952 | if (!tx) tx = undefined | |
| 1953 | ||
| 1954 | assert(rx || tx, 'at least one session key must be specified') | |
| 1955 | ||
| 1956 | if (rx) { | |
| 1957 | assert(ArrayBuffer.isView(rx), 'rx must be a typed array') | |
| 1958 | assert( | |
| 1959 | rx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1960 | "rx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1961 | ) | |
| 1962 | } | |
| 1963 | ||
| 1964 | if (tx) { | |
| 1965 | assert(ArrayBuffer.isView(tx), 'tx must be a typed array') | |
| 1966 | assert( | |
| 1967 | tx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1968 | "tx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1969 | ) | |
| 1970 | } | |
| 1971 | ||
| 1972 | assert(ArrayBuffer.isView(serverPk), 'serverPk must be a typed array') | |
| 1973 | assert(ArrayBuffer.isView(serverSk), 'serverSk must be a typed array') | |
| 1974 | assert(ArrayBuffer.isView(clientPk), 'clientPk must be a typed array') | |
| 1975 | assert( | |
| 1976 | serverPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1977 | "serverPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1978 | ) | |
| 1979 | assert( | |
| 1980 | serverSk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1981 | "serverSk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1982 | ) | |
| 1983 | assert( | |
| 1984 | clientPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1985 | "clientPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1986 | ) | |
| 1987 | ||
| 1988 | const res = binding.crypto_kx_server_session_keys(rx, tx, serverPk, serverSk, clientPk) | |
| 1989 | ||
| 1990 | if (res !== 0) throw new Error('status: ' + res) | |
| 1991 | } | |
| 1992 | ||
| 1993 | // crypto_scalarmult | |
| 1994 | ||
| 1995 | exports.crypto_scalarmult_base = function (q, n) { | |
| 1996 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 1997 | assert( | |
| 1998 | q.byteLength === binding.crypto_scalarmult_BYTES, | |
| 1999 | "q must be 'crypto_scalarmult_BYTES' bytes" | |
| 2000 | ) | |
| 2001 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2002 | assert( | |
| 2003 | n.byteLength === binding.crypto_scalarmult_SCALARBYTES, | |
| 2004 | "n must be 'crypto_scalarmult_SCALARBYTES' bytes" | |
| 2005 | ) | |
| 2006 | ||
| 2007 | const res = binding.crypto_scalarmult_base(q, n) | |
| 2008 | ||
| 2009 | if (res !== 0) throw new Error('status: ' + res) | |
| 2010 | } | |
| 2011 | ||
| 2012 | exports.crypto_scalarmult = function (q, n, p) { | |
| 2013 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2014 | assert( | |
| 2015 | q.byteLength === binding.crypto_scalarmult_BYTES, | |
| 2016 | "q must be 'crypto_scalarmult_BYTES' bytes" | |
| 2017 | ) | |
| 2018 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2019 | assert( | |
| 2020 | n.byteLength === binding.crypto_scalarmult_SCALARBYTES, | |
| 2021 | "n must be 'crypto_scalarmult_SCALARBYTES' bytes" | |
| 2022 | ) | |
| 2023 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2024 | assert( | |
| 2025 | p.byteLength === binding.crypto_scalarmult_BYTES, | |
| 2026 | "p must be 'crypto_scalarmult_BYTES' bytes" | |
| 2027 | ) | |
| 2028 | ||
| 2029 | const res = binding.crypto_scalarmult(q, n, p) | |
| 2030 | ||
| 2031 | if (res !== 0) throw new Error('status: ' + res) | |
| 2032 | } | |
| 2033 | ||
| 2034 | exports.crypto_scalarmult_ed25519_base = function (q, n) { | |
| 2035 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2036 | assert( | |
| 2037 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2038 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2039 | ) | |
| 2040 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2041 | assert( | |
| 2042 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2043 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2044 | ) | |
| 2045 | ||
| 2046 | const res = binding.crypto_scalarmult_ed25519_base(q, n) | |
| 2047 | ||
| 2048 | if (res !== 0) throw new Error('status: ' + res) | |
| 2049 | } | |
| 2050 | ||
| 2051 | exports.crypto_scalarmult_ed25519 = function (q, n, p) { | |
| 2052 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2053 | assert( | |
| 2054 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2055 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2056 | ) | |
| 2057 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2058 | assert( | |
| 2059 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2060 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2061 | ) | |
| 2062 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2063 | assert( | |
| 2064 | p.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2065 | "p must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2066 | ) | |
| 2067 | ||
| 2068 | const res = binding.crypto_scalarmult_ed25519(q, n, p) | |
| 2069 | ||
| 2070 | if (res !== 0) throw new Error('status: ' + res) | |
| 2071 | } | |
| 2072 | ||
| 2073 | exports.crypto_core_ed25519_is_valid_point = function (p) { | |
| 2074 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2075 | assert( | |
| 2076 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2077 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2078 | ) | |
| 2079 | ||
| 2080 | return binding.crypto_core_ed25519_is_valid_point(p) | |
| 2081 | } | |
| 2082 | ||
| 2083 | exports.crypto_core_ed25519_from_uniform = function (p, r) { | |
| 2084 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2085 | assert( | |
| 2086 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2087 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2088 | ) | |
| 2089 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2090 | assert( | |
| 2091 | r.byteLength === binding.crypto_core_ed25519_UNIFORMBYTES, | |
| 2092 | "r must be 'crypto_core_ed25519_UNIFORMBYTES' bytes" | |
| 2093 | ) | |
| 2094 | ||
| 2095 | const res = binding.crypto_core_ed25519_from_uniform(p, r) | |
| 2096 | ||
| 2097 | if (res !== 0) throw new Error('status: ' + res) | |
| 2098 | } | |
| 2099 | ||
| 2100 | exports.crypto_scalarmult_ed25519_base_noclamp = function (q, n) { | |
| 2101 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2102 | assert( | |
| 2103 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2104 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2105 | ) | |
| 2106 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2107 | assert( | |
| 2108 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2109 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2110 | ) | |
| 2111 | ||
| 2112 | const res = binding.crypto_scalarmult_ed25519_base_noclamp(q, n) | |
| 2113 | ||
| 2114 | if (res !== 0) throw new Error('status: ' + res) | |
| 2115 | } | |
| 2116 | ||
| 2117 | exports.crypto_scalarmult_ed25519_noclamp = function (q, n, p) { | |
| 2118 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2119 | assert( | |
| 2120 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2121 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2122 | ) | |
| 2123 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2124 | assert( | |
| 2125 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2126 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2127 | ) | |
| 2128 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2129 | assert( | |
| 2130 | p.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2131 | "p must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2132 | ) | |
| 2133 | ||
| 2134 | const res = binding.crypto_scalarmult_ed25519_noclamp(q, n, p) | |
| 2135 | ||
| 2136 | if (res !== 0) throw new Error('status: ' + res) | |
| 2137 | } | |
| 2138 | ||
| 2139 | // crypto_core | |
| 2140 | ||
| 2141 | exports.crypto_core_ed25519_add = function (r, p, q) { | |
| 2142 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2143 | assert( | |
| 2144 | r.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2145 | "r must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2146 | ) | |
| 2147 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2148 | assert( | |
| 2149 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2150 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2151 | ) | |
| 2152 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2153 | assert( | |
| 2154 | q.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2155 | "q must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2156 | ) | |
| 2157 | ||
| 2158 | const res = binding.crypto_core_ed25519_add(r, p, q) | |
| 2159 | ||
| 2160 | if (res !== 0) throw new Error('status: ' + res) | |
| 2161 | } | |
| 2162 | ||
| 2163 | exports.crypto_core_ed25519_sub = function (r, p, q) { | |
| 2164 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2165 | assert( | |
| 2166 | r.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2167 | "r must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2168 | ) | |
| 2169 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2170 | assert( | |
| 2171 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2172 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2173 | ) | |
| 2174 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2175 | assert( | |
| 2176 | q.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2177 | "q must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2178 | ) | |
| 2179 | ||
| 2180 | const res = binding.crypto_core_ed25519_sub(r, p, q) | |
| 2181 | ||
| 2182 | if (res !== 0) throw new Error('status: ' + res) | |
| 2183 | } | |
| 2184 | ||
| 2185 | exports.crypto_core_ed25519_scalar_random = function (r) { | |
| 2186 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2187 | assert( | |
| 2188 | r.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2189 | "r must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2190 | ) | |
| 2191 | ||
| 2192 | binding.crypto_core_ed25519_scalar_random(r) | |
| 2193 | } | |
| 2194 | ||
| 2195 | exports.crypto_core_ed25519_scalar_reduce = function (r, s) { | |
| 2196 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2197 | assert( | |
| 2198 | r.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2199 | "r must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2200 | ) | |
| 2201 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2202 | assert( | |
| 2203 | s.byteLength === binding.crypto_core_ed25519_NONREDUCEDSCALARBYTES, | |
| 2204 | "s must be 'crypto_core_ed25519_NONREDUCEDSCALARBYTES' bytes" | |
| 2205 | ) | |
| 2206 | ||
| 2207 | binding.crypto_core_ed25519_scalar_reduce(r, s) | |
| 2208 | } | |
| 2209 | ||
| 2210 | exports.crypto_core_ed25519_scalar_invert = function (recip, s) { | |
| 2211 | assert(ArrayBuffer.isView(recip), 'recip must be a typed array') | |
| 2212 | assert( | |
| 2213 | recip.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2214 | "recip must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2215 | ) | |
| 2216 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2217 | assert( | |
| 2218 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2219 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2220 | ) | |
| 2221 | ||
| 2222 | binding.crypto_core_ed25519_scalar_invert(recip, s) | |
| 2223 | } | |
| 2224 | ||
| 2225 | exports.crypto_core_ed25519_scalar_negate = function (neg, s) { | |
| 2226 | assert(ArrayBuffer.isView(neg), 'neg must be a typed array') | |
| 2227 | assert( | |
| 2228 | neg.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2229 | "neg must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2230 | ) | |
| 2231 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2232 | assert( | |
| 2233 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2234 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2235 | ) | |
| 2236 | ||
| 2237 | binding.crypto_core_ed25519_scalar_negate(neg, s) | |
| 2238 | } | |
| 2239 | ||
| 2240 | exports.crypto_core_ed25519_scalar_complement = function (comp, s) { | |
| 2241 | assert(ArrayBuffer.isView(comp), 'comp must be a typed array') | |
| 2242 | assert( | |
| 2243 | comp.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2244 | "comp must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2245 | ) | |
| 2246 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2247 | assert( | |
| 2248 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2249 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2250 | ) | |
| 2251 | ||
| 2252 | binding.crypto_core_ed25519_scalar_complement(comp, s) | |
| 2253 | } | |
| 2254 | ||
| 2255 | exports.crypto_core_ed25519_scalar_add = function (z, x, y) { | |
| 2256 | assert(ArrayBuffer.isView(z), 'z must be a typed array') | |
| 2257 | assert( | |
| 2258 | z.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2259 | "z must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2260 | ) | |
| 2261 | assert(ArrayBuffer.isView(x), 'x must be a typed array') | |
| 2262 | assert( | |
| 2263 | x.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2264 | "x must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2265 | ) | |
| 2266 | assert(ArrayBuffer.isView(y), 'y must be a typed array') | |
| 2267 | assert( | |
| 2268 | y.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2269 | "y must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2270 | ) | |
| 2271 | ||
| 2272 | binding.crypto_core_ed25519_scalar_add(z, x, y) | |
| 2273 | } | |
| 2274 | ||
| 2275 | exports.crypto_core_ed25519_scalar_sub = function (z, x, y) { | |
| 2276 | assert(ArrayBuffer.isView(z), 'z must be a typed array') | |
| 2277 | assert( | |
| 2278 | z.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2279 | "z must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2280 | ) | |
| 2281 | assert(ArrayBuffer.isView(x), 'x must be a typed array') | |
| 2282 | assert( | |
| 2283 | x.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2284 | "x must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2285 | ) | |
| 2286 | assert(ArrayBuffer.isView(y), 'y must be a typed array') | |
| 2287 | assert( | |
| 2288 | y.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2289 | "y must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2290 | ) | |
| 2291 | ||
| 2292 | binding.crypto_core_ed25519_scalar_sub(z, x, y) | |
| 2293 | } | |
| 2294 | ||
| 2295 | // crypto_shorthash | |
| 2296 | ||
| 2297 | exports.crypto_shorthash = function (out, input, k) { | |
| 2298 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2299 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2300 | assert( | |
| 2301 | out.byteLength === binding.crypto_shorthash_BYTES, | |
| 2302 | "out must be 'crypto_shorthash_BYTES' bytes" | |
| 2303 | ) | |
| 2304 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2305 | assert( | |
| 2306 | k.byteLength === binding.crypto_shorthash_KEYBYTES, | |
| 2307 | "k must be 'crypto_shorthash_KEYBYTES' bytes" | |
| 2308 | ) | |
| 2309 | ||
| 2310 | const res = binding.crypto_shorthash(out, input, k) | |
| 2311 | ||
| 2312 | if (res !== 0) throw new Error('status: ' + res) | |
| 2313 | } | |
| 2314 | ||
| 2315 | // crypto_kdf | |
| 2316 | ||
| 2317 | exports.crypto_kdf_keygen = function (key) { | |
| 2318 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 2319 | assert(key.byteLength === binding.crypto_kdf_KEYBYTES, "key must be 'crypto_kdf_KEYBYTES' bytes") | |
| 2320 | ||
| 2321 | binding.crypto_kdf_keygen(key) | |
| 2322 | } | |
| 2323 | ||
| 2324 | exports.crypto_kdf_derive_from_key = function (subkey, subkeyId, ctx, key) { | |
| 2325 | assert(ArrayBuffer.isView(subkey), 'subkey must be a typed array') | |
| 2326 | assert( | |
| 2327 | subkey.byteLength >= binding.crypto_kdf_BYTES_MIN, | |
| 2328 | "subkey must be at least 'crypto_kdf_BYTES_MIN' bytes" | |
| 2329 | ) | |
| 2330 | assert( | |
| 2331 | subkey.byteLength <= binding.crypto_kdf_BYTES_MAX, | |
| 2332 | "subkey must be at most 'crypto_kdf_BYTES_MAX' bytes" | |
| 2333 | ) | |
| 2334 | assert(ArrayBuffer.isView(ctx), 'ctx must be a typed array') | |
| 2335 | assert( | |
| 2336 | ctx.byteLength === binding.crypto_kdf_CONTEXTBYTES, | |
| 2337 | "ctx must be 'crypto_kdf_CONTEXTBYTES' bytes" | |
| 2338 | ) | |
| 2339 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 2340 | assert(key.byteLength === binding.crypto_kdf_KEYBYTES, "key must be 'crypto_kdf_KEYBYTES' bytes") | |
| 2341 | ||
| 2342 | const res = binding.crypto_kdf_derive_from_key(subkey, subkeyId, ctx, key) | |
| 2343 | ||
| 2344 | if (res !== 0) throw new Error('status: ' + res) | |
| 2345 | } | |
| 2346 | ||
| 2347 | // crypto_hash | |
| 2348 | ||
| 2349 | exports.crypto_hash = function (out, input) { | |
| 2350 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2351 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2352 | assert(out.byteLength === binding.crypto_hash_BYTES, "out must be 'crypto_hash_BYTES' bytes") | |
| 2353 | ||
| 2354 | const res = binding.crypto_hash(out, input) | |
| 2355 | ||
| 2356 | if (res !== 0) throw new Error('status: ' + res) | |
| 2357 | } | |
| 2358 | ||
| 2359 | exports.crypto_hash_sha256 = function (out, input) { | |
| 2360 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2361 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2362 | assert( | |
| 2363 | out.byteLength === binding.crypto_hash_sha256_BYTES, | |
| 2364 | "out must be 'crypto_hash_sha256_BYTES' bytes" | |
| 2365 | ) | |
| 2366 | ||
| 2367 | const res = binding.crypto_hash_sha256(out, input) | |
| 2368 | ||
| 2369 | if (res !== 0) throw new Error('status: ' + res) | |
| 2370 | } | |
| 2371 | ||
| 2372 | exports.crypto_hash_sha256_init = function (state) { | |
| 2373 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2374 | assert( | |
| 2375 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2376 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2377 | ) | |
| 2378 | ||
| 2379 | const res = binding.crypto_hash_sha256_init(state) | |
| 2380 | ||
| 2381 | if (res !== 0) throw new Error('status: ' + res) | |
| 2382 | } | |
| 2383 | ||
| 2384 | exports.crypto_hash_sha256_update = function (state, input) { | |
| 2385 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2386 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2387 | assert( | |
| 2388 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2389 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2390 | ) | |
| 2391 | ||
| 2392 | const res = binding.crypto_hash_sha256_update(state, input) | |
| 2393 | ||
| 2394 | if (res !== 0) throw new Error('status: ' + res) | |
| 2395 | } | |
| 2396 | ||
| 2397 | exports.crypto_hash_sha256_final = function (state, out) { | |
| 2398 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2399 | assert( | |
| 2400 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2401 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2402 | ) | |
| 2403 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2404 | assert( | |
| 2405 | out.byteLength === binding.crypto_hash_sha256_BYTES, | |
| 2406 | "out must be 'crypto_hash_sha256_BYTES' bytes" | |
| 2407 | ) | |
| 2408 | ||
| 2409 | const res = binding.crypto_hash_sha256_final(state, out) | |
| 2410 | ||
| 2411 | if (res !== 0) throw new Error('status: ' + res) | |
| 2412 | } | |
| 2413 | ||
| 2414 | exports.crypto_hash_sha512 = function (out, input) { | |
| 2415 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2416 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2417 | assert( | |
| 2418 | out.byteLength === binding.crypto_hash_sha512_BYTES, | |
| 2419 | "out must be 'crypto_hash_sha512_BYTES' bytes" | |
| 2420 | ) | |
| 2421 | ||
| 2422 | const res = binding.crypto_hash_sha512(out, input) | |
| 2423 | ||
| 2424 | if (res !== 0) throw new Error('status: ' + res) | |
| 2425 | } | |
| 2426 | ||
| 2427 | exports.crypto_hash_sha512_init = function (state) { | |
| 2428 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2429 | assert( | |
| 2430 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2431 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2432 | ) | |
| 2433 | ||
| 2434 | const res = binding.crypto_hash_sha512_init(state) | |
| 2435 | ||
| 2436 | if (res !== 0) throw new Error('status: ' + res) | |
| 2437 | } | |
| 2438 | ||
| 2439 | exports.crypto_hash_sha512_update = function (state, input) { | |
| 2440 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2441 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2442 | assert( | |
| 2443 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2444 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2445 | ) | |
| 2446 | ||
| 2447 | const res = binding.crypto_hash_sha512_update(state, input) | |
| 2448 | ||
| 2449 | if (res !== 0) throw new Error('status: ' + res) | |
| 2450 | } | |
| 2451 | ||
| 2452 | exports.crypto_hash_sha512_final = function (state, out) { | |
| 2453 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2454 | assert( | |
| 2455 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2456 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2457 | ) | |
| 2458 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2459 | assert( | |
| 2460 | out.byteLength === binding.crypto_hash_sha512_BYTES, | |
| 2461 | "out must be 'crypto_hash_sha512_BYTES' bytes" | |
| 2462 | ) | |
| 2463 | ||
| 2464 | const res = binding.crypto_hash_sha512_final(state, out) | |
| 2465 | ||
| 2466 | if (res !== 0) throw new Error('status: ' + res) | |
| 2467 | } | |
| 2468 | ||
| 2469 | // crypto_aead | |
| 2470 | ||
| 2471 | exports.crypto_aead_xchacha20poly1305_ietf_keygen = function (k) { | |
| 2472 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2473 | assert( | |
| 2474 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2475 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2476 | ) | |
| 2477 | ||
| 2478 | binding.crypto_aead_xchacha20poly1305_ietf_keygen(k) | |
| 2479 | } | |
| 2480 | ||
| 2481 | exports.crypto_aead_xchacha20poly1305_ietf_encrypt = function (c, m, ad, nsec, npub, k) { | |
| 2482 | if (!ad) ad = undefined | |
| 2483 | ||
| 2484 | assert(nsec === null, 'nsec must always be set to null') | |
| 2485 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2486 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2487 | assert( | |
| 2488 | c.byteLength === m.byteLength + binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2489 | "c must be 'm.byteLength + crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2490 | ) | |
| 2491 | assert(c.byteLength <= 0xffffffff, 'c.byteLength must be a 32bit integer') | |
| 2492 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2493 | assert( | |
| 2494 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2495 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2496 | ) | |
| 2497 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2498 | assert( | |
| 2499 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2500 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2501 | ) | |
| 2502 | ||
| 2503 | const res = binding.crypto_aead_xchacha20poly1305_ietf_encrypt(c, m, ad, npub, k) | |
| 2504 | ||
| 2505 | if (res < 0) throw new Error('could not encrypt data') | |
| 2506 | ||
| 2507 | return res | |
| 2508 | } | |
| 2509 | ||
| 2510 | exports.crypto_aead_xchacha20poly1305_ietf_decrypt = function (m, nsec, c, ad, npub, k) { | |
| 2511 | if (!ad) ad = undefined | |
| 2512 | ||
| 2513 | assert(nsec === null, 'nsec must always be set to null') | |
| 2514 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2515 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2516 | assert( | |
| 2517 | m.byteLength === c.byteLength - binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2518 | "m must be 'c.byteLength - crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2519 | ) | |
| 2520 | assert(m.byteLength <= 0xffffffff, 'm.byteLength must be a 32bit integer') | |
| 2521 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2522 | assert( | |
| 2523 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2524 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2525 | ) | |
| 2526 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2527 | assert( | |
| 2528 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2529 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2530 | ) | |
| 2531 | ||
| 2532 | const res = binding.crypto_aead_xchacha20poly1305_ietf_decrypt(m, c, ad, npub, k) | |
| 2533 | ||
| 2534 | if (res < 0) throw new Error('could not verify data') | |
| 2535 | ||
| 2536 | return res | |
| 2537 | } | |
| 2538 | ||
| 2539 | exports.crypto_aead_xchacha20poly1305_ietf_encrypt_detached = function ( | |
| 2540 | c, | |
| 2541 | mac, | |
| 2542 | m, | |
| 2543 | ad, | |
| 2544 | nsec, | |
| 2545 | npub, | |
| 2546 | k | |
| 2547 | ) { | |
| 2548 | if (!ad) ad = undefined | |
| 2549 | ||
| 2550 | assert(nsec === null, 'nsec must always be set to null') | |
| 2551 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2552 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2553 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2554 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2555 | assert( | |
| 2556 | mac.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2557 | "mac must be 'crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2558 | ) | |
| 2559 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2560 | assert( | |
| 2561 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2562 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2563 | ) | |
| 2564 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2565 | assert( | |
| 2566 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2567 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2568 | ) | |
| 2569 | ||
| 2570 | const res = binding.crypto_aead_xchacha20poly1305_ietf_encrypt_detached(c, mac, m, ad, npub, k) | |
| 2571 | ||
| 2572 | if (res < 0) throw new Error('could not encrypt data') | |
| 2573 | ||
| 2574 | return res | |
| 2575 | } | |
| 2576 | ||
| 2577 | exports.crypto_aead_xchacha20poly1305_ietf_decrypt_detached = function ( | |
| 2578 | m, | |
| 2579 | nsec, | |
| 2580 | c, | |
| 2581 | mac, | |
| 2582 | ad, | |
| 2583 | npub, | |
| 2584 | k | |
| 2585 | ) { | |
| 2586 | if (!ad) ad = undefined | |
| 2587 | ||
| 2588 | assert(nsec === null, 'nsec must always be set to null') | |
| 2589 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2590 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2591 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 2592 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2593 | assert( | |
| 2594 | mac.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2595 | "mac must be 'crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2596 | ) | |
| 2597 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2598 | assert( | |
| 2599 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2600 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2601 | ) | |
| 2602 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2603 | assert( | |
| 2604 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2605 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2606 | ) | |
| 2607 | ||
| 2608 | const res = binding.crypto_aead_xchacha20poly1305_ietf_decrypt_detached(m, c, mac, ad, npub, k) | |
| 2609 | ||
| 2610 | if (res !== 0) throw new Error('could not verify data') | |
| 2611 | } | |
| 2612 | ||
| 2613 | exports.crypto_aead_chacha20poly1305_ietf_keygen = function (k) { | |
| 2614 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2615 | assert( | |
| 2616 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2617 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2618 | ) | |
| 2619 | ||
| 2620 | binding.crypto_aead_chacha20poly1305_ietf_keygen(k) | |
| 2621 | } | |
| 2622 | ||
| 2623 | exports.crypto_aead_chacha20poly1305_ietf_encrypt = function (c, m, ad, nsec, npub, k) { | |
| 2624 | if (!ad) ad = undefined | |
| 2625 | ||
| 2626 | assert(nsec === null, 'nsec must always be set to null') | |
| 2627 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2628 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2629 | assert( | |
| 2630 | c.byteLength === m.byteLength + binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2631 | "c must be 'm.byteLength + crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2632 | ) | |
| 2633 | assert(c.byteLength <= 0xffffffff, 'c.byteLength must be a 32bit integer') | |
| 2634 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2635 | assert( | |
| 2636 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2637 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2638 | ) | |
| 2639 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2640 | assert( | |
| 2641 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2642 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2643 | ) | |
| 2644 | ||
| 2645 | const res = binding.crypto_aead_chacha20poly1305_ietf_encrypt(c, m, ad, npub, k) | |
| 2646 | ||
| 2647 | if (res < 0) throw new Error('could not encrypt data') | |
| 2648 | ||
| 2649 | return res | |
| 2650 | } | |
| 2651 | ||
| 2652 | exports.crypto_aead_chacha20poly1305_ietf_decrypt = function (m, nsec, c, ad, npub, k) { | |
| 2653 | if (!ad) ad = undefined | |
| 2654 | ||
| 2655 | assert(nsec === null, 'nsec must always be set to null') | |
| 2656 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2657 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2658 | assert( | |
| 2659 | m.byteLength === c.byteLength - binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2660 | "m must be 'c.byteLength - crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2661 | ) | |
| 2662 | assert(m.byteLength <= 0xffffffff, 'm.byteLength must be a 32bit integer') | |
| 2663 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2664 | assert( | |
| 2665 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2666 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2667 | ) | |
| 2668 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2669 | assert( | |
| 2670 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2671 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2672 | ) | |
| 2673 | ||
| 2674 | const res = binding.crypto_aead_chacha20poly1305_ietf_decrypt(m, c, ad, npub, k) | |
| 2675 | ||
| 2676 | if (res < 0) throw new Error('could not verify data') | |
| 2677 | ||
| 2678 | return res | |
| 2679 | } | |
| 2680 | ||
| 2681 | exports.crypto_aead_chacha20poly1305_ietf_encrypt_detached = function ( | |
| 2682 | c, | |
| 2683 | mac, | |
| 2684 | m, | |
| 2685 | ad, | |
| 2686 | nsec, | |
| 2687 | npub, | |
| 2688 | k | |
| 2689 | ) { | |
| 2690 | if (!ad) ad = undefined | |
| 2691 | ||
| 2692 | assert(nsec === null, 'nsec must always be set to null') | |
| 2693 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2694 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2695 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2696 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2697 | assert( | |
| 2698 | mac.byteLength === binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2699 | "mac must be 'crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2700 | ) | |
| 2701 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2702 | assert( | |
| 2703 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2704 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2705 | ) | |
| 2706 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2707 | assert( | |
| 2708 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2709 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2710 | ) | |
| 2711 | ||
| 2712 | const res = binding.crypto_aead_chacha20poly1305_ietf_encrypt_detached(c, mac, m, ad, npub, k) | |
| 2713 | ||
| 2714 | if (res < 0) throw new Error('could not encrypt data') | |
| 2715 | ||
| 2716 | return res | |
| 2717 | } | |
| 2718 | ||
| 2719 | exports.crypto_aead_chacha20poly1305_ietf_decrypt_detached = function ( | |
| 2720 | m, | |
| 2721 | nsec, | |
| 2722 | c, | |
| 2723 | mac, | |
| 2724 | ad, | |
| 2725 | npub, | |
| 2726 | k | |
| 2727 | ) { | |
| 2728 | if (!ad) ad = undefined | |
| 2729 | ||
| 2730 | assert(nsec === null, 'nsec must always be set to null') | |
| 2731 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2732 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2733 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 2734 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2735 | assert( | |
| 2736 | mac.byteLength === binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2737 | "mac must be 'crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2738 | ) | |
| 2739 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2740 | assert( | |
| 2741 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2742 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2743 | ) | |
| 2744 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2745 | assert( | |
| 2746 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2747 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2748 | ) | |
| 2749 | ||
| 2750 | const res = binding.crypto_aead_chacha20poly1305_ietf_decrypt_detached(m, c, mac, ad, npub, k) | |
| 2751 | ||
| 2752 | if (res !== 0) throw new Error('could not verify data') | |
| 2753 | } | |
| 2754 | ||
| 2755 | // crypto_stream | |
| 2756 | ||
| 2757 | exports.crypto_stream_xor_wrap_init = function (state, n, k) { | |
| 2758 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2759 | assert( | |
| 2760 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2761 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2762 | ) | |
| 2763 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2764 | assert( | |
| 2765 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 2766 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 2767 | ) | |
| 2768 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2769 | assert( | |
| 2770 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 2771 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 2772 | ) | |
| 2773 | ||
| 2774 | binding.crypto_stream_xor_wrap_init(state, n, k) | |
| 2775 | } | |
| 2776 | ||
| 2777 | exports.crypto_stream_xor_wrap_update = function (state, c, m) { | |
| 2778 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2779 | assert( | |
| 2780 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2781 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2782 | ) | |
| 2783 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2784 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2785 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2786 | ||
| 2787 | binding.crypto_stream_xor_wrap_update(state, c, m) | |
| 2788 | } | |
| 2789 | ||
| 2790 | exports.crypto_stream_xor_wrap_final = function (state) { | |
| 2791 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2792 | assert( | |
| 2793 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2794 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2795 | ) | |
| 2796 | ||
| 2797 | binding.crypto_stream_xor_wrap_final(state) | |
| 2798 | } | |
| 2799 | ||
| 2800 | exports.crypto_stream_chacha20_xor_wrap_init = function (state, n, k) { | |
| 2801 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2802 | assert( | |
| 2803 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2804 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2805 | ) | |
| 2806 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2807 | assert( | |
| 2808 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 2809 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 2810 | ) | |
| 2811 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2812 | assert( | |
| 2813 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 2814 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 2815 | ) | |
| 2816 | ||
| 2817 | binding.crypto_stream_chacha20_xor_wrap_init(state, n, k) | |
| 2818 | } | |
| 2819 | ||
| 2820 | exports.crypto_stream_chacha20_xor_wrap_update = function (state, c, m) { | |
| 2821 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2822 | assert( | |
| 2823 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2824 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2825 | ) | |
| 2826 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2827 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2828 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2829 | ||
| 2830 | binding.crypto_stream_chacha20_xor_wrap_update(state, c, m) | |
| 2831 | } | |
| 2832 | ||
| 2833 | exports.crypto_stream_chacha20_xor_wrap_final = function (state) { | |
| 2834 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2835 | assert( | |
| 2836 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2837 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2838 | ) | |
| 2839 | ||
| 2840 | binding.crypto_stream_chacha20_xor_wrap_final(state) | |
| 2841 | } | |
| 2842 | ||
| 2843 | exports.crypto_stream_chacha20_ietf_xor_wrap_init = function (state, n, k) { | |
| 2844 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2845 | assert( | |
| 2846 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2847 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2848 | ) | |
| 2849 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2850 | assert( | |
| 2851 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 2852 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 2853 | ) | |
| 2854 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2855 | assert( | |
| 2856 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 2857 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 2858 | ) | |
| 2859 | ||
| 2860 | binding.crypto_stream_chacha20_ietf_xor_wrap_init(state, n, k) | |
| 2861 | } | |
| 2862 | ||
| 2863 | exports.crypto_stream_chacha20_ietf_xor_wrap_update = function (state, c, m) { | |
| 2864 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2865 | assert( | |
| 2866 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2867 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2868 | ) | |
| 2869 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2870 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2871 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2872 | ||
| 2873 | binding.crypto_stream_chacha20_ietf_xor_wrap_update(state, c, m) | |
| 2874 | } | |
| 2875 | ||
| 2876 | exports.crypto_stream_chacha20_ietf_xor_wrap_final = function (state) { | |
| 2877 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2878 | assert( | |
| 2879 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2880 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2881 | ) | |
| 2882 | ||
| 2883 | binding.crypto_stream_chacha20_ietf_xor_wrap_final(state) | |
| 2884 | } | |
| 2885 | ||
| 2886 | exports.crypto_stream_xchacha20_xor_wrap_init = function (state, n, k) { | |
| 2887 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2888 | assert( | |
| 2889 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2890 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2891 | ) | |
| 2892 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2893 | assert( | |
| 2894 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 2895 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 2896 | ) | |
| 2897 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2898 | assert( | |
| 2899 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 2900 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 2901 | ) | |
| 2902 | ||
| 2903 | binding.crypto_stream_xchacha20_xor_wrap_init(state, n, k) | |
| 2904 | } | |
| 2905 | ||
| 2906 | exports.crypto_stream_xchacha20_xor_wrap_update = function (state, c, m) { | |
| 2907 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2908 | assert( | |
| 2909 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2910 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2911 | ) | |
| 2912 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2913 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2914 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2915 | ||
| 2916 | binding.crypto_stream_xchacha20_xor_wrap_update(state, c, m) | |
| 2917 | } | |
| 2918 | ||
| 2919 | exports.crypto_stream_xchacha20_xor_wrap_final = function (state) { | |
| 2920 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2921 | assert( | |
| 2922 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2923 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2924 | ) | |
| 2925 | ||
| 2926 | binding.crypto_stream_xchacha20_xor_wrap_final(state) | |
| 2927 | } | |
| 2928 | ||
| 2929 | exports.crypto_stream_salsa20_xor_wrap_init = function (state, n, k) { | |
| 2930 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2931 | assert( | |
| 2932 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2933 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2934 | ) | |
| 2935 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2936 | assert( | |
| 2937 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 2938 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 2939 | ) | |
| 2940 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2941 | assert( | |
| 2942 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 2943 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 2944 | ) | |
| 2945 | ||
| 2946 | binding.crypto_stream_salsa20_xor_wrap_init(state, n, k) | |
| 2947 | } | |
| 2948 | ||
| 2949 | exports.crypto_stream_salsa20_xor_wrap_update = function (state, c, m) { | |
| 2950 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2951 | assert( | |
| 2952 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2953 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2954 | ) | |
| 2955 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2956 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2957 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2958 | ||
| 2959 | binding.crypto_stream_salsa20_xor_wrap_update(state, c, m) | |
| 2960 | } | |
| 2961 | ||
| 2962 | exports.crypto_stream_salsa20_xor_wrap_final = function (state) { | |
| 2963 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2964 | assert( | |
| 2965 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2966 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2967 | ) | |
| 2968 | ||
| 2969 | binding.crypto_stream_salsa20_xor_wrap_final(state) | |
| 2970 | } | |
| 2971 | ||
| 2972 | // experimental | |
| 2973 | ||
| 2974 | exports.extension_tweak_ed25519_base = function (n, p, ns) { | |
| 2975 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2976 | assert( | |
| 2977 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 2978 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 2979 | ) | |
| 2980 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2981 | assert( | |
| 2982 | p.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 2983 | "p must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 2984 | ) | |
| 2985 | ||
| 2986 | binding.extension_tweak_ed25519_base(n, p, ns) | |
| 2987 | } | |
| 2988 | ||
| 2989 | exports.extension_tweak_ed25519_sign_detached = function (sig, m, scalar, pk) { | |
| 2990 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 2991 | assert(sig.byteLength === binding.crypto_sign_BYTES, "sig must be 'crypto_sign_BYTES' bytes") | |
| 2992 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2993 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 2994 | assert( | |
| 2995 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 2996 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 2997 | ) | |
| 2998 | ||
| 2999 | if (pk) { | |
| 3000 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3001 | assert( | |
| 3002 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3003 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3004 | ) | |
| 3005 | } | |
| 3006 | ||
| 3007 | const res = binding.extension_tweak_ed25519_sign_detached(sig, m, scalar, pk) | |
| 3008 | ||
| 3009 | if (res !== 0) throw new Error('failed to compute signature') | |
| 3010 | } | |
| 3011 | ||
| 3012 | exports.extension_tweak_ed25519_sk_to_scalar = function (n, sk) { | |
| 3013 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 3014 | assert( | |
| 3015 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3016 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3017 | ) | |
| 3018 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 3019 | assert( | |
| 3020 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 3021 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 3022 | ) | |
| 3023 | ||
| 3024 | binding.extension_tweak_ed25519_sk_to_scalar(n, sk) | |
| 3025 | } | |
| 3026 | ||
| 3027 | exports.extension_tweak_ed25519_scalar = function (scalarOut, scalar, ns) { | |
| 3028 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3029 | assert( | |
| 3030 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3031 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3032 | ) | |
| 3033 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 3034 | assert( | |
| 3035 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3036 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3037 | ) | |
| 3038 | ||
| 3039 | binding.extension_tweak_ed25519_scalar(scalarOut, scalar, ns) | |
| 3040 | } | |
| 3041 | ||
| 3042 | exports.extension_tweak_ed25519_pk = function (tpk, pk, ns) { | |
| 3043 | assert(ArrayBuffer.isView(tpk), 'tpk must be a typed array') | |
| 3044 | assert( | |
| 3045 | tpk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3046 | "tpk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3047 | ) | |
| 3048 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3049 | assert( | |
| 3050 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3051 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3052 | ) | |
| 3053 | ||
| 3054 | const res = binding.extension_tweak_ed25519_pk(tpk, pk, ns) | |
| 3055 | ||
| 3056 | if (res !== 0) throw new Error('failed to tweak public key') | |
| 3057 | } | |
| 3058 | ||
| 3059 | exports.extension_tweak_ed25519_keypair = function (pk, scalarOut, scalarIn, ns) { | |
| 3060 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3061 | assert( | |
| 3062 | pk.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 3063 | "pk must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 3064 | ) | |
| 3065 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3066 | assert( | |
| 3067 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3068 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3069 | ) | |
| 3070 | assert(ArrayBuffer.isView(scalarIn), 'scalarIn must be a typed array') | |
| 3071 | assert( | |
| 3072 | scalarIn.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3073 | "scalarIn must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3074 | ) | |
| 3075 | ||
| 3076 | binding.extension_tweak_ed25519_keypair(pk, scalarOut, scalarIn, ns) | |
| 3077 | } | |
| 3078 | ||
| 3079 | exports.extension_tweak_ed25519_scalar_add = function (scalarOut, scalar, n) { | |
| 3080 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3081 | assert( | |
| 3082 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3083 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3084 | ) | |
| 3085 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 3086 | assert( | |
| 3087 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3088 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3089 | ) | |
| 3090 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 3091 | assert( | |
| 3092 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3093 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3094 | ) | |
| 3095 | ||
| 3096 | binding.extension_tweak_ed25519_scalar_add(scalarOut, scalar, n) | |
| 3097 | } | |
| 3098 | ||
| 3099 | exports.extension_tweak_ed25519_pk_add = function (tpk, pk, p) { | |
| 3100 | assert(ArrayBuffer.isView(tpk), 'tpk must be a typed array') | |
| 3101 | assert( | |
| 3102 | tpk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3103 | "tpk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3104 | ) | |
| 3105 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3106 | assert( | |
| 3107 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3108 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3109 | ) | |
| 3110 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 3111 | assert( | |
| 3112 | p.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3113 | "p must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3114 | ) | |
| 3115 | ||
| 3116 | const res = binding.extension_tweak_ed25519_pk_add(tpk, pk, p) | |
| 3117 | ||
| 3118 | if (res !== 0) throw new Error('failed to add tweak to public key') | |
| 3119 | } | |
| 3120 | ||
| 3121 | exports.extension_tweak_ed25519_keypair_add = function (pk, scalarOut, scalarIn, tweak) { | |
| 3122 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3123 | assert( | |
| 3124 | pk.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 3125 | "pk must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 3126 | ) | |
| 3127 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3128 | assert( | |
| 3129 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3130 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3131 | ) | |
| 3132 | assert(ArrayBuffer.isView(scalarIn), 'scalarIn must be a typed array') | |
| 3133 | assert( | |
| 3134 | scalarIn.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3135 | "scalarIn must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3136 | ) | |
| 3137 | assert(ArrayBuffer.isView(tweak), 'tweak must be a typed array') | |
| 3138 | assert( | |
| 3139 | tweak.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3140 | "tweak must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3141 | ) | |
| 3142 | ||
| 3143 | const res = binding.extension_tweak_ed25519_keypair_add(pk, scalarOut, scalarIn, tweak) | |
| 3144 | ||
| 3145 | if (res !== 0) throw new Error('failed to add tweak to keypair') | |
| 3146 | } | |
| 3147 | ||
| 3148 | exports.extension_pbkdf2_sha512_async = function (out, passwd, salt, iter, outlen, callback) { | |
| 3149 | assert( | |
| 3150 | iter >= binding.extension_pbkdf2_sha512_ITERATIONS_MIN, | |
| 3151 | "iter must be at least 'extension_pbkdf2_sha512_ITERATIONS_MIN'" | |
| 3152 | ) | |
| 3153 | assert( | |
| 3154 | outlen <= binding.extension_pbkdf2_sha512_BYTES_MAX, | |
| 3155 | "outlen must be at most 'extension_pbkdf2_sha512_BYTES_MAX'" | |
| 3156 | ) | |
| 3157 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 3158 | assert(out.byteLength >= outlen, "out must be at least 'outlen' bytes") | |
| 3159 | assert(out.byteLength > 0, 'out must not be empty') | |
| 3160 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 3161 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 3162 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 3163 | assert(salt.byteLength > 0, 'salt must not be empty') | |
| 3164 | ||
| 3165 | const [done, promise] = checkStatus(callback) | |
| 3166 | ||
| 3167 | binding.extension_pbkdf2_sha512_async( | |
| 3168 | out.buffer, | |
| 3169 | out.byteOffset, | |
| 3170 | out.byteLength, | |
| 3171 | ||
| 3172 | passwd.buffer, | |
| 3173 | passwd.byteOffset, | |
| 3174 | passwd.byteLength, | |
| 3175 | ||
| 3176 | salt.buffer, | |
| 3177 | salt.byteOffset, | |
| 3178 | salt.byteLength, | |
| 3179 | ||
| 3180 | iter, | |
| 3181 | outlen, | |
| 3182 | ||
| 3183 | done | |
| 3184 | ) | |
| 3185 | ||
| 3186 | return promise | |
| 3187 | } | |
| 3188 | ||
| 3189 | exports.extension_pbkdf2_sha512 = function (out, passwd, salt, iter, outlen) { | |
| 3190 | assert( | |
| 3191 | iter >= binding.extension_pbkdf2_sha512_ITERATIONS_MIN, | |
| 3192 | "iter must be at least 'extension_pbkdf2_sha512_ITERATIONS_MIN'" | |
| 3193 | ) | |
| 3194 | assert( | |
| 3195 | outlen <= binding.extension_pbkdf2_sha512_BYTES_MAX, | |
| 3196 | "outlen must be at most 'extension_pbkdf2_sha512_BYTES_MAX'" | |
| 3197 | ) | |
| 3198 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 3199 | assert(out.byteLength >= outlen, "out must be at least 'outlen' bytes") | |
| 3200 | assert(out.byteLength > 0, 'out must not be empty') | |
| 3201 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 3202 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 3203 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 3204 | assert(salt.byteLength > 0, 'salt must not be empty') | |
| 3205 | ||
| 3206 | const res = binding.extension_pbkdf2_sha512(out, passwd, salt, iter, outlen) | |
| 3207 | ||
| 3208 | if (res !== 0) throw new Error('failed to add tweak to public key') | |
| 3209 | } | |
| 3210 | ||
| 3211 | function checkStatus(callback, booleanResult = false) { | |
| 3212 | let done, promise | |
| 3213 | ||
| 3214 | if (typeof callback === 'function') { | |
| 3215 | done = function (status) { | |
| 3216 | if (booleanResult) callback(null, status === 0) | |
| 3217 | else if (status === 0) callback(null) | |
| 3218 | else callback(new Error('status: ' + status)) | |
| 3219 | } | |
| 3220 | } else { | |
| 3221 | promise = new Promise(function (resolve, reject) { | |
| 3222 | done = function (status) { | |
| 3223 | if (booleanResult) resolve(status === 0) | |
| 3224 | else if (status === 0) resolve() | |
| 3225 | else reject(new Error('status: ' + status)) | |
| 3226 | } | |
| 3227 | }) | |
| 3228 | } | |
| 3229 | ||
| 3230 | return [done, promise] | |
| 3231 | } |
| … | … | @@ -0,0 +1,62 @@ |
| 1 | { | |
| 2 | "name": "sodium-native", | |
| 3 | "version": "5.1.0", | |
| 4 | "description": "Low level bindings for libsodium", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js", | |
| 8 | "binding.cc", | |
| 9 | "binding.js", | |
| 10 | "extensions", | |
| 11 | "prebuilds", | |
| 12 | "CMakeLists.txt" | |
| 13 | ], | |
| 14 | "imports": { | |
| 15 | "assert": { | |
| 16 | "bare": "bare-assert", | |
| 17 | "node": "assert" | |
| 18 | } | |
| 19 | }, | |
| 20 | "addon": true, | |
| 21 | "dependencies": { | |
| 22 | "bare-assert": "^1.2.0", | |
| 23 | "require-addon": "^1.1.0", | |
| 24 | "which-runtime": "^1.2.1" | |
| 25 | }, | |
| 26 | "devDependencies": { | |
| 27 | "bare-compat-napi": "^1.3.5", | |
| 28 | "brittle": "^3.16.2", | |
| 29 | "cmake-bare": "^1.6.1", | |
| 30 | "cmake-fetch": "^1.4.3", | |
| 31 | "cmake-napi": "^1.2.1", | |
| 32 | "prettier": "^3.6.2", | |
| 33 | "prettier-config-holepunch": "^2.0.0" | |
| 34 | }, | |
| 35 | "scripts": { | |
| 36 | "test": "prettier . --check && npm run test:node && npm run test:bare", | |
| 37 | "test:node": "node test/all.js", | |
| 38 | "test:bare": "bare test/all.js" | |
| 39 | }, | |
| 40 | "standard": { | |
| 41 | "ignore": [ | |
| 42 | "/test/fixtures/*.js" | |
| 43 | ] | |
| 44 | }, | |
| 45 | "engines": { | |
| 46 | "bare": ">=1.16.0" | |
| 47 | }, | |
| 48 | "repository": { | |
| 49 | "type": "git", | |
| 50 | "url": "git+https://github.com/holepunchto/sodium-native.git" | |
| 51 | }, | |
| 52 | "contributors": [ | |
| 53 | "Emil Bay <github@tixz.dk> (http://bayes.dk)", | |
| 54 | "Mathias Buus <mathiasbuus@gmail.com> (https://mafinto.sh)", | |
| 55 | "Christophe Diederichs <chm-diederichs@hyperdivision.dk>" | |
| 56 | ], | |
| 57 | "license": "MIT", | |
| 58 | "bugs": { | |
| 59 | "url": "https://github.com/holepunchto/sodium-native/issues" | |
| 60 | }, | |
| 61 | "homepage": "https://github.com/holepunchto/sodium-native" | |
| 62 | } |
| … | … | @@ -0,0 +1,7 @@ |
| 1 | Copyright 2017 Mathias Buus & Emil Bay | |
| 2 | ||
| 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
| 4 | ||
| 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
| 6 | ||
| 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| … | … | @@ -0,0 +1,288 @@ |
| 1 | # sodium-universal | |
| 2 | ||
| 3 | Universal wrapper for `sodium-javascript` and `sodium-native` working in Node.js and the Browser | |
| 4 | ||
| 5 | ```sh | |
| 6 | npm install sodium-universal | |
| 7 | ``` | |
| 8 | ||
| 9 | `sodium-native` ships with prebuilds for macOS, Windows, and Linux. | |
| 10 | ||
| 11 | Note that the module itself is generated on a prepublish hook. | |
| 12 | ||
| 13 | ## Usage | |
| 14 | ||
| 15 | ```js | |
| 16 | var sodium = require('sodium-universal') | |
| 17 | ||
| 18 | var rnd = Buffer.allocUnsafe(12) // Cryptographically random data | |
| 19 | sodium.randombytes_buf(rnd) | |
| 20 | ||
| 21 | console.log(rnd.toString('hex')) | |
| 22 | ||
| 23 | ``` | |
| 24 | ||
| 25 | Works seamlessly with Node.js: | |
| 26 | ||
| 27 | ```sh | |
| 28 | $ node example.js | |
| 29 | c7dbd46a6cc84ff2e0d1285c | |
| 30 | ``` | |
| 31 | ||
| 32 | And the browser: | |
| 33 | ||
| 34 | First install the peer dep, `sodium-javascript`. | |
| 35 | ||
| 36 | ```sh | |
| 37 | npm install --save sodium-javascript | |
| 38 | ``` | |
| 39 | ||
| 40 | And then browserify | |
| 41 | ||
| 42 | ```sh | |
| 43 | browserify example.js > bundle.js | |
| 44 | ``` | |
| 45 | ||
| 46 | ## Introduction | |
| 47 | ||
| 48 | [`libsodium`](https://download.libsodium.org/doc/) is a collection of cryptographic primitives, providing a low-level foundation to build higher-level cryptographic applications and protocols. It is often put in contrast to RSA based cryptography and OpenSSL, even though they all share overlapping algorithms and target slightly different audiences. Sodium is a collection of modern collection of primitives, fulfilling the same cryptographic tasks, but based on simpler and more efficient algorithms. | |
| 49 | ||
| 50 | This library provides seamless bindings to [`sodium-native`](https://github.com/sodium-friends/sodium-native), which is the original C implementation of `libsodium` exposed as a Node native module. For the browser we expose [`sodium-javascript`](https://github.com/sodium-friends/sodium-javascript), using the `package.json` [`browser`](https://github.com/defunctzombie/package-browser-field-spec) field, which is supported by most bundlers. | |
| 51 | ||
| 52 | ## API | |
| 53 | ||
| 54 | Please refer to [`sodium-native`](https://github.com/sodium-friends/sodium-native#api) and [`sodium-javascript`](https://github.com/sodium-friends/sodium-javascript#api). Note that the two modules do not yet have feature parity, where `sodium-native` is the more featureful of the two. | |
| 55 | ||
| 56 | ## Compatibilty | |
| 57 | ||
| 58 | <!-- Generated by build-scripts/symbols.js with | |
| 59 | sodium-native@3.2.0 | |
| 60 | sodium-javascript@0.6.0 --> | |
| 61 | | C Library Symbol | `sodium-native` | `sodium-javascript` | | |
| 62 | |:--------------------------------------------------------:|:--------------------:|:--------------------:| | |
| 63 | | `crypto_aead_chacha20poly1305_ietf_ABYTES` | :white_check_mark: | :white_check_mark: | | |
| 64 | | `crypto_aead_chacha20poly1305_ietf_decrypt` | :white_check_mark: | :white_check_mark: | | |
| 65 | | `crypto_aead_chacha20poly1305_ietf_decrypt_detached` | :white_check_mark: | :white_check_mark: | | |
| 66 | | `crypto_aead_chacha20poly1305_ietf_encrypt` | :white_check_mark: | :white_check_mark: | | |
| 67 | | `crypto_aead_chacha20poly1305_ietf_encrypt_detached` | :white_check_mark: | :white_check_mark: | | |
| 68 | | `crypto_aead_chacha20poly1305_ietf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 69 | | `crypto_aead_chacha20poly1305_ietf_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 70 | | `crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 71 | | `crypto_aead_chacha20poly1305_ietf_NPUBBYTES` | :white_check_mark: | :white_check_mark: | | |
| 72 | | `crypto_aead_chacha20poly1305_ietf_NSECBYTES` | :white_check_mark: | :white_check_mark: | | |
| 73 | | `crypto_aead_xchacha20poly1305_ietf_ABYTES` | :white_check_mark: | :small_red_triangle: | | |
| 74 | | `crypto_aead_xchacha20poly1305_ietf_decrypt` | :white_check_mark: | :small_red_triangle: | | |
| 75 | | `crypto_aead_xchacha20poly1305_ietf_decrypt_detached` | :white_check_mark: | :small_red_triangle: | | |
| 76 | | `crypto_aead_xchacha20poly1305_ietf_encrypt` | :white_check_mark: | :small_red_triangle: | | |
| 77 | | `crypto_aead_xchacha20poly1305_ietf_encrypt_detached` | :white_check_mark: | :small_red_triangle: | | |
| 78 | | `crypto_aead_xchacha20poly1305_ietf_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 79 | | `crypto_aead_xchacha20poly1305_ietf_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 80 | | `crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 81 | | `crypto_aead_xchacha20poly1305_ietf_NPUBBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 82 | | `crypto_aead_xchacha20poly1305_ietf_NSECBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 83 | | `crypto_auth` | :white_check_mark: | :small_red_triangle: | | |
| 84 | | `crypto_auth_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 85 | | `crypto_auth_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 86 | | `crypto_auth_verify` | :white_check_mark: | :small_red_triangle: | | |
| 87 | | `crypto_box_BEFORENMBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 88 | | `crypto_box_BOXZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 89 | | `crypto_box_detached` | :white_check_mark: | :small_red_triangle: | | |
| 90 | | `crypto_box_easy` | :white_check_mark: | :small_red_triangle: | | |
| 91 | | `crypto_box_keypair` | :white_check_mark: | :white_check_mark: | | |
| 92 | | `crypto_box_MACBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 93 | | `crypto_box_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 94 | | `crypto_box_open_detached` | :white_check_mark: | :small_red_triangle: | | |
| 95 | | `crypto_box_open_easy` | :white_check_mark: | :small_red_triangle: | | |
| 96 | | `crypto_box_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 97 | | `crypto_box_seal` | :white_check_mark: | :white_check_mark: | | |
| 98 | | `crypto_box_seal_open` | :white_check_mark: | :white_check_mark: | | |
| 99 | | `crypto_box_SEALBYTES` | :white_check_mark: | :white_check_mark: | | |
| 100 | | `crypto_box_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 101 | | `crypto_box_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 102 | | `crypto_box_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 103 | | `crypto_box_ZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 104 | | `crypto_core_ed25519_add` | :white_check_mark: | :small_red_triangle: | | |
| 105 | | `crypto_core_ed25519_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 106 | | `crypto_core_ed25519_from_uniform` | :white_check_mark: | :small_red_triangle: | | |
| 107 | | `crypto_core_ed25519_is_valid_point` | :white_check_mark: | :small_red_triangle: | | |
| 108 | | `crypto_core_ed25519_NONREDUCEDSCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 109 | | `crypto_core_ed25519_scalar_add` | :white_check_mark: | :small_red_triangle: | | |
| 110 | | `crypto_core_ed25519_scalar_complement` | :white_check_mark: | :small_red_triangle: | | |
| 111 | | `crypto_core_ed25519_scalar_invert` | :white_check_mark: | :small_red_triangle: | | |
| 112 | | `crypto_core_ed25519_scalar_negate` | :white_check_mark: | :small_red_triangle: | | |
| 113 | | `crypto_core_ed25519_scalar_random` | :white_check_mark: | :small_red_triangle: | | |
| 114 | | `crypto_core_ed25519_scalar_reduce` | :white_check_mark: | :small_red_triangle: | | |
| 115 | | `crypto_core_ed25519_scalar_sub` | :white_check_mark: | :small_red_triangle: | | |
| 116 | | `crypto_core_ed25519_SCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 117 | | `crypto_core_ed25519_sub` | :white_check_mark: | :small_red_triangle: | | |
| 118 | | `crypto_core_ed25519_UNIFORMBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 119 | | `crypto_generichash` | :white_check_mark: | :white_check_mark: | | |
| 120 | | `crypto_generichash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 121 | | `crypto_generichash_BYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 122 | | `crypto_generichash_BYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 123 | | `crypto_generichash_final` | :white_check_mark: | :small_red_triangle: | | |
| 124 | | `crypto_generichash_init` | :white_check_mark: | :small_red_triangle: | | |
| 125 | | `crypto_generichash_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 126 | | `crypto_generichash_KEYBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 127 | | `crypto_generichash_KEYBYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 128 | | `crypto_generichash_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 129 | | `crypto_generichash_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 130 | | `crypto_generichash_update` | :white_check_mark: | :small_red_triangle: | | |
| 131 | | `crypto_hash` | :white_check_mark: | :white_check_mark: | | |
| 132 | | `crypto_hash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 133 | | `crypto_hash_sha256` | :white_check_mark: | :white_check_mark: | | |
| 134 | | `crypto_hash_sha256_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 135 | | `crypto_hash_sha256_final` | :white_check_mark: | :small_red_triangle: | | |
| 136 | | `crypto_hash_sha256_init` | :white_check_mark: | :small_red_triangle: | | |
| 137 | | `crypto_hash_sha256_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 138 | | `crypto_hash_sha256_update` | :white_check_mark: | :small_red_triangle: | | |
| 139 | | `crypto_hash_sha512` | :white_check_mark: | :white_check_mark: | | |
| 140 | | `crypto_hash_sha512_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 141 | | `crypto_hash_sha512_final` | :white_check_mark: | :small_red_triangle: | | |
| 142 | | `crypto_hash_sha512_init` | :white_check_mark: | :small_red_triangle: | | |
| 143 | | `crypto_hash_sha512_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 144 | | `crypto_hash_sha512_update` | :white_check_mark: | :small_red_triangle: | | |
| 145 | | `crypto_kdf_BYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 146 | | `crypto_kdf_BYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 147 | | `crypto_kdf_CONTEXTBYTES` | :white_check_mark: | :white_check_mark: | | |
| 148 | | `crypto_kdf_derive_from_key` | :white_check_mark: | :white_check_mark: | | |
| 149 | | `crypto_kdf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 150 | | `crypto_kdf_keygen` | :white_check_mark: | :white_check_mark: | | |
| 151 | | `crypto_kx_client_session_keys` | :white_check_mark: | :small_red_triangle: | | |
| 152 | | `crypto_kx_keypair` | :white_check_mark: | :white_check_mark: | | |
| 153 | | `crypto_kx_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 154 | | `crypto_kx_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 155 | | `crypto_kx_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 156 | | `crypto_kx_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 157 | | `crypto_kx_server_session_keys` | :white_check_mark: | :small_red_triangle: | | |
| 158 | | `crypto_kx_SESSIONKEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 159 | | `crypto_onetimeauth` | :white_check_mark: | :white_check_mark: | | |
| 160 | | `crypto_onetimeauth_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 161 | | `crypto_onetimeauth_final` | :white_check_mark: | :small_red_triangle: | | |
| 162 | | `crypto_onetimeauth_init` | :white_check_mark: | :small_red_triangle: | | |
| 163 | | `crypto_onetimeauth_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 164 | | `crypto_onetimeauth_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 165 | | `crypto_onetimeauth_update` | :white_check_mark: | :small_red_triangle: | | |
| 166 | | `crypto_onetimeauth_verify` | :white_check_mark: | :white_check_mark: | | |
| 167 | | `crypto_pwhash` | :white_check_mark: | :small_red_triangle: | | |
| 168 | | `crypto_pwhash_BYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 169 | | `crypto_pwhash_BYTES_MIN` | :white_check_mark: | :small_red_triangle: | | |
| 170 | | `crypto_pwhash_SALTBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 171 | | `crypto_pwhash_scryptsalsa208sha256` | :white_check_mark: | :small_red_triangle: | | |
| 172 | | `crypto_pwhash_scryptsalsa208sha256_BYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 173 | | `crypto_pwhash_scryptsalsa208sha256_BYTES_MIN` | :white_check_mark: | :small_red_triangle: | | |
| 174 | | `crypto_pwhash_scryptsalsa208sha256_SALTBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 175 | | `crypto_pwhash_scryptsalsa208sha256_str` | :white_check_mark: | :small_red_triangle: | | |
| 176 | | `crypto_pwhash_scryptsalsa208sha256_str_needs_rehash` | :white_check_mark: | :small_red_triangle: | | |
| 177 | | `crypto_pwhash_scryptsalsa208sha256_str_verify` | :white_check_mark: | :small_red_triangle: | | |
| 178 | | `crypto_pwhash_scryptsalsa208sha256_STRBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 179 | | `crypto_pwhash_str` | :white_check_mark: | :small_red_triangle: | | |
| 180 | | `crypto_pwhash_str_needs_rehash` | :white_check_mark: | :small_red_triangle: | | |
| 181 | | `crypto_pwhash_str_verify` | :white_check_mark: | :small_red_triangle: | | |
| 182 | | `crypto_pwhash_STRBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 183 | | `crypto_scalarmult` | :white_check_mark: | :white_check_mark: | | |
| 184 | | `crypto_scalarmult_base` | :white_check_mark: | :white_check_mark: | | |
| 185 | | `crypto_scalarmult_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 186 | | `crypto_scalarmult_ed25519` | :white_check_mark: | :small_red_triangle: | | |
| 187 | | `crypto_scalarmult_ed25519_base` | :white_check_mark: | :small_red_triangle: | | |
| 188 | | `crypto_scalarmult_ed25519_base_noclamp` | :white_check_mark: | :small_red_triangle: | | |
| 189 | | `crypto_scalarmult_ed25519_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 190 | | `crypto_scalarmult_ed25519_noclamp` | :white_check_mark: | :small_red_triangle: | | |
| 191 | | `crypto_scalarmult_ed25519_SCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 192 | | `crypto_scalarmult_SCALARBYTES` | :white_check_mark: | :white_check_mark: | | |
| 193 | | `crypto_secretbox` | :small_red_triangle: | :white_check_mark: | | |
| 194 | | `crypto_secretbox_BOXZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 195 | | `crypto_secretbox_detached` | :white_check_mark: | :white_check_mark: | | |
| 196 | | `crypto_secretbox_easy` | :white_check_mark: | :white_check_mark: | | |
| 197 | | `crypto_secretbox_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 198 | | `crypto_secretbox_MACBYTES` | :white_check_mark: | :white_check_mark: | | |
| 199 | | `crypto_secretbox_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 200 | | `crypto_secretbox_open` | :small_red_triangle: | :white_check_mark: | | |
| 201 | | `crypto_secretbox_open_detached` | :white_check_mark: | :white_check_mark: | | |
| 202 | | `crypto_secretbox_open_easy` | :white_check_mark: | :white_check_mark: | | |
| 203 | | `crypto_secretbox_ZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 204 | | `crypto_secretstream_xchacha20poly1305_ABYTES` | :white_check_mark: | :small_red_triangle: | | |
| 205 | | `crypto_secretstream_xchacha20poly1305_HEADERBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 206 | | `crypto_secretstream_xchacha20poly1305_init_pull` | :white_check_mark: | :small_red_triangle: | | |
| 207 | | `crypto_secretstream_xchacha20poly1305_init_push` | :white_check_mark: | :small_red_triangle: | | |
| 208 | | `crypto_secretstream_xchacha20poly1305_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 209 | | `crypto_secretstream_xchacha20poly1305_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 210 | | `crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 211 | | `crypto_secretstream_xchacha20poly1305_pull` | :white_check_mark: | :small_red_triangle: | | |
| 212 | | `crypto_secretstream_xchacha20poly1305_push` | :white_check_mark: | :small_red_triangle: | | |
| 213 | | `crypto_secretstream_xchacha20poly1305_rekey` | :white_check_mark: | :small_red_triangle: | | |
| 214 | | `crypto_secretstream_xchacha20poly1305_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 215 | | `crypto_shorthash` | :white_check_mark: | :white_check_mark: | | |
| 216 | | `crypto_shorthash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 217 | | `crypto_shorthash_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 218 | | `crypto_sign` | :white_check_mark: | :white_check_mark: | | |
| 219 | | `crypto_sign_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 220 | | `crypto_sign_detached` | :white_check_mark: | :white_check_mark: | | |
| 221 | | `crypto_sign_ed25519_pk_to_curve25519` | :white_check_mark: | :small_red_triangle: | | |
| 222 | | `crypto_sign_ed25519_sk_to_curve25519` | :white_check_mark: | :small_red_triangle: | | |
| 223 | | `crypto_sign_ed25519_sk_to_pk` | :white_check_mark: | :small_red_triangle: | | |
| 224 | | `crypto_sign_keypair` | :white_check_mark: | :white_check_mark: | | |
| 225 | | `crypto_sign_open` | :white_check_mark: | :white_check_mark: | | |
| 226 | | `crypto_sign_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 227 | | `crypto_sign_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 228 | | `crypto_sign_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 229 | | `crypto_sign_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 230 | | `crypto_sign_verify_detached` | :white_check_mark: | :white_check_mark: | | |
| 231 | | `crypto_stream` | :white_check_mark: | :white_check_mark: | | |
| 232 | | `crypto_stream_chacha20` | :white_check_mark: | :white_check_mark: | | |
| 233 | | `crypto_stream_chacha20_ietf` | :white_check_mark: | :white_check_mark: | | |
| 234 | | `crypto_stream_chacha20_ietf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 235 | | `crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 236 | | `crypto_stream_chacha20_ietf_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 237 | | `crypto_stream_chacha20_ietf_xor` | :white_check_mark: | :white_check_mark: | | |
| 238 | | `crypto_stream_chacha20_ietf_xor_ic` | :white_check_mark: | :white_check_mark: | | |
| 239 | | `crypto_stream_chacha20_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 240 | | `crypto_stream_chacha20_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 241 | | `crypto_stream_chacha20_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 242 | | `crypto_stream_chacha20_xor` | :white_check_mark: | :white_check_mark: | | |
| 243 | | `crypto_stream_chacha20_xor_ic` | :white_check_mark: | :white_check_mark: | | |
| 244 | | `crypto_stream_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 245 | | `crypto_stream_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 246 | | `crypto_stream_salsa20_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 247 | | `crypto_stream_salsa20_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 248 | | `crypto_stream_salsa20_NONCEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 249 | | `crypto_stream_salsa20_xor` | :white_check_mark: | :small_red_triangle: | | |
| 250 | | `crypto_stream_salsa20_xor_ic` | :white_check_mark: | :small_red_triangle: | | |
| 251 | | `crypto_stream_xchacha20` | :white_check_mark: | :small_red_triangle: | | |
| 252 | | `crypto_stream_xchacha20_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 253 | | `crypto_stream_xchacha20_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 254 | | `crypto_stream_xchacha20_NONCEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 255 | | `crypto_stream_xchacha20_xor` | :white_check_mark: | :small_red_triangle: | | |
| 256 | | `crypto_stream_xchacha20_xor_ic` | :white_check_mark: | :small_red_triangle: | | |
| 257 | | `crypto_stream_xor` | :white_check_mark: | :white_check_mark: | | |
| 258 | | `crypto_verify_16` | :small_red_triangle: | :white_check_mark: | | |
| 259 | | `crypto_verify_32` | :small_red_triangle: | :white_check_mark: | | |
| 260 | | `randombytes_buf` | :white_check_mark: | :white_check_mark: | | |
| 261 | | `randombytes_buf_deterministic` | :white_check_mark: | :small_red_triangle: | | |
| 262 | | `randombytes_random` | :white_check_mark: | :small_red_triangle: | | |
| 263 | | `randombytes_SEEDBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 264 | | `randombytes_uniform` | :white_check_mark: | :small_red_triangle: | | |
| 265 | | `sodium_add` | :white_check_mark: | :small_red_triangle: | | |
| 266 | | `sodium_compare` | :white_check_mark: | :small_red_triangle: | | |
| 267 | | `sodium_free` | :white_check_mark: | :small_red_triangle: | | |
| 268 | | `sodium_increment` | :white_check_mark: | :small_red_triangle: | | |
| 269 | | `sodium_is_zero` | :white_check_mark: | :white_check_mark: | | |
| 270 | | `sodium_malloc` | :white_check_mark: | :white_check_mark: | | |
| 271 | | `sodium_memcmp` | :white_check_mark: | :white_check_mark: | | |
| 272 | | `sodium_memzero` | :white_check_mark: | :white_check_mark: | | |
| 273 | | `sodium_mlock` | :white_check_mark: | :small_red_triangle: | | |
| 274 | | `sodium_mprotect_noaccess` | :white_check_mark: | :small_red_triangle: | | |
| 275 | | `sodium_mprotect_readonly` | :white_check_mark: | :small_red_triangle: | | |
| 276 | | `sodium_mprotect_readwrite` | :white_check_mark: | :small_red_triangle: | | |
| 277 | | `sodium_munlock` | :white_check_mark: | :small_red_triangle: | | |
| 278 | | `sodium_pad` | :white_check_mark: | :small_red_triangle: | | |
| 279 | | `sodium_sub` | :white_check_mark: | :small_red_triangle: | | |
| 280 | | `sodium_unpad` | :white_check_mark: | :small_red_triangle: | | |
| 281 | ||
| 282 | ### Missing altogether (Ctrl + F friendly) | |
| 283 | ||
| 284 | `crypto_aead_aes256gcm_ABYTES`, `crypto_aead_aes256gcm_beforenm`, `crypto_aead_aes256gcm_decrypt`, `crypto_aead_aes256gcm_decrypt_afternm`, `crypto_aead_aes256gcm_decrypt_detached`, `crypto_aead_aes256gcm_decrypt_detached_afternm`, `crypto_aead_aes256gcm_encrypt`, `crypto_aead_aes256gcm_encrypt_afternm`, `crypto_aead_aes256gcm_encrypt_detached`, `crypto_aead_aes256gcm_encrypt_detached_afternm`, `crypto_aead_aes256gcm_is_available`, `crypto_aead_aes256gcm_KEYBYTES`, `crypto_aead_aes256gcm_keygen`, `crypto_aead_aes256gcm_MESSAGEBYTES_MAX`, `crypto_aead_aes256gcm_NPUBBYTES`, `crypto_aead_aes256gcm_NSECBYTES`, `crypto_aead_aes256gcm_STATEBYTES`, `crypto_aead_chacha20poly1305_ABYTES`, `crypto_aead_chacha20poly1305_decrypt`, `crypto_aead_chacha20poly1305_decrypt_detached`, `crypto_aead_chacha20poly1305_encrypt`, `crypto_aead_chacha20poly1305_encrypt_detached`, `crypto_aead_chacha20poly1305_KEYBYTES`, `crypto_aead_chacha20poly1305_keygen`, `crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX`, `crypto_aead_chacha20poly1305_NPUBBYTES`, `crypto_aead_chacha20poly1305_NSECBYTES`, `crypto_auth_hmacsha256`, `crypto_auth_hmacsha256_BYTES`, `crypto_auth_hmacsha256_final`, `crypto_auth_hmacsha256_init`, `crypto_auth_hmacsha256_KEYBYTES`, `crypto_auth_hmacsha256_keygen`, `crypto_auth_hmacsha256_STATEBYTES`, `crypto_auth_hmacsha256_update`, `crypto_auth_hmacsha256_verify`, `crypto_auth_hmacsha512`, `crypto_auth_hmacsha512256`, `crypto_auth_hmacsha512256_BYTES`, `crypto_auth_hmacsha512256_final`, `crypto_auth_hmacsha512256_init`, `crypto_auth_hmacsha512256_KEYBYTES`, `crypto_auth_hmacsha512256_keygen`, `crypto_auth_hmacsha512256_STATEBYTES`, `crypto_auth_hmacsha512256_update`, `crypto_auth_hmacsha512256_verify`, `crypto_auth_hmacsha512_BYTES`, `crypto_auth_hmacsha512_final`, `crypto_auth_hmacsha512_init`, `crypto_auth_hmacsha512_KEYBYTES`, `crypto_auth_hmacsha512_keygen`, `crypto_auth_hmacsha512_STATEBYTES`, `crypto_auth_hmacsha512_update`, `crypto_auth_hmacsha512_verify`, `crypto_auth_keygen`, `crypto_auth_primitive`, `crypto_box`, `crypto_box_afternm`, `crypto_box_beforenm`, `crypto_box_curve25519xchacha20poly1305_beforenm`, `crypto_box_curve25519xchacha20poly1305_BEFORENMBYTES`, `crypto_box_curve25519xchacha20poly1305_detached`, `crypto_box_curve25519xchacha20poly1305_detached_afternm`, `crypto_box_curve25519xchacha20poly1305_easy`, `crypto_box_curve25519xchacha20poly1305_easy_afternm`, `crypto_box_curve25519xchacha20poly1305_keypair`, `crypto_box_curve25519xchacha20poly1305_MACBYTES`, `crypto_box_curve25519xchacha20poly1305_MESSAGEBYTES_MAX`, `crypto_box_curve25519xchacha20poly1305_NONCEBYTES`, `crypto_box_curve25519xchacha20poly1305_open_detached`, `crypto_box_curve25519xchacha20poly1305_open_detached_afternm`, `crypto_box_curve25519xchacha20poly1305_open_easy`, `crypto_box_curve25519xchacha20poly1305_open_easy_afternm`, `crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES`, `crypto_box_curve25519xchacha20poly1305_seal`, `crypto_box_curve25519xchacha20poly1305_seal_open`, `crypto_box_curve25519xchacha20poly1305_SEALBYTES`, `crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES`, `crypto_box_curve25519xchacha20poly1305_seed_keypair`, `crypto_box_curve25519xchacha20poly1305_SEEDBYTES`, `crypto_box_curve25519xsalsa20poly1305`, `crypto_box_curve25519xsalsa20poly1305_afternm`, `crypto_box_curve25519xsalsa20poly1305_beforenm`, `crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES`, `crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES`, `crypto_box_curve25519xsalsa20poly1305_keypair`, `crypto_box_curve25519xsalsa20poly1305_MACBYTES`, `crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX`, `crypto_box_curve25519xsalsa20poly1305_NONCEBYTES`, `crypto_box_curve25519xsalsa20poly1305_open`, `crypto_box_curve25519xsalsa20poly1305_open_afternm`, `crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES`, `crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES`, `crypto_box_curve25519xsalsa20poly1305_seed_keypair`, `crypto_box_curve25519xsalsa20poly1305_SEEDBYTES`, `crypto_box_curve25519xsalsa20poly1305_ZEROBYTES`, `crypto_box_detached_afternm`, `crypto_box_easy_afternm`, `crypto_box_MESSAGEBYTES_MAX`, `crypto_box_open`, `crypto_box_open_afternm`, `crypto_box_open_detached_afternm`, `crypto_box_open_easy_afternm`, `crypto_box_primitive`, `crypto_core_ed25519_from_hash`, `crypto_core_ed25519_HASHBYTES`, `crypto_core_ed25519_random`, `crypto_core_ed25519_scalar_mul`, `crypto_core_hchacha20`, `crypto_core_hchacha20_CONSTBYTES`, `crypto_core_hchacha20_INPUTBYTES`, `crypto_core_hchacha20_KEYBYTES`, `crypto_core_hchacha20_OUTPUTBYTES`, `crypto_core_hsalsa20`, `crypto_core_hsalsa20_CONSTBYTES`, `crypto_core_hsalsa20_INPUTBYTES`, `crypto_core_hsalsa20_KEYBYTES`, `crypto_core_hsalsa20_OUTPUTBYTES`, `crypto_core_ristretto255_add`, `crypto_core_ristretto255_BYTES`, `crypto_core_ristretto255_from_hash`, `crypto_core_ristretto255_HASHBYTES`, `crypto_core_ristretto255_is_valid_point`, `crypto_core_ristretto255_NONREDUCEDSCALARBYTES`, `crypto_core_ristretto255_random`, `crypto_core_ristretto255_scalar_add`, `crypto_core_ristretto255_scalar_complement`, `crypto_core_ristretto255_scalar_invert`, `crypto_core_ristretto255_scalar_mul`, `crypto_core_ristretto255_scalar_negate`, `crypto_core_ristretto255_scalar_random`, `crypto_core_ristretto255_scalar_reduce`, `crypto_core_ristretto255_scalar_sub`, `crypto_core_ristretto255_SCALARBYTES`, `crypto_core_ristretto255_sub`, `crypto_core_salsa20`, `crypto_core_salsa2012`, `crypto_core_salsa2012_CONSTBYTES`, `crypto_core_salsa2012_INPUTBYTES`, `crypto_core_salsa2012_KEYBYTES`, `crypto_core_salsa2012_OUTPUTBYTES`, `crypto_core_salsa208`, `crypto_core_salsa208_CONSTBYTES`, `crypto_core_salsa208_INPUTBYTES`, `crypto_core_salsa208_KEYBYTES`, `crypto_core_salsa208_OUTPUTBYTES`, `crypto_core_salsa20_CONSTBYTES`, `crypto_core_salsa20_INPUTBYTES`, `crypto_core_salsa20_KEYBYTES`, `crypto_core_salsa20_OUTPUTBYTES`, `crypto_generichash_blake2b`, `crypto_generichash_blake2b_BYTES`, `crypto_generichash_blake2b_BYTES_MAX`, `crypto_generichash_blake2b_BYTES_MIN`, `crypto_generichash_blake2b_final`, `crypto_generichash_blake2b_init`, `crypto_generichash_blake2b_init_salt_personal`, `crypto_generichash_blake2b_KEYBYTES`, `crypto_generichash_blake2b_KEYBYTES_MAX`, `crypto_generichash_blake2b_KEYBYTES_MIN`, `crypto_generichash_blake2b_keygen`, `crypto_generichash_blake2b_PERSONALBYTES`, `crypto_generichash_blake2b_salt_personal`, `crypto_generichash_blake2b_SALTBYTES`, `crypto_generichash_blake2b_STATEBYTES`, `crypto_generichash_blake2b_update`, `crypto_generichash_primitive`, `crypto_hash_primitive`, `crypto_kdf_blake2b_BYTES_MAX`, `crypto_kdf_blake2b_BYTES_MIN`, `crypto_kdf_blake2b_CONTEXTBYTES`, `crypto_kdf_blake2b_derive_from_key`, `crypto_kdf_blake2b_KEYBYTES`, `crypto_kdf_primitive`, `crypto_kx_primitive`, `crypto_onetimeauth_keygen`, `crypto_onetimeauth_poly1305`, `crypto_onetimeauth_poly1305_BYTES`, `crypto_onetimeauth_poly1305_final`, `crypto_onetimeauth_poly1305_init`, `crypto_onetimeauth_poly1305_KEYBYTES`, `crypto_onetimeauth_poly1305_keygen`, `crypto_onetimeauth_poly1305_STATEBYTES`, `crypto_onetimeauth_poly1305_update`, `crypto_onetimeauth_poly1305_verify`, `crypto_onetimeauth_primitive`, `crypto_pwhash_alg_argon2i13`, `crypto_pwhash_alg_argon2id13`, `crypto_pwhash_alg_default`, `crypto_pwhash_argon2i`, `crypto_pwhash_argon2i_alg_argon2i13`, `crypto_pwhash_argon2i_BYTES_MAX`, `crypto_pwhash_argon2i_BYTES_MIN`, `crypto_pwhash_argon2i_memlimit_interactive`, `crypto_pwhash_argon2i_memlimit_max`, `crypto_pwhash_argon2i_memlimit_min`, `crypto_pwhash_argon2i_memlimit_moderate`, `crypto_pwhash_argon2i_memlimit_sensitive`, `crypto_pwhash_argon2i_opslimit_interactive`, `crypto_pwhash_argon2i_opslimit_max`, `crypto_pwhash_argon2i_opslimit_min`, `crypto_pwhash_argon2i_opslimit_moderate`, `crypto_pwhash_argon2i_opslimit_sensitive`, `crypto_pwhash_argon2i_passwd_max`, `crypto_pwhash_argon2i_passwd_min`, `crypto_pwhash_argon2i_SALTBYTES`, `crypto_pwhash_argon2i_str`, `crypto_pwhash_argon2i_str_needs_rehash`, `crypto_pwhash_argon2i_str_verify`, `crypto_pwhash_argon2i_STRBYTES`, `crypto_pwhash_argon2i_strprefix`, `crypto_pwhash_argon2id`, `crypto_pwhash_argon2id_alg_argon2id13`, `crypto_pwhash_argon2id_BYTES_MAX`, `crypto_pwhash_argon2id_BYTES_MIN`, `crypto_pwhash_argon2id_memlimit_interactive`, `crypto_pwhash_argon2id_memlimit_max`, `crypto_pwhash_argon2id_memlimit_min`, `crypto_pwhash_argon2id_memlimit_moderate`, `crypto_pwhash_argon2id_memlimit_sensitive`, `crypto_pwhash_argon2id_opslimit_interactive`, `crypto_pwhash_argon2id_opslimit_max`, `crypto_pwhash_argon2id_opslimit_min`, `crypto_pwhash_argon2id_opslimit_moderate`, `crypto_pwhash_argon2id_opslimit_sensitive`, `crypto_pwhash_argon2id_passwd_max`, `crypto_pwhash_argon2id_passwd_min`, `crypto_pwhash_argon2id_SALTBYTES`, `crypto_pwhash_argon2id_str`, `crypto_pwhash_argon2id_str_needs_rehash`, `crypto_pwhash_argon2id_str_verify`, `crypto_pwhash_argon2id_STRBYTES`, `crypto_pwhash_argon2id_strprefix`, `crypto_pwhash_memlimit_interactive`, `crypto_pwhash_memlimit_max`, `crypto_pwhash_memlimit_min`, `crypto_pwhash_memlimit_moderate`, `crypto_pwhash_memlimit_sensitive`, `crypto_pwhash_opslimit_interactive`, `crypto_pwhash_opslimit_max`, `crypto_pwhash_opslimit_min`, `crypto_pwhash_opslimit_moderate`, `crypto_pwhash_opslimit_sensitive`, `crypto_pwhash_passwd_max`, `crypto_pwhash_passwd_min`, `crypto_pwhash_primitive`, `crypto_pwhash_scryptsalsa208sha256_ll`, `crypto_pwhash_scryptsalsa208sha256_memlimit_interactive`, `crypto_pwhash_scryptsalsa208sha256_memlimit_max`, `crypto_pwhash_scryptsalsa208sha256_memlimit_min`, `crypto_pwhash_scryptsalsa208sha256_memlimit_sensitive`, `crypto_pwhash_scryptsalsa208sha256_opslimit_interactive`, `crypto_pwhash_scryptsalsa208sha256_opslimit_max`, `crypto_pwhash_scryptsalsa208sha256_opslimit_min`, `crypto_pwhash_scryptsalsa208sha256_opslimit_sensitive`, `crypto_pwhash_scryptsalsa208sha256_passwd_max`, `crypto_pwhash_scryptsalsa208sha256_passwd_min`, `crypto_pwhash_scryptsalsa208sha256_strprefix`, `crypto_pwhash_str_alg`, `crypto_pwhash_strprefix`, `crypto_scalarmult_curve25519`, `crypto_scalarmult_curve25519_base`, `crypto_scalarmult_curve25519_BYTES`, `crypto_scalarmult_curve25519_SCALARBYTES`, `crypto_scalarmult_primitive`, `crypto_scalarmult_ristretto255`, `crypto_scalarmult_ristretto255_base`, `crypto_scalarmult_ristretto255_BYTES`, `crypto_scalarmult_ristretto255_SCALARBYTES`, `crypto_secretbox_keygen`, `crypto_secretbox_MESSAGEBYTES_MAX`, `crypto_secretbox_primitive`, `crypto_secretbox_xchacha20poly1305_detached`, `crypto_secretbox_xchacha20poly1305_easy`, `crypto_secretbox_xchacha20poly1305_KEYBYTES`, `crypto_secretbox_xchacha20poly1305_MACBYTES`, `crypto_secretbox_xchacha20poly1305_MESSAGEBYTES_MAX`, `crypto_secretbox_xchacha20poly1305_NONCEBYTES`, `crypto_secretbox_xchacha20poly1305_open_detached`, `crypto_secretbox_xchacha20poly1305_open_easy`, `crypto_secretbox_xsalsa20poly1305`, `crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES`, `crypto_secretbox_xsalsa20poly1305_KEYBYTES`, `crypto_secretbox_xsalsa20poly1305_keygen`, `crypto_secretbox_xsalsa20poly1305_MACBYTES`, `crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX`, `crypto_secretbox_xsalsa20poly1305_NONCEBYTES`, `crypto_secretbox_xsalsa20poly1305_open`, `crypto_secretbox_xsalsa20poly1305_ZEROBYTES`, `crypto_secretstream_xchacha20poly1305_tag_final`, `crypto_secretstream_xchacha20poly1305_tag_message`, `crypto_secretstream_xchacha20poly1305_tag_push`, `crypto_secretstream_xchacha20poly1305_tag_rekey`, `crypto_shorthash_keygen`, `crypto_shorthash_primitive`, `crypto_shorthash_siphash24`, `crypto_shorthash_siphash24_BYTES`, `crypto_shorthash_siphash24_KEYBYTES`, `crypto_shorthash_siphashx24`, `crypto_shorthash_siphashx24_BYTES`, `crypto_shorthash_siphashx24_KEYBYTES`, `crypto_sign_ed25519`, `crypto_sign_ed25519_BYTES`, `crypto_sign_ed25519_detached`, `crypto_sign_ed25519_keypair`, `crypto_sign_ed25519_MESSAGEBYTES_MAX`, `crypto_sign_ed25519_open`, `crypto_sign_ed25519_PUBLICKEYBYTES`, `crypto_sign_ed25519_SECRETKEYBYTES`, `crypto_sign_ed25519_seed_keypair`, `crypto_sign_ed25519_SEEDBYTES`, `crypto_sign_ed25519_sk_to_seed`, `crypto_sign_ed25519_verify_detached`, `crypto_sign_ed25519ph_final_create`, `crypto_sign_ed25519ph_final_verify`, `crypto_sign_ed25519ph_init`, `crypto_sign_ed25519ph_STATEBYTES`, `crypto_sign_ed25519ph_update`, `crypto_sign_edwards25519sha512batch`, `crypto_sign_edwards25519sha512batch_keypair`, `crypto_sign_edwards25519sha512batch_open`, `crypto_sign_final_create`, `crypto_sign_final_verify`, `crypto_sign_init`, `crypto_sign_MESSAGEBYTES_MAX`, `crypto_sign_primitive`, `crypto_sign_STATEBYTES`, `crypto_sign_update`, `crypto_stream_chacha20_ietf_keygen`, `crypto_stream_chacha20_keygen`, `crypto_stream_keygen`, `crypto_stream_MESSAGEBYTES_MAX`, `crypto_stream_primitive`, `crypto_stream_salsa20`, `crypto_stream_salsa2012`, `crypto_stream_salsa2012_KEYBYTES`, `crypto_stream_salsa2012_keygen`, `crypto_stream_salsa2012_MESSAGEBYTES_MAX`, `crypto_stream_salsa2012_NONCEBYTES`, `crypto_stream_salsa2012_xor`, `crypto_stream_salsa208`, `crypto_stream_salsa208_KEYBYTES`, `crypto_stream_salsa208_keygen`, `crypto_stream_salsa208_MESSAGEBYTES_MAX`, `crypto_stream_salsa208_NONCEBYTES`, `crypto_stream_salsa208_xor`, `crypto_stream_salsa20_keygen`, `crypto_stream_xchacha20_keygen`, `crypto_stream_xsalsa20`, `crypto_stream_xsalsa20_KEYBYTES`, `crypto_stream_xsalsa20_keygen`, `crypto_stream_xsalsa20_MESSAGEBYTES_MAX`, `crypto_stream_xsalsa20_NONCEBYTES`, `crypto_stream_xsalsa20_xor`, `crypto_stream_xsalsa20_xor_ic`, `crypto_verify_16_BYTES`, `crypto_verify_32_BYTES`, `crypto_verify_64`, `crypto_verify_64_BYTES`, `randombytes`, `randombytes_close`, `randombytes_implementation_name`, `randombytes_internal_implementation`, `randombytes_set_implementation`, `randombytes_stir`, `randombytes_sysrandom_implementation`, `sodium_allocarray`, `sodium_base642bin`, `sodium_base64_encoded_len`, `sodium_bin2base64`, `sodium_bin2hex`, `sodium_hex2bin`, `sodium_init`, `sodium_library_minimal`, `sodium_library_version_major`, `sodium_library_version_minor`, `sodium_misuse`, `sodium_runtime_has_aesni`, `sodium_runtime_has_avx`, `sodium_runtime_has_avx2`, `sodium_runtime_has_avx512f`, `sodium_runtime_has_neon`, `sodium_runtime_has_pclmul`, `sodium_runtime_has_rdrand`, `sodium_runtime_has_sse2`, `sodium_runtime_has_sse3`, `sodium_runtime_has_sse41`, `sodium_runtime_has_ssse3`, `sodium_set_misuse_handler`, `sodium_stackzero`, `sodium_version_string` | |
| 285 | ||
| 286 | ## License | |
| 287 | ||
| 288 | [MIT](LICENSE.md) |
| … | … | @@ -0,0 +1,33 @@ |
| 1 | #!/usr/bin/env node | |
| 2 | ||
| 3 | const fs = require('fs') | |
| 4 | const path = require('path') | |
| 5 | const root = path.dirname(require.resolve('sodium-javascript/package.json')) | |
| 6 | ||
| 7 | const tmp = "module.exports = require('sodium-native')\n" | |
| 8 | ||
| 9 | function recurse (dir) { | |
| 10 | const ls = fs.readdirSync(dir) | |
| 11 | const subdir = path.relative(root, dir) | |
| 12 | if (subdir) fs.mkdirSync(subdir, { recursive: true }) | |
| 13 | ||
| 14 | for (const file of ls) { | |
| 15 | if (file === 'internal') recurse(path.join(dir, file)) | |
| 16 | if (!/\.js$/i.test(file)) continue | |
| 17 | if (file === 'example.js') continue | |
| 18 | if (file === 'test.js') continue | |
| 19 | ||
| 20 | fs.writeFileSync(path.join(__dirname, '..', subdir, file), tmp) | |
| 21 | } | |
| 22 | } | |
| 23 | ||
| 24 | recurse(root) | |
| 25 | ||
| 26 | const pkg = require('sodium-javascript/package.json') | |
| 27 | const myPkg = require('../package.json') | |
| 28 | ||
| 29 | for (const key of Object.keys(pkg.dependencies)) { | |
| 30 | myPkg.dependencies[key] = pkg.dependencies[key] | |
| 31 | } | |
| 32 | ||
| 33 | fs.writeFileSync(path.join(__dirname, '../package.json'), JSON.stringify(myPkg, null, 2) + '\n') |
| … | … | @@ -0,0 +1,32 @@ |
| 1 | var exec = require('child_process').execSync | |
| 2 | var path = require('path') | |
| 3 | ||
| 4 | var symbols = exec(`nm -gU "${path.join(require.resolve('sodium-native'), '../prebuilds/darwin-x64/libsodium.dylib')}"`).toString().trim().split('\n') | |
| 5 | var native = new Set(Object.keys(require('sodium-native')).sort()) | |
| 6 | var js = new Set(Object.keys(require('sodium-javascript')).sort()) | |
| 7 | ||
| 8 | symbols = symbols.map(l => l.split(' ').pop().replace(/^_/, '').replace(/_[^_]*bytes.*/, (s) => s.toUpperCase())) | |
| 9 | ||
| 10 | var missing = [] | |
| 11 | ||
| 12 | console.log(` | |
| 13 | <!-- Generated by build-scripts/symbols.js with | |
| 14 | sodium-native@${require('sodium-native/package.json').version} | |
| 15 | sodium-javascript@${require('sodium-javascript/package.json').version} -->\ | |
| 16 | `) | |
| 17 | ||
| 18 | const max = symbols.filter(s => native.has(s) || js.has(s)).map(s => s.length).reduce((a, b) => Math.max(a, b)) | |
| 19 | const lines = ':-' + ''.padEnd(max, '--') + '-:' | |
| 20 | const center = s => s.padStart(Math.floor((max + s.length) / 2), ' ').padEnd(max, ' ') | |
| 21 | ||
| 22 | console.log(`| ${center('C Library Symbol')} | \`sodium-native\` | \`sodium-javascript\` |`) | |
| 23 | console.log(`|${lines}|:--------------------:|:--------------------:|`) | |
| 24 | ||
| 25 | for (var i = 0; i < symbols.length; i++) { | |
| 26 | var s = symbols[i] | |
| 27 | if (!(native.has(s) || js.has(s))) missing.push(s) | |
| 28 | else console.log(`| \`${(s + '`').padEnd(max + 1, ' ')} | ${native.has(s) ? ' :white_check_mark: ' : ':small_red_triangle:'} | ${js.has(s) ? ' :white_check_mark: ' : ':small_red_triangle:'} |`) | |
| 29 | } | |
| 30 | ||
| 31 | console.log('\n### Missing altogether (Ctrl + F friendly)\n') | |
| 32 | console.log(missing.map(s => '`' + s + '`').join(', ')) |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | const fs = require('fs') | |
| 2 | const path = require('path') | |
| 3 | const { Transform, PassThrough } = require('stream') | |
| 4 | ||
| 5 | module.exports = function (file, opts) { | |
| 6 | const basedir = path.resolve(__dirname, '..') | |
| 7 | const relname = path.relative(basedir, file) | |
| 8 | ||
| 9 | if (opts._flags.browserField === false) { | |
| 10 | return new PassThrough() | |
| 11 | } | |
| 12 | ||
| 13 | return new Transform({ | |
| 14 | transform (data, enc, cb) { | |
| 15 | cb() | |
| 16 | }, | |
| 17 | flush (cb) { | |
| 18 | const filename = require.resolve('sodium-javascript/' + relname) | |
| 19 | fs.readFile(filename, (err, buf) => { | |
| 20 | if (err) return cb(err) | |
| 21 | this.push(buf) | |
| 22 | cb(null) | |
| 23 | }) | |
| 24 | } | |
| 25 | }) | |
| 26 | } |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1,49 @@ |
| 1 | { | |
| 2 | "name": "sodium-universal", | |
| 3 | "version": "5.0.1", | |
| 4 | "description": "Universal wrapper for sodium-javascript and sodium-native working in Node.js and the Browser", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "sodium-native": "^5.0.1" | |
| 8 | }, | |
| 9 | "peerDependencies": { | |
| 10 | "sodium-javascript": "~0.8.0" | |
| 11 | }, | |
| 12 | "peerDependenciesMeta": { | |
| 13 | "sodium-javascript": { | |
| 14 | "optional": true | |
| 15 | } | |
| 16 | }, | |
| 17 | "scripts": { | |
| 18 | "prepublish": "./build-scripts/generate.js" | |
| 19 | }, | |
| 20 | "browser": { | |
| 21 | "sodium-native": "sodium-javascript" | |
| 22 | }, | |
| 23 | "browserify": { | |
| 24 | "transform": [ | |
| 25 | "./build-scripts/transform.js" | |
| 26 | ] | |
| 27 | }, | |
| 28 | "repository": { | |
| 29 | "type": "git", | |
| 30 | "url": "git+https://github.com/holepunchto/sodium-universal.git" | |
| 31 | }, | |
| 32 | "keywords": [ | |
| 33 | "libsodium", | |
| 34 | "sodium", | |
| 35 | "sodium-native", | |
| 36 | "sodium-javascript", | |
| 37 | "browserify" | |
| 38 | ], | |
| 39 | "contributors": [ | |
| 40 | "Emil Bay <github@tixz.dk> (http://bayes.dk)", | |
| 41 | "Mathias Buus <mathiasbuus@gmail.com> (https://mafinto.sh)", | |
| 42 | "Christophe Diederichs <chm-diederichs@hyperdivision.dk>" | |
| 43 | ], | |
| 44 | "license": "MIT", | |
| 45 | "bugs": { | |
| 46 | "url": "https://github.com/holepunchto/sodium-universal/issues" | |
| 47 | }, | |
| 48 | "homepage": "https://github.com/holepunchto/sodium-universal#readme" | |
| 49 | } |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1,42 @@ |
| 1 | { | |
| 2 | "name": "@hyperswarm/secret-stream", | |
| 3 | "version": "6.9.1", | |
| 4 | "description": "Secret stream backed by Noise and libsodium's secretstream", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js", | |
| 8 | "lib/**.js" | |
| 9 | ], | |
| 10 | "dependencies": { | |
| 11 | "b4a": "^1.1.0", | |
| 12 | "hypercore-crypto": "^3.3.1", | |
| 13 | "noise-curve-ed": "^2.0.1", | |
| 14 | "noise-handshake": "^4.0.0", | |
| 15 | "sodium-secretstream": "^1.1.0", | |
| 16 | "sodium-universal": "^5.0.0", | |
| 17 | "streamx": "^2.14.0", | |
| 18 | "timeout-refresh": "^2.0.0", | |
| 19 | "unslab": "^1.3.0" | |
| 20 | }, | |
| 21 | "devDependencies": { | |
| 22 | "brittle": "^3.3.0", | |
| 23 | "prettier": "^3.6.2", | |
| 24 | "prettier-config-holepunch": "^2.0.0", | |
| 25 | "udx-native": "^1.13.2" | |
| 26 | }, | |
| 27 | "scripts": { | |
| 28 | "format": "prettier --write .", | |
| 29 | "lint": "prettier --check .", | |
| 30 | "test": "brittle test.js" | |
| 31 | }, | |
| 32 | "repository": { | |
| 33 | "type": "git", | |
| 34 | "url": "https://github.com/holepunchto/hyperswarm-secret-stream.git" | |
| 35 | }, | |
| 36 | "author": "Mathias Buus (@mafintosh)", | |
| 37 | "license": "Apache-2.0", | |
| 38 | "bugs": { | |
| 39 | "url": "https://github.com/holepunchto/hyperswarm-secret-stream/issues" | |
| 40 | }, | |
| 41 | "homepage": "https://github.com/holepunchto/hyperswarm-secret-stream" | |
| 42 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,153 @@ |
| 1 | # Buffer for Array | |
| 2 | ||
| 3 | Buffer for Array (B4A) provides a set of functions for bridging the gap between the Node.js `Buffer` class and the `Uint8Array` class. A browser compatibility layer is also included, making it possible to use B4A in both Node.js and browsers without having to worry about whether you're dealing with buffers or typed arrays. | |
| 4 | ||
| 5 | ## Installation | |
| 6 | ||
| 7 | ```sh | |
| 8 | npm i b4a | |
| 9 | ``` | |
| 10 | ||
| 11 | ### React Native | |
| 12 | ||
| 13 | When imported from React Native `b4a` will make use of `react-native-b4a` for optimised buffer operations when available. All you need to do is install it: | |
| 14 | ||
| 15 | ```sh | |
| 16 | npm i react-native-b4a | |
| 17 | ``` | |
| 18 | ||
| 19 | ## API | |
| 20 | ||
| 21 | #### `b4a.isBuffer(value)` | |
| 22 | ||
| 23 | See https://nodejs.org/api/buffer.html#static-method-bufferisbufferobj | |
| 24 | ||
| 25 | This will also return `true` when passed a `Uint8Array`. | |
| 26 | ||
| 27 | #### `b4a.isEncoding(encoding)` | |
| 28 | ||
| 29 | See https://nodejs.org/api/buffer.html#static-method-bufferisencodingencoding | |
| 30 | ||
| 31 | #### `b4a.alloc(size[, fill[, encoding]])` | |
| 32 | ||
| 33 | See https://nodejs.org/api/buffer.html#static-method-bufferallocsize-fill-encoding | |
| 34 | ||
| 35 | #### `b4a.allocUnsafe(size)` | |
| 36 | ||
| 37 | See https://nodejs.org/api/buffer.html#static-method-bufferallocunsafesize | |
| 38 | ||
| 39 | #### `b4a.allocUnsafeSlow(size)` | |
| 40 | ||
| 41 | See https://nodejs.org/api/buffer.html#static-method-bufferallocunsafeslowsize | |
| 42 | ||
| 43 | #### `b4a.byteLength(string)` | |
| 44 | ||
| 45 | See https://nodejs.org/api/buffer.html#static-method-bufferbytelengthstring-encoding | |
| 46 | ||
| 47 | #### `b4a.compare(buf1, buf2)` | |
| 48 | ||
| 49 | See https://nodejs.org/api/buffer.html#static-method-buffercomparebuf1-buf2 | |
| 50 | ||
| 51 | #### `b4a.concat(buffers[, totalLength])` | |
| 52 | ||
| 53 | See https://nodejs.org/api/buffer.html#static-method-bufferconcatlist-totallength | |
| 54 | ||
| 55 | #### `b4a.copy(source, target[, targetStart[, sourceStart[, sourceEnd]]])` | |
| 56 | ||
| 57 | See https://nodejs.org/api/buffer.html#bufcopytarget-targetstart-sourcestart-sourceend | |
| 58 | ||
| 59 | #### `b4a.equals(buf1, buf2)` | |
| 60 | ||
| 61 | See https://nodejs.org/api/buffer.html#bufequalsotherbuffer | |
| 62 | ||
| 63 | #### `b4a.fill(buffer, value[, offset[, end]][, encoding])` | |
| 64 | ||
| 65 | See https://nodejs.org/api/buffer.html#buffillvalue-offset-end-encoding | |
| 66 | ||
| 67 | #### `b4a.from(array)` | |
| 68 | ||
| 69 | See https://nodejs.org/api/buffer.html#static-method-bufferfromarray | |
| 70 | ||
| 71 | #### `b4a.from(arrayBuffer[, byteOffset[, length]])` | |
| 72 | ||
| 73 | See https://nodejs.org/api/buffer.html#static-method-bufferfromarraybuffer-byteoffset-length | |
| 74 | ||
| 75 | #### `b4a.from(buffer)` | |
| 76 | ||
| 77 | See https://nodejs.org/api/buffer.html#static-method-bufferfrombuffer | |
| 78 | ||
| 79 | #### `b4a.from(string[, encoding])` | |
| 80 | ||
| 81 | See https://nodejs.org/api/buffer.html#static-method-bufferfromstring-encoding | |
| 82 | ||
| 83 | #### `b4a.includes(buffer, value[, byteOffset][, encoding])` | |
| 84 | ||
| 85 | See https://nodejs.org/api/buffer.html#bufincludesvalue-byteoffset-encoding | |
| 86 | ||
| 87 | #### `b4a.indexOf(buffer, value[, byteOffset][, encoding])` | |
| 88 | ||
| 89 | See https://nodejs.org/api/buffer.html#bufindexofvalue-byteoffset-encoding | |
| 90 | ||
| 91 | #### `b4a.lastIndexOf(buffer, value[, byteOffset][, encoding])` | |
| 92 | ||
| 93 | See https://nodejs.org/api/buffer.html#buflastindexofvalue-byteoffset-encoding | |
| 94 | ||
| 95 | #### `b4a.swap16(buffer)` | |
| 96 | ||
| 97 | See https://nodejs.org/api/buffer.html#bufswap16 | |
| 98 | ||
| 99 | #### `b4a.swap32(buffer)` | |
| 100 | ||
| 101 | See https://nodejs.org/api/buffer.html#bufswap32 | |
| 102 | ||
| 103 | #### `b4a.swap64(buffer)` | |
| 104 | ||
| 105 | See https://nodejs.org/api/buffer.html#bufswap64 | |
| 106 | ||
| 107 | #### `b4a.toBuffer(buffer)` | |
| 108 | ||
| 109 | Convert a buffer to its canonical representation. In Node.js, the canonical representation is a `Buffer`. In the browser, the canonical representation is a `Uint8Array`. | |
| 110 | ||
| 111 | #### `b4a.toString(buffer, [encoding[, start[, end]]])` | |
| 112 | ||
| 113 | See https://nodejs.org/api/buffer.html#buftostringencoding-start-end | |
| 114 | ||
| 115 | #### `b4a.write(buffer, string[, offset[, length]][, encoding])` | |
| 116 | ||
| 117 | See https://nodejs.org/api/buffer.html#bufwritestring-offset-length-encoding | |
| 118 | ||
| 119 | #### `b4a.writeDoubleLE(buffer, value[, offset])` | |
| 120 | ||
| 121 | See https://nodejs.org/api/buffer.html#bufwritedoublelevalue-offset | |
| 122 | ||
| 123 | #### `b4a.writeFloatLE(buffer, value[, offset])` | |
| 124 | ||
| 125 | See https://nodejs.org/api/buffer.html#bufwritefloatlevalue-offset | |
| 126 | ||
| 127 | #### `b4a.writeUInt32LE(buffer, value[, offset])` | |
| 128 | ||
| 129 | https://nodejs.org/api/buffer.html#bufwriteuint32levalue-offset | |
| 130 | ||
| 131 | #### `b4a.writeInt32LE(buffer, value[, offset])` | |
| 132 | ||
| 133 | See https://nodejs.org/api/buffer.html#bufwriteint32levalue-offset | |
| 134 | ||
| 135 | #### `b4a.readDoubleLE(buffer[, offset])` | |
| 136 | ||
| 137 | See https://nodejs.org/api/buffer.html#bufreaddoubleleoffset | |
| 138 | ||
| 139 | #### `b4a.readFloatLE(buffer[, offset])` | |
| 140 | ||
| 141 | See https://nodejs.org/api/buffer.html#bufreadfloatleoffset | |
| 142 | ||
| 143 | #### `b4a.readUInt32LE(buffer[, offset])` | |
| 144 | ||
| 145 | See https://nodejs.org/api/buffer.html#bufreaduint32leoffset | |
| 146 | ||
| 147 | #### `b4a.readInt32LE(buffer[, offset])` | |
| 148 | ||
| 149 | See https://nodejs.org/api/buffer.html#bufreadint32leoffset | |
| 150 | ||
| 151 | ## License | |
| 152 | ||
| 153 | Apache 2.0 |
| … | … | @@ -0,0 +1,567 @@ |
| 1 | const ascii = require('./lib/ascii') | |
| 2 | const base64 = require('./lib/base64') | |
| 3 | const hex = require('./lib/hex') | |
| 4 | const latin1 = require('./lib/latin1') | |
| 5 | const utf8 = require('./lib/utf8') | |
| 6 | const utf16le = require('./lib/utf16le') | |
| 7 | ||
| 8 | const LE = new Uint8Array(Uint16Array.of(0xff).buffer)[0] === 0xff | |
| 9 | ||
| 10 | function codecFor(encoding) { | |
| 11 | switch (encoding) { | |
| 12 | case 'ascii': | |
| 13 | return ascii | |
| 14 | case 'base64': | |
| 15 | return base64 | |
| 16 | case 'hex': | |
| 17 | return hex | |
| 18 | case 'binary': | |
| 19 | case 'latin1': | |
| 20 | return latin1 | |
| 21 | case 'utf8': | |
| 22 | case 'utf-8': | |
| 23 | case undefined: | |
| 24 | case null: | |
| 25 | return utf8 | |
| 26 | case 'ucs2': | |
| 27 | case 'ucs-2': | |
| 28 | case 'utf16le': | |
| 29 | case 'utf-16le': | |
| 30 | return utf16le | |
| 31 | default: | |
| 32 | throw new Error(`Unknown encoding '${encoding}'`) | |
| 33 | } | |
| 34 | } | |
| 35 | ||
| 36 | function isBuffer(value) { | |
| 37 | return value instanceof Uint8Array | |
| 38 | } | |
| 39 | ||
| 40 | function isEncoding(encoding) { | |
| 41 | try { | |
| 42 | codecFor(encoding) | |
| 43 | return true | |
| 44 | } catch { | |
| 45 | return false | |
| 46 | } | |
| 47 | } | |
| 48 | ||
| 49 | function alloc(size, fill, encoding) { | |
| 50 | const buffer = new Uint8Array(size) | |
| 51 | if (fill !== undefined) { | |
| 52 | exports.fill(buffer, fill, 0, buffer.byteLength, encoding) | |
| 53 | } | |
| 54 | return buffer | |
| 55 | } | |
| 56 | ||
| 57 | function allocUnsafe(size) { | |
| 58 | return new Uint8Array(size) | |
| 59 | } | |
| 60 | ||
| 61 | function allocUnsafeSlow(size) { | |
| 62 | return new Uint8Array(size) | |
| 63 | } | |
| 64 | ||
| 65 | function byteLength(string, encoding) { | |
| 66 | return codecFor(encoding).byteLength(string) | |
| 67 | } | |
| 68 | ||
| 69 | function compare(a, b) { | |
| 70 | if (a === b) return 0 | |
| 71 | ||
| 72 | const len = Math.min(a.byteLength, b.byteLength) | |
| 73 | ||
| 74 | a = new DataView(a.buffer, a.byteOffset, a.byteLength) | |
| 75 | b = new DataView(b.buffer, b.byteOffset, b.byteLength) | |
| 76 | ||
| 77 | let i = 0 | |
| 78 | ||
| 79 | for (let n = len - (len % 4); i < n; i += 4) { | |
| 80 | const x = a.getUint32(i, LE) | |
| 81 | const y = b.getUint32(i, LE) | |
| 82 | if (x !== y) break | |
| 83 | } | |
| 84 | ||
| 85 | for (; i < len; i++) { | |
| 86 | const x = a.getUint8(i) | |
| 87 | const y = b.getUint8(i) | |
| 88 | if (x < y) return -1 | |
| 89 | if (x > y) return 1 | |
| 90 | } | |
| 91 | ||
| 92 | return a.byteLength > b.byteLength ? 1 : a.byteLength < b.byteLength ? -1 : 0 | |
| 93 | } | |
| 94 | ||
| 95 | function concat(buffers, length) { | |
| 96 | if (length === undefined) { | |
| 97 | length = buffers.reduce((len, buffer) => len + buffer.byteLength, 0) | |
| 98 | } | |
| 99 | ||
| 100 | const result = new Uint8Array(length) | |
| 101 | ||
| 102 | let offset = 0 | |
| 103 | ||
| 104 | for (const buffer of buffers) { | |
| 105 | if (offset + buffer.byteLength > result.byteLength) { | |
| 106 | result.set(buffer.subarray(0, result.byteLength - offset), offset) | |
| 107 | return result | |
| 108 | } | |
| 109 | ||
| 110 | result.set(buffer, offset) | |
| 111 | offset += buffer.byteLength | |
| 112 | } | |
| 113 | ||
| 114 | return result | |
| 115 | } | |
| 116 | ||
| 117 | function copy( | |
| 118 | source, | |
| 119 | target, | |
| 120 | targetStart = 0, | |
| 121 | sourceStart = 0, | |
| 122 | sourceEnd = source.byteLength | |
| 123 | ) { | |
| 124 | if (targetStart < 0) targetStart = 0 | |
| 125 | if (targetStart >= target.byteLength) return 0 | |
| 126 | ||
| 127 | const targetLength = target.byteLength - targetStart | |
| 128 | ||
| 129 | if (sourceStart < 0) sourceStart = 0 | |
| 130 | if (sourceStart >= source.byteLength) return 0 | |
| 131 | ||
| 132 | if (sourceEnd <= sourceStart) return 0 | |
| 133 | if (sourceEnd > source.byteLength) sourceEnd = source.byteLength | |
| 134 | ||
| 135 | if (sourceEnd - sourceStart > targetLength) { | |
| 136 | sourceEnd = sourceStart + targetLength | |
| 137 | } | |
| 138 | ||
| 139 | const sourceLength = sourceEnd - sourceStart | |
| 140 | ||
| 141 | if (source === target) { | |
| 142 | target.copyWithin(targetStart, sourceStart, sourceEnd) | |
| 143 | } else { | |
| 144 | if (sourceStart !== 0 || sourceEnd !== source.byteLength) { | |
| 145 | source = source.subarray(sourceStart, sourceEnd) | |
| 146 | } | |
| 147 | ||
| 148 | target.set(source, targetStart) | |
| 149 | } | |
| 150 | ||
| 151 | return sourceLength | |
| 152 | } | |
| 153 | ||
| 154 | function equals(a, b) { | |
| 155 | if (a === b) return true | |
| 156 | if (a.byteLength !== b.byteLength) return false | |
| 157 | ||
| 158 | return compare(a, b) === 0 | |
| 159 | } | |
| 160 | ||
| 161 | function fill( | |
| 162 | buffer, | |
| 163 | value, | |
| 164 | offset = 0, | |
| 165 | end = buffer.byteLength, | |
| 166 | encoding = 'utf8' | |
| 167 | ) { | |
| 168 | if (typeof value === 'string') { | |
| 169 | if (typeof offset === 'string') { | |
| 170 | // fill(string, encoding) | |
| 171 | encoding = offset | |
| 172 | offset = 0 | |
| 173 | end = buffer.byteLength | |
| 174 | } else if (typeof end === 'string') { | |
| 175 | // fill(string, offset, encoding) | |
| 176 | encoding = end | |
| 177 | end = buffer.byteLength | |
| 178 | } | |
| 179 | } else if (typeof value === 'number') { | |
| 180 | value = value & 0xff | |
| 181 | } else if (typeof value === 'boolean') { | |
| 182 | value = +value | |
| 183 | } | |
| 184 | ||
| 185 | if (offset < 0) offset = 0 | |
| 186 | if (offset >= buffer.byteLength) return buffer | |
| 187 | ||
| 188 | if (end <= offset) return buffer | |
| 189 | if (end > buffer.byteLength) end = buffer.byteLength | |
| 190 | ||
| 191 | if (typeof value === 'number') return buffer.fill(value, offset, end) | |
| 192 | ||
| 193 | if (typeof value === 'string') value = exports.from(value, encoding) | |
| 194 | ||
| 195 | const len = value.byteLength | |
| 196 | ||
| 197 | for (let i = 0, n = end - offset; i < n; ++i) { | |
| 198 | buffer[i + offset] = value[i % len] | |
| 199 | } | |
| 200 | ||
| 201 | return buffer | |
| 202 | } | |
| 203 | ||
| 204 | function from(value, encodingOrOffset, length) { | |
| 205 | // from(string, encoding) | |
| 206 | if (typeof value === 'string') return fromString(value, encodingOrOffset) | |
| 207 | ||
| 208 | // from(array) | |
| 209 | if (Array.isArray(value)) return fromArray(value) | |
| 210 | ||
| 211 | // from(buffer) | |
| 212 | if (ArrayBuffer.isView(value)) return fromBuffer(value) | |
| 213 | ||
| 214 | // from(arrayBuffer[, byteOffset[, length]]) | |
| 215 | return fromArrayBuffer(value, encodingOrOffset, length) | |
| 216 | } | |
| 217 | ||
| 218 | function fromString(string, encoding) { | |
| 219 | const codec = codecFor(encoding) | |
| 220 | const buffer = new Uint8Array(codec.byteLength(string)) | |
| 221 | codec.write(buffer, string) | |
| 222 | return buffer | |
| 223 | } | |
| 224 | ||
| 225 | function fromArray(array) { | |
| 226 | const buffer = new Uint8Array(array.length) | |
| 227 | buffer.set(array) | |
| 228 | return buffer | |
| 229 | } | |
| 230 | ||
| 231 | function fromBuffer(buffer) { | |
| 232 | const copy = new Uint8Array(buffer.byteLength) | |
| 233 | copy.set(buffer) | |
| 234 | return copy | |
| 235 | } | |
| 236 | ||
| 237 | function fromArrayBuffer(arrayBuffer, byteOffset, length) { | |
| 238 | return new Uint8Array(arrayBuffer, byteOffset, length) | |
| 239 | } | |
| 240 | ||
| 241 | function includes(buffer, value, byteOffset, encoding) { | |
| 242 | return indexOf(buffer, value, byteOffset, encoding) !== -1 | |
| 243 | } | |
| 244 | ||
| 245 | function indexOf(buffer, value, byteOffset, encoding) { | |
| 246 | return bidirectionalIndexOf( | |
| 247 | buffer, | |
| 248 | value, | |
| 249 | byteOffset, | |
| 250 | encoding, | |
| 251 | true /* first */ | |
| 252 | ) | |
| 253 | } | |
| 254 | ||
| 255 | function lastIndexOf(buffer, value, byteOffset, encoding) { | |
| 256 | return bidirectionalIndexOf( | |
| 257 | buffer, | |
| 258 | value, | |
| 259 | byteOffset, | |
| 260 | encoding, | |
| 261 | false /* last */ | |
| 262 | ) | |
| 263 | } | |
| 264 | ||
| 265 | function bidirectionalIndexOf(buffer, value, byteOffset, encoding, first) { | |
| 266 | if (buffer.byteLength === 0) return -1 | |
| 267 | ||
| 268 | if (typeof byteOffset === 'string') { | |
| 269 | encoding = byteOffset | |
| 270 | byteOffset = 0 | |
| 271 | } else if (byteOffset === undefined) { | |
| 272 | byteOffset = first ? 0 : buffer.length - 1 | |
| 273 | } else if (byteOffset < 0) { | |
| 274 | byteOffset += buffer.byteLength | |
| 275 | } | |
| 276 | ||
| 277 | if (byteOffset >= buffer.byteLength) { | |
| 278 | if (first) return -1 | |
| 279 | else byteOffset = buffer.byteLength - 1 | |
| 280 | } else if (byteOffset < 0) { | |
| 281 | if (first) byteOffset = 0 | |
| 282 | else return -1 | |
| 283 | } | |
| 284 | ||
| 285 | if (typeof value === 'string') { | |
| 286 | value = from(value, encoding) | |
| 287 | } else if (typeof value === 'number') { | |
| 288 | value = value & 0xff | |
| 289 | ||
| 290 | if (first) { | |
| 291 | return buffer.indexOf(value, byteOffset) | |
| 292 | } else { | |
| 293 | return buffer.lastIndexOf(value, byteOffset) | |
| 294 | } | |
| 295 | } | |
| 296 | ||
| 297 | if (value.byteLength === 0) return -1 | |
| 298 | ||
| 299 | if (first) { | |
| 300 | let foundIndex = -1 | |
| 301 | ||
| 302 | for (let i = byteOffset; i < buffer.byteLength; i++) { | |
| 303 | if (buffer[i] === value[foundIndex === -1 ? 0 : i - foundIndex]) { | |
| 304 | if (foundIndex === -1) foundIndex = i | |
| 305 | if (i - foundIndex + 1 === value.byteLength) return foundIndex | |
| 306 | } else { | |
| 307 | if (foundIndex !== -1) i -= i - foundIndex | |
| 308 | foundIndex = -1 | |
| 309 | } | |
| 310 | } | |
| 311 | } else { | |
| 312 | if (byteOffset + value.byteLength > buffer.byteLength) { | |
| 313 | byteOffset = buffer.byteLength - value.byteLength | |
| 314 | } | |
| 315 | ||
| 316 | for (let i = byteOffset; i >= 0; i--) { | |
| 317 | let found = true | |
| 318 | ||
| 319 | for (let j = 0; j < value.byteLength; j++) { | |
| 320 | if (buffer[i + j] !== value[j]) { | |
| 321 | found = false | |
| 322 | break | |
| 323 | } | |
| 324 | } | |
| 325 | ||
| 326 | if (found) return i | |
| 327 | } | |
| 328 | } | |
| 329 | ||
| 330 | return -1 | |
| 331 | } | |
| 332 | ||
| 333 | function swap(buffer, n, m) { | |
| 334 | const i = buffer[n] | |
| 335 | buffer[n] = buffer[m] | |
| 336 | buffer[m] = i | |
| 337 | } | |
| 338 | ||
| 339 | function swap16(buffer) { | |
| 340 | const len = buffer.byteLength | |
| 341 | ||
| 342 | if (len % 2 !== 0) | |
| 343 | throw new RangeError('Buffer size must be a multiple of 16-bits') | |
| 344 | ||
| 345 | for (let i = 0; i < len; i += 2) swap(buffer, i, i + 1) | |
| 346 | ||
| 347 | return buffer | |
| 348 | } | |
| 349 | ||
| 350 | function swap32(buffer) { | |
| 351 | const len = buffer.byteLength | |
| 352 | ||
| 353 | if (len % 4 !== 0) | |
| 354 | throw new RangeError('Buffer size must be a multiple of 32-bits') | |
| 355 | ||
| 356 | for (let i = 0; i < len; i += 4) { | |
| 357 | swap(buffer, i, i + 3) | |
| 358 | swap(buffer, i + 1, i + 2) | |
| 359 | } | |
| 360 | ||
| 361 | return buffer | |
| 362 | } | |
| 363 | ||
| 364 | function swap64(buffer) { | |
| 365 | const len = buffer.byteLength | |
| 366 | ||
| 367 | if (len % 8 !== 0) | |
| 368 | throw new RangeError('Buffer size must be a multiple of 64-bits') | |
| 369 | ||
| 370 | for (let i = 0; i < len; i += 8) { | |
| 371 | swap(buffer, i, i + 7) | |
| 372 | swap(buffer, i + 1, i + 6) | |
| 373 | swap(buffer, i + 2, i + 5) | |
| 374 | swap(buffer, i + 3, i + 4) | |
| 375 | } | |
| 376 | ||
| 377 | return buffer | |
| 378 | } | |
| 379 | ||
| 380 | function toBuffer(buffer) { | |
| 381 | return buffer | |
| 382 | } | |
| 383 | ||
| 384 | function toString( | |
| 385 | buffer, | |
| 386 | encoding = 'utf8', | |
| 387 | start = 0, | |
| 388 | end = buffer.byteLength | |
| 389 | ) { | |
| 390 | // toString(buffer) | |
| 391 | if (arguments.length === 1) return utf8.toString(buffer) | |
| 392 | ||
| 393 | // toString(buffer, encoding) | |
| 394 | if (arguments.length === 2) return codecFor(encoding).toString(buffer) | |
| 395 | ||
| 396 | if (start < 0) start = 0 | |
| 397 | if (start >= buffer.byteLength) return '' | |
| 398 | ||
| 399 | if (end <= start) return '' | |
| 400 | if (end > buffer.byteLength) end = buffer.byteLength | |
| 401 | ||
| 402 | if (start !== 0 || end !== buffer.byteLength) { | |
| 403 | buffer = buffer.subarray(start, end) | |
| 404 | } | |
| 405 | ||
| 406 | return codecFor(encoding).toString(buffer) | |
| 407 | } | |
| 408 | ||
| 409 | function write(buffer, string, offset, length, encoding) { | |
| 410 | // write(buffer, string) | |
| 411 | if (arguments.length === 2) return utf8.write(buffer, string) | |
| 412 | ||
| 413 | if (typeof offset === 'string') { | |
| 414 | // write(buffer, string, encoding) | |
| 415 | encoding = offset | |
| 416 | offset = 0 | |
| 417 | length = buffer.byteLength | |
| 418 | } else if (typeof length === 'string') { | |
| 419 | // write(buffer, string, offset, encoding) | |
| 420 | encoding = length | |
| 421 | length = buffer.byteLength - offset | |
| 422 | } | |
| 423 | ||
| 424 | length = Math.min(length, exports.byteLength(string, encoding)) | |
| 425 | ||
| 426 | let start = offset | |
| 427 | if (start < 0) start = 0 | |
| 428 | if (start >= buffer.byteLength) return 0 | |
| 429 | ||
| 430 | let end = offset + length | |
| 431 | if (end <= start) return 0 | |
| 432 | if (end > buffer.byteLength) end = buffer.byteLength | |
| 433 | ||
| 434 | if (start !== 0 || end !== buffer.byteLength) { | |
| 435 | buffer = buffer.subarray(start, end) | |
| 436 | } | |
| 437 | ||
| 438 | return codecFor(encoding).write(buffer, string) | |
| 439 | } | |
| 440 | ||
| 441 | function readDoubleBE(buffer, offset = 0) { | |
| 442 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 443 | return view.getFloat64(offset, false) | |
| 444 | } | |
| 445 | ||
| 446 | function readDoubleLE(buffer, offset = 0) { | |
| 447 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 448 | return view.getFloat64(offset, true) | |
| 449 | } | |
| 450 | ||
| 451 | function readFloatBE(buffer, offset = 0) { | |
| 452 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 453 | return view.getFloat32(offset, false) | |
| 454 | } | |
| 455 | ||
| 456 | function readFloatLE(buffer, offset = 0) { | |
| 457 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 458 | return view.getFloat32(offset, true) | |
| 459 | } | |
| 460 | ||
| 461 | function readInt32BE(buffer, offset = 0) { | |
| 462 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 463 | return view.getInt32(offset, false) | |
| 464 | } | |
| 465 | ||
| 466 | function readInt32LE(buffer, offset = 0) { | |
| 467 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 468 | return view.getInt32(offset, true) | |
| 469 | } | |
| 470 | ||
| 471 | function readUInt32BE(buffer, offset = 0) { | |
| 472 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 473 | return view.getUint32(offset, false) | |
| 474 | } | |
| 475 | ||
| 476 | function readUInt32LE(buffer, offset = 0) { | |
| 477 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 478 | return view.getUint32(offset, true) | |
| 479 | } | |
| 480 | ||
| 481 | function writeDoubleBE(buffer, value, offset = 0) { | |
| 482 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 483 | view.setFloat64(offset, value, false) | |
| 484 | return offset + 8 | |
| 485 | } | |
| 486 | ||
| 487 | function writeDoubleLE(buffer, value, offset = 0) { | |
| 488 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 489 | view.setFloat64(offset, value, true) | |
| 490 | return offset + 8 | |
| 491 | } | |
| 492 | ||
| 493 | function writeFloatBE(buffer, value, offset = 0) { | |
| 494 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 495 | view.setFloat32(offset, value, false) | |
| 496 | return offset + 4 | |
| 497 | } | |
| 498 | ||
| 499 | function writeFloatLE(buffer, value, offset = 0) { | |
| 500 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 501 | view.setFloat32(offset, value, true) | |
| 502 | return offset + 4 | |
| 503 | } | |
| 504 | ||
| 505 | function writeInt32BE(buffer, value, offset = 0) { | |
| 506 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 507 | view.setInt32(offset, value, false) | |
| 508 | return offset + 4 | |
| 509 | } | |
| 510 | ||
| 511 | function writeInt32LE(buffer, value, offset = 0) { | |
| 512 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 513 | view.setInt32(offset, value, true) | |
| 514 | return offset + 4 | |
| 515 | } | |
| 516 | ||
| 517 | function writeUInt32BE(buffer, value, offset = 0) { | |
| 518 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 519 | view.setUint32(offset, value, false) | |
| 520 | return offset + 4 | |
| 521 | } | |
| 522 | ||
| 523 | function writeUInt32LE(buffer, value, offset = 0) { | |
| 524 | const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 525 | view.setUint32(offset, value, true) | |
| 526 | return offset + 4 | |
| 527 | } | |
| 528 | ||
| 529 | module.exports = exports = { | |
| 530 | isBuffer, | |
| 531 | isEncoding, | |
| 532 | alloc, | |
| 533 | allocUnsafe, | |
| 534 | allocUnsafeSlow, | |
| 535 | byteLength, | |
| 536 | compare, | |
| 537 | concat, | |
| 538 | copy, | |
| 539 | equals, | |
| 540 | fill, | |
| 541 | from, | |
| 542 | includes, | |
| 543 | indexOf, | |
| 544 | lastIndexOf, | |
| 545 | swap16, | |
| 546 | swap32, | |
| 547 | swap64, | |
| 548 | toBuffer, | |
| 549 | toString, | |
| 550 | write, | |
| 551 | readDoubleBE, | |
| 552 | readDoubleLE, | |
| 553 | readFloatBE, | |
| 554 | readFloatLE, | |
| 555 | readInt32BE, | |
| 556 | readInt32LE, | |
| 557 | readUInt32BE, | |
| 558 | readUInt32LE, | |
| 559 | writeDoubleBE, | |
| 560 | writeDoubleLE, | |
| 561 | writeFloatBE, | |
| 562 | writeFloatLE, | |
| 563 | writeInt32BE, | |
| 564 | writeInt32LE, | |
| 565 | writeUInt32BE, | |
| 566 | writeUInt32LE | |
| 567 | } |
| … | … | @@ -0,0 +1,188 @@ |
| 1 | function isBuffer(value) { | |
| 2 | return Buffer.isBuffer(value) || value instanceof Uint8Array | |
| 3 | } | |
| 4 | ||
| 5 | function isEncoding(encoding) { | |
| 6 | return Buffer.isEncoding(encoding) | |
| 7 | } | |
| 8 | ||
| 9 | function alloc(size, fill, encoding) { | |
| 10 | return Buffer.alloc(size, fill, encoding) | |
| 11 | } | |
| 12 | ||
| 13 | function allocUnsafe(size) { | |
| 14 | return Buffer.allocUnsafe(size) | |
| 15 | } | |
| 16 | ||
| 17 | function allocUnsafeSlow(size) { | |
| 18 | return Buffer.allocUnsafeSlow(size) | |
| 19 | } | |
| 20 | ||
| 21 | function byteLength(string, encoding) { | |
| 22 | return Buffer.byteLength(string, encoding) | |
| 23 | } | |
| 24 | ||
| 25 | function compare(a, b) { | |
| 26 | return Buffer.compare(a, b) | |
| 27 | } | |
| 28 | ||
| 29 | function concat(buffers, totalLength) { | |
| 30 | return Buffer.concat(buffers, totalLength) | |
| 31 | } | |
| 32 | ||
| 33 | function copy(source, target, targetStart, start, end) { | |
| 34 | return toBuffer(source).copy(target, targetStart, start, end) | |
| 35 | } | |
| 36 | ||
| 37 | function equals(a, b) { | |
| 38 | return toBuffer(a).equals(b) | |
| 39 | } | |
| 40 | ||
| 41 | function fill(buffer, value, offset, end, encoding) { | |
| 42 | return toBuffer(buffer).fill(value, offset, end, encoding) | |
| 43 | } | |
| 44 | ||
| 45 | function from(value, encodingOrOffset, length) { | |
| 46 | return Buffer.from(value, encodingOrOffset, length) | |
| 47 | } | |
| 48 | ||
| 49 | function includes(buffer, value, byteOffset, encoding) { | |
| 50 | return toBuffer(buffer).includes(value, byteOffset, encoding) | |
| 51 | } | |
| 52 | ||
| 53 | function indexOf(buffer, value, byfeOffset, encoding) { | |
| 54 | return toBuffer(buffer).indexOf(value, byfeOffset, encoding) | |
| 55 | } | |
| 56 | ||
| 57 | function lastIndexOf(buffer, value, byteOffset, encoding) { | |
| 58 | return toBuffer(buffer).lastIndexOf(value, byteOffset, encoding) | |
| 59 | } | |
| 60 | ||
| 61 | function swap16(buffer) { | |
| 62 | return toBuffer(buffer).swap16() | |
| 63 | } | |
| 64 | ||
| 65 | function swap32(buffer) { | |
| 66 | return toBuffer(buffer).swap32() | |
| 67 | } | |
| 68 | ||
| 69 | function swap64(buffer) { | |
| 70 | return toBuffer(buffer).swap64() | |
| 71 | } | |
| 72 | ||
| 73 | function toBuffer(buffer) { | |
| 74 | if (Buffer.isBuffer(buffer)) return buffer | |
| 75 | return Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 76 | } | |
| 77 | ||
| 78 | function toString(buffer, encoding, start, end) { | |
| 79 | return toBuffer(buffer).toString(encoding, start, end) | |
| 80 | } | |
| 81 | ||
| 82 | function write(buffer, string, offset, length, encoding) { | |
| 83 | return toBuffer(buffer).write(string, offset, length, encoding) | |
| 84 | } | |
| 85 | ||
| 86 | function readDoubleBE(buffer, offset) { | |
| 87 | return toBuffer(buffer).readDoubleBE(offset) | |
| 88 | } | |
| 89 | ||
| 90 | function readDoubleLE(buffer, offset) { | |
| 91 | return toBuffer(buffer).readDoubleLE(offset) | |
| 92 | } | |
| 93 | ||
| 94 | function readFloatBE(buffer, offset) { | |
| 95 | return toBuffer(buffer).readFloatBE(offset) | |
| 96 | } | |
| 97 | ||
| 98 | function readFloatLE(buffer, offset) { | |
| 99 | return toBuffer(buffer).readFloatLE(offset) | |
| 100 | } | |
| 101 | ||
| 102 | function readInt32BE(buffer, offset) { | |
| 103 | return toBuffer(buffer).readInt32BE(offset) | |
| 104 | } | |
| 105 | ||
| 106 | function readInt32LE(buffer, offset) { | |
| 107 | return toBuffer(buffer).readInt32LE(offset) | |
| 108 | } | |
| 109 | ||
| 110 | function readUInt32BE(buffer, offset) { | |
| 111 | return toBuffer(buffer).readUInt32BE(offset) | |
| 112 | } | |
| 113 | ||
| 114 | function readUInt32LE(buffer, offset) { | |
| 115 | return toBuffer(buffer).readUInt32LE(offset) | |
| 116 | } | |
| 117 | ||
| 118 | function writeDoubleBE(buffer, value, offset) { | |
| 119 | return toBuffer(buffer).writeDoubleBE(value, offset) | |
| 120 | } | |
| 121 | ||
| 122 | function writeDoubleLE(buffer, value, offset) { | |
| 123 | return toBuffer(buffer).writeDoubleLE(value, offset) | |
| 124 | } | |
| 125 | ||
| 126 | function writeFloatBE(buffer, value, offset) { | |
| 127 | return toBuffer(buffer).writeFloatBE(value, offset) | |
| 128 | } | |
| 129 | ||
| 130 | function writeFloatLE(buffer, value, offset) { | |
| 131 | return toBuffer(buffer).writeFloatLE(value, offset) | |
| 132 | } | |
| 133 | ||
| 134 | function writeInt32BE(buffer, value, offset) { | |
| 135 | return toBuffer(buffer).writeInt32BE(value, offset) | |
| 136 | } | |
| 137 | ||
| 138 | function writeInt32LE(buffer, value, offset) { | |
| 139 | return toBuffer(buffer).writeInt32LE(value, offset) | |
| 140 | } | |
| 141 | ||
| 142 | function writeUInt32BE(buffer, value, offset) { | |
| 143 | return toBuffer(buffer).writeUInt32BE(value, offset) | |
| 144 | } | |
| 145 | ||
| 146 | function writeUInt32LE(buffer, value, offset) { | |
| 147 | return toBuffer(buffer).writeUInt32LE(value, offset) | |
| 148 | } | |
| 149 | ||
| 150 | module.exports = { | |
| 151 | isBuffer, | |
| 152 | isEncoding, | |
| 153 | alloc, | |
| 154 | allocUnsafe, | |
| 155 | allocUnsafeSlow, | |
| 156 | byteLength, | |
| 157 | compare, | |
| 158 | concat, | |
| 159 | copy, | |
| 160 | equals, | |
| 161 | fill, | |
| 162 | from, | |
| 163 | includes, | |
| 164 | indexOf, | |
| 165 | lastIndexOf, | |
| 166 | swap16, | |
| 167 | swap32, | |
| 168 | swap64, | |
| 169 | toBuffer, | |
| 170 | toString, | |
| 171 | write, | |
| 172 | readDoubleBE, | |
| 173 | readDoubleLE, | |
| 174 | readFloatBE, | |
| 175 | readFloatLE, | |
| 176 | readInt32BE, | |
| 177 | readInt32LE, | |
| 178 | readUInt32BE, | |
| 179 | readUInt32LE, | |
| 180 | writeDoubleBE, | |
| 181 | writeDoubleLE, | |
| 182 | writeFloatBE, | |
| 183 | writeFloatLE, | |
| 184 | writeInt32BE, | |
| 185 | writeInt32LE, | |
| 186 | writeUInt32BE, | |
| 187 | writeUInt32LE | |
| 188 | } |
| … | … | @@ -0,0 +1,31 @@ |
| 1 | function byteLength(string) { | |
| 2 | return string.length | |
| 3 | } | |
| 4 | ||
| 5 | function toString(buffer) { | |
| 6 | const len = buffer.byteLength | |
| 7 | ||
| 8 | let result = '' | |
| 9 | ||
| 10 | for (let i = 0; i < len; i++) { | |
| 11 | result += String.fromCharCode(buffer[i] & 0x7f) | |
| 12 | } | |
| 13 | ||
| 14 | return result | |
| 15 | } | |
| 16 | ||
| 17 | function write(buffer, string) { | |
| 18 | const len = buffer.byteLength | |
| 19 | ||
| 20 | for (let i = 0; i < len; i++) { | |
| 21 | buffer[i] = string.charCodeAt(i) | |
| 22 | } | |
| 23 | ||
| 24 | return len | |
| 25 | } | |
| 26 | ||
| 27 | module.exports = { | |
| 28 | byteLength, | |
| 29 | toString, | |
| 30 | write | |
| 31 | } |
| … | … | @@ -0,0 +1,65 @@ |
| 1 | const alphabet = | |
| 2 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' | |
| 3 | ||
| 4 | const codes = new Uint8Array(256) | |
| 5 | ||
| 6 | for (let i = 0; i < alphabet.length; i++) { | |
| 7 | codes[alphabet.charCodeAt(i)] = i | |
| 8 | } | |
| 9 | ||
| 10 | codes[/* - */ 0x2d] = 62 | |
| 11 | codes[/* _ */ 0x5f] = 63 | |
| 12 | ||
| 13 | function byteLength(string) { | |
| 14 | let len = string.length | |
| 15 | ||
| 16 | if (string.charCodeAt(len - 1) === 0x3d) len-- | |
| 17 | if (len > 1 && string.charCodeAt(len - 1) === 0x3d) len-- | |
| 18 | ||
| 19 | return (len * 3) >>> 2 | |
| 20 | } | |
| 21 | ||
| 22 | function toString(buffer) { | |
| 23 | const len = buffer.byteLength | |
| 24 | ||
| 25 | let result = '' | |
| 26 | ||
| 27 | for (let i = 0; i < len; i += 3) { | |
| 28 | result += | |
| 29 | alphabet[buffer[i] >> 2] + | |
| 30 | alphabet[((buffer[i] & 3) << 4) | (buffer[i + 1] >> 4)] + | |
| 31 | alphabet[((buffer[i + 1] & 15) << 2) | (buffer[i + 2] >> 6)] + | |
| 32 | alphabet[buffer[i + 2] & 63] | |
| 33 | } | |
| 34 | ||
| 35 | if (len % 3 === 2) { | |
| 36 | result = result.substring(0, result.length - 1) + '=' | |
| 37 | } else if (len % 3 === 1) { | |
| 38 | result = result.substring(0, result.length - 2) + '==' | |
| 39 | } | |
| 40 | ||
| 41 | return result | |
| 42 | } | |
| 43 | ||
| 44 | function write(buffer, string) { | |
| 45 | const len = buffer.byteLength | |
| 46 | ||
| 47 | for (let i = 0, j = 0; j < len; i += 4) { | |
| 48 | const a = codes[string.charCodeAt(i)] | |
| 49 | const b = codes[string.charCodeAt(i + 1)] | |
| 50 | const c = codes[string.charCodeAt(i + 2)] | |
| 51 | const d = codes[string.charCodeAt(i + 3)] | |
| 52 | ||
| 53 | buffer[j++] = (a << 2) | (b >> 4) | |
| 54 | buffer[j++] = ((b & 15) << 4) | (c >> 2) | |
| 55 | buffer[j++] = ((c & 3) << 6) | (d & 63) | |
| 56 | } | |
| 57 | ||
| 58 | return len | |
| 59 | } | |
| 60 | ||
| 61 | module.exports = { | |
| 62 | byteLength, | |
| 63 | toString, | |
| 64 | write | |
| 65 | } |
| … | … | @@ -0,0 +1,51 @@ |
| 1 | function byteLength(string) { | |
| 2 | return string.length >>> 1 | |
| 3 | } | |
| 4 | ||
| 5 | function toString(buffer) { | |
| 6 | const len = buffer.byteLength | |
| 7 | ||
| 8 | buffer = new DataView(buffer.buffer, buffer.byteOffset, len) | |
| 9 | ||
| 10 | let result = '' | |
| 11 | let i = 0 | |
| 12 | ||
| 13 | for (let n = len - (len % 4); i < n; i += 4) { | |
| 14 | result += buffer.getUint32(i).toString(16).padStart(8, '0') | |
| 15 | } | |
| 16 | ||
| 17 | for (; i < len; i++) { | |
| 18 | result += buffer.getUint8(i).toString(16).padStart(2, '0') | |
| 19 | } | |
| 20 | ||
| 21 | return result | |
| 22 | } | |
| 23 | ||
| 24 | function write(buffer, string) { | |
| 25 | const len = buffer.byteLength | |
| 26 | ||
| 27 | for (let i = 0; i < len; i++) { | |
| 28 | const a = hexValue(string.charCodeAt(i * 2)) | |
| 29 | const b = hexValue(string.charCodeAt(i * 2 + 1)) | |
| 30 | ||
| 31 | if (a === undefined || b === undefined) { | |
| 32 | return buffer.subarray(0, i) | |
| 33 | } | |
| 34 | ||
| 35 | buffer[i] = (a << 4) | b | |
| 36 | } | |
| 37 | ||
| 38 | return len | |
| 39 | } | |
| 40 | ||
| 41 | module.exports = { | |
| 42 | byteLength, | |
| 43 | toString, | |
| 44 | write | |
| 45 | } | |
| 46 | ||
| 47 | function hexValue(char) { | |
| 48 | if (char >= 0x30 && char <= 0x39) return char - 0x30 | |
| 49 | if (char >= 0x41 && char <= 0x46) return char - 0x41 + 10 | |
| 50 | if (char >= 0x61 && char <= 0x66) return char - 0x61 + 10 | |
| 51 | } |
| … | … | @@ -0,0 +1,31 @@ |
| 1 | function byteLength(string) { | |
| 2 | return string.length | |
| 3 | } | |
| 4 | ||
| 5 | function toString(buffer) { | |
| 6 | const len = buffer.byteLength | |
| 7 | ||
| 8 | let result = '' | |
| 9 | ||
| 10 | for (let i = 0; i < len; i++) { | |
| 11 | result += String.fromCharCode(buffer[i]) | |
| 12 | } | |
| 13 | ||
| 14 | return result | |
| 15 | } | |
| 16 | ||
| 17 | function write(buffer, string) { | |
| 18 | const len = buffer.byteLength | |
| 19 | ||
| 20 | for (let i = 0; i < len; i++) { | |
| 21 | buffer[i] = string.charCodeAt(i) | |
| 22 | } | |
| 23 | ||
| 24 | return len | |
| 25 | } | |
| 26 | ||
| 27 | module.exports = { | |
| 28 | byteLength, | |
| 29 | toString, | |
| 30 | write | |
| 31 | } |
| … | … | @@ -0,0 +1,40 @@ |
| 1 | function byteLength(string) { | |
| 2 | return string.length * 2 | |
| 3 | } | |
| 4 | ||
| 5 | function toString(buffer) { | |
| 6 | const len = buffer.byteLength | |
| 7 | ||
| 8 | let result = '' | |
| 9 | ||
| 10 | for (let i = 0; i < len - 1; i += 2) { | |
| 11 | result += String.fromCharCode(buffer[i] + buffer[i + 1] * 256) | |
| 12 | } | |
| 13 | ||
| 14 | return result | |
| 15 | } | |
| 16 | ||
| 17 | function write(buffer, string) { | |
| 18 | const len = buffer.byteLength | |
| 19 | ||
| 20 | let units = len | |
| 21 | ||
| 22 | for (let i = 0; i < string.length; ++i) { | |
| 23 | if ((units -= 2) < 0) break | |
| 24 | ||
| 25 | const c = string.charCodeAt(i) | |
| 26 | const hi = c >> 8 | |
| 27 | const lo = c % 256 | |
| 28 | ||
| 29 | buffer[i * 2] = lo | |
| 30 | buffer[i * 2 + 1] = hi | |
| 31 | } | |
| 32 | ||
| 33 | return len | |
| 34 | } | |
| 35 | ||
| 36 | module.exports = { | |
| 37 | byteLength, | |
| 38 | toString, | |
| 39 | write | |
| 40 | } |
| … | … | @@ -0,0 +1,141 @@ |
| 1 | function byteLength(string) { | |
| 2 | let length = 0 | |
| 3 | ||
| 4 | for (let i = 0, n = string.length; i < n; i++) { | |
| 5 | const code = string.charCodeAt(i) | |
| 6 | ||
| 7 | if (code >= 0xd800 && code <= 0xdbff && i + 1 < n) { | |
| 8 | const code = string.charCodeAt(i + 1) | |
| 9 | ||
| 10 | if (code >= 0xdc00 && code <= 0xdfff) { | |
| 11 | length += 4 | |
| 12 | i++ | |
| 13 | continue | |
| 14 | } | |
| 15 | } | |
| 16 | ||
| 17 | if (code <= 0x7f) length += 1 | |
| 18 | else if (code <= 0x7ff) length += 2 | |
| 19 | else length += 3 | |
| 20 | } | |
| 21 | ||
| 22 | return length | |
| 23 | } | |
| 24 | ||
| 25 | let toString | |
| 26 | ||
| 27 | if (typeof TextDecoder !== 'undefined') { | |
| 28 | const decoder = new TextDecoder() | |
| 29 | ||
| 30 | toString = function toString(buffer) { | |
| 31 | return decoder.decode(buffer) | |
| 32 | } | |
| 33 | } else { | |
| 34 | toString = function toString(buffer) { | |
| 35 | const len = buffer.byteLength | |
| 36 | ||
| 37 | let output = '' | |
| 38 | let i = 0 | |
| 39 | ||
| 40 | while (i < len) { | |
| 41 | let byte = buffer[i] | |
| 42 | ||
| 43 | if (byte <= 0x7f) { | |
| 44 | output += String.fromCharCode(byte) | |
| 45 | i++ | |
| 46 | continue | |
| 47 | } | |
| 48 | ||
| 49 | let bytesNeeded = 0 | |
| 50 | let codePoint = 0 | |
| 51 | ||
| 52 | if (byte <= 0xdf) { | |
| 53 | bytesNeeded = 1 | |
| 54 | codePoint = byte & 0x1f | |
| 55 | } else if (byte <= 0xef) { | |
| 56 | bytesNeeded = 2 | |
| 57 | codePoint = byte & 0x0f | |
| 58 | } else if (byte <= 0xf4) { | |
| 59 | bytesNeeded = 3 | |
| 60 | codePoint = byte & 0x07 | |
| 61 | } | |
| 62 | ||
| 63 | if (len - i - bytesNeeded > 0) { | |
| 64 | let k = 0 | |
| 65 | ||
| 66 | while (k < bytesNeeded) { | |
| 67 | byte = buffer[i + k + 1] | |
| 68 | codePoint = (codePoint << 6) | (byte & 0x3f) | |
| 69 | k += 1 | |
| 70 | } | |
| 71 | } else { | |
| 72 | codePoint = 0xfffd | |
| 73 | bytesNeeded = len - i | |
| 74 | } | |
| 75 | ||
| 76 | output += String.fromCodePoint(codePoint) | |
| 77 | i += bytesNeeded + 1 | |
| 78 | } | |
| 79 | ||
| 80 | return output | |
| 81 | } | |
| 82 | } | |
| 83 | ||
| 84 | let write | |
| 85 | ||
| 86 | if (typeof TextEncoder !== 'undefined') { | |
| 87 | const encoder = new TextEncoder() | |
| 88 | ||
| 89 | write = function write(buffer, string) { | |
| 90 | return encoder.encodeInto(string, buffer).written | |
| 91 | } | |
| 92 | } else { | |
| 93 | write = function write(buffer, string) { | |
| 94 | const len = buffer.byteLength | |
| 95 | ||
| 96 | let i = 0 | |
| 97 | let j = 0 | |
| 98 | ||
| 99 | while (i < string.length) { | |
| 100 | const code = string.codePointAt(i) | |
| 101 | ||
| 102 | if (code <= 0x7f) { | |
| 103 | buffer[j++] = code | |
| 104 | i++ | |
| 105 | continue | |
| 106 | } | |
| 107 | ||
| 108 | let count = 0 | |
| 109 | let bits = 0 | |
| 110 | ||
| 111 | if (code <= 0x7ff) { | |
| 112 | count = 6 | |
| 113 | bits = 0xc0 | |
| 114 | } else if (code <= 0xffff) { | |
| 115 | count = 12 | |
| 116 | bits = 0xe0 | |
| 117 | } else if (code <= 0x1fffff) { | |
| 118 | count = 18 | |
| 119 | bits = 0xf0 | |
| 120 | } | |
| 121 | ||
| 122 | buffer[j++] = bits | (code >> count) | |
| 123 | count -= 6 | |
| 124 | ||
| 125 | while (count >= 0) { | |
| 126 | buffer[j++] = 0x80 | ((code >> count) & 0x3f) | |
| 127 | count -= 6 | |
| 128 | } | |
| 129 | ||
| 130 | i += code >= 0x10000 ? 2 : 1 | |
| 131 | } | |
| 132 | ||
| 133 | return len | |
| 134 | } | |
| 135 | } | |
| 136 | ||
| 137 | module.exports = { | |
| 138 | byteLength, | |
| 139 | toString, | |
| 140 | write | |
| 141 | } |
| … | … | @@ -0,0 +1,49 @@ |
| 1 | { | |
| 2 | "name": "b4a", | |
| 3 | "version": "1.8.0", | |
| 4 | "description": "Bridging the gap between buffers and typed arrays", | |
| 5 | "exports": { | |
| 6 | "./package": "./package.json", | |
| 7 | ".": { | |
| 8 | "react-native": "./react-native.js", | |
| 9 | "browser": "./browser.js", | |
| 10 | "default": "./index.js" | |
| 11 | } | |
| 12 | }, | |
| 13 | "files": [ | |
| 14 | "browser.js", | |
| 15 | "index.js", | |
| 16 | "react-native.js", | |
| 17 | "lib" | |
| 18 | ], | |
| 19 | "scripts": { | |
| 20 | "test": "npm run lint && npm run test:bare && npm run test:node", | |
| 21 | "test:bare": "bare test.mjs", | |
| 22 | "test:node": "node test.mjs", | |
| 23 | "lint": "prettier . --check" | |
| 24 | }, | |
| 25 | "repository": { | |
| 26 | "type": "git", | |
| 27 | "url": "git+https://github.com/holepunchto/b4a.git" | |
| 28 | }, | |
| 29 | "author": "Holepunch", | |
| 30 | "license": "Apache-2.0", | |
| 31 | "bugs": { | |
| 32 | "url": "https://github.com/holepunchto/b4a/issues" | |
| 33 | }, | |
| 34 | "homepage": "https://github.com/holepunchto/b4a#readme", | |
| 35 | "devDependencies": { | |
| 36 | "brittle": "^3.5.2", | |
| 37 | "nanobench": "^3.0.0", | |
| 38 | "prettier": "^3.6.2", | |
| 39 | "prettier-config-holepunch": "^1.0.0" | |
| 40 | }, | |
| 41 | "peerDependencies": { | |
| 42 | "react-native-b4a": "*" | |
| 43 | }, | |
| 44 | "peerDependenciesMeta": { | |
| 45 | "react-native-b4a": { | |
| 46 | "optional": true | |
| 47 | } | |
| 48 | } | |
| 49 | } |
| … | … | @@ -0,0 +1,5 @@ |
| 1 | try { | |
| 2 | module.exports = require('react-native-b4a') | |
| 3 | } catch { | |
| 4 | module.exports = require('./browser') | |
| 5 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,156 @@ |
| 1 | # bare-addon-resolve | |
| 2 | ||
| 3 | Low-level addon resolution algorithm for Bare. The algorithm is implemented as a generator function that yields either package manifests to be read or resolution candidates to be tested by the caller. As a convenience, the main export is a synchronous and asynchronous iterable that relies on package manifests being read by a callback. For asynchronous iteration, the callback may return promises which will be awaited before being passed to the generator. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i bare-addon-resolve | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | For synchronous resolution: | |
| 12 | ||
| 13 | ```js | |
| 14 | const resolve = require('bare-addon-resolve') | |
| 15 | ||
| 16 | function readPackage(url) { | |
| 17 | // Read and parse `url` if it exists, otherwise `null` | |
| 18 | } | |
| 19 | ||
| 20 | for (const resolution of resolve('./addon', new URL('file:///directory/'), readPackage)) { | |
| 21 | console.log(resolution) | |
| 22 | } | |
| 23 | ``` | |
| 24 | ||
| 25 | For asynchronous resolution: | |
| 26 | ||
| 27 | ```js | |
| 28 | const resolve = require('bare-addon-resolve') | |
| 29 | ||
| 30 | async function readPackage(url) { | |
| 31 | // Read and parse `url` if it exists, otherwise `null` | |
| 32 | } | |
| 33 | ||
| 34 | for await (const resolution of resolve('./addon', new URL('file:///directory/'), readPackage)) { | |
| 35 | console.log(resolution) | |
| 36 | } | |
| 37 | ``` | |
| 38 | ||
| 39 | ## API | |
| 40 | ||
| 41 | #### `const resolver = resolve(specifier, parentURL[, options][, readPackage])` | |
| 42 | ||
| 43 | Resolve `specifier` relative to `parentURL`, which must be a WHATWG `URL` instance. `readPackage` is called with a `URL` instance for every package manifest to be read and must either return the parsed JSON package manifest, if it exists, or `null`. If `readPackage` returns a promise, synchronous iteration is not supported. | |
| 44 | ||
| 45 | Options include: | |
| 46 | ||
| 47 | ```js | |
| 48 | options = { | |
| 49 | // A list of builtin addon specifiers. If matched, the protocol of the | |
| 50 | // resolved URL will be `builtinProtocol`. | |
| 51 | builtins: [], | |
| 52 | // The protocol to use for resolved builtin addon specifiers. | |
| 53 | builtinProtocol: 'builtin:', | |
| 54 | // Whether or not addons linked ahead-of-time should be resolved. | |
| 55 | linked: true, | |
| 56 | // The protocol to use for addons linked ahead-of-time. | |
| 57 | linkedProtocol: 'linked:', | |
| 58 | // The supported import conditions. "default" is always recognized. | |
| 59 | conditions: [], | |
| 60 | // An array reference which will contain the matched conditions when yielding | |
| 61 | // resolutions. | |
| 62 | matchedConditions: [], | |
| 63 | // The `<platform>-<arch>` combinations to look for when resolving dynamic | |
| 64 | // addons. If empty, only builtin specifiers can be resolved. In Bare, | |
| 65 | // pass `[Bare.Addon.host]`. | |
| 66 | hosts: [], | |
| 67 | // The file extensions to look for when resolving dynamic addons. | |
| 68 | extensions: [], | |
| 69 | // A map of preresolved imports with keys being serialized directory URLs and | |
| 70 | // values being "imports" maps. | |
| 71 | resolutions | |
| 72 | } | |
| 73 | ``` | |
| 74 | ||
| 75 | #### `for (const resolution of resolver)` | |
| 76 | ||
| 77 | Synchronously iterate the addon resolution candidates. The resolved addon is the first candidate that exists as a file on the file system. | |
| 78 | ||
| 79 | #### `for await (const resolution of resolver)` | |
| 80 | ||
| 81 | Asynchronously iterate the addon resolution candidates. If `readPackage` returns promises, these will be awaited. The same comments as `for (const resolution of resolver)` apply. | |
| 82 | ||
| 83 | ### Algorithm | |
| 84 | ||
| 85 | The following generator functions implement the resolution algorithm. The yielded values have the following shape: | |
| 86 | ||
| 87 | **Package manifest** | |
| 88 | ||
| 89 | ```js | |
| 90 | next.value = { | |
| 91 | package: URL | |
| 92 | } | |
| 93 | ``` | |
| 94 | ||
| 95 | If the package manifest identified by `next.value.package` exists, `generator.next()` must be passed the parsed JSON value of the manifest. If it does not exist, pass `null` instead. | |
| 96 | ||
| 97 | **Resolution candidate** | |
| 98 | ||
| 99 | ```js | |
| 100 | next.value = { | |
| 101 | resolution: URL | |
| 102 | } | |
| 103 | ``` | |
| 104 | ||
| 105 | If the addon identified by `next.value.resolution` exists, `generator.next()` may be passed `true` to signal that the resolution for the current set of conditions has been identified. If it does not exist, pass `false` instead. | |
| 106 | ||
| 107 | To drive the generator functions, a loop like the following can be used: | |
| 108 | ||
| 109 | ```js | |
| 110 | const generator = resolve.addon(specifier, parentURL) | |
| 111 | ||
| 112 | let next = generator.next() | |
| 113 | ||
| 114 | while (next.done !== true) { | |
| 115 | const value = next.value | |
| 116 | ||
| 117 | if (value.package) { | |
| 118 | // Read and parse `value.package` if it exists, otherwise `null` | |
| 119 | let info | |
| 120 | ||
| 121 | next = generator.next(info) | |
| 122 | } else { | |
| 123 | const resolution = value.resolution | |
| 124 | ||
| 125 | // `true` if `resolution` was the correct candidate, otherwise `false` | |
| 126 | let resolved | |
| 127 | ||
| 128 | next = generator.next(resolved) | |
| 129 | } | |
| 130 | } | |
| 131 | ``` | |
| 132 | ||
| 133 | Options are the same as `resolve()` for all functions. | |
| 134 | ||
| 135 | > [!WARNING] | |
| 136 | > These functions are currently subject to change between minor releases. If using them directly, make sure to specify a tilde range (`~1.2.3`) when declaring the module dependency. | |
| 137 | ||
| 138 | #### `const generator = resolve.addon(specifier, parentURL[, options])` | |
| 139 | ||
| 140 | #### `const generator = resolve.url(url, parentURL[, options])` | |
| 141 | ||
| 142 | #### `const generator = resolve.package(packageSpecifier, packageVersion, parentURL[, options])` | |
| 143 | ||
| 144 | #### `const generator = resolve.packageSelf(packageName, packageSubpath, packageVersion, parentURL[, options])` | |
| 145 | ||
| 146 | #### `const generator = resolve.preresolved(directoryURL, resolutions[, options])` | |
| 147 | ||
| 148 | #### `const generator = resolve.file(filename, parentURL[, options])` | |
| 149 | ||
| 150 | #### `const generator = resolve.directory(dirname, version, parentURL[, options])` | |
| 151 | ||
| 152 | #### `const generator = resolve.linked(name, version[, options])` | |
| 153 | ||
| 154 | ## License | |
| 155 | ||
| 156 | Apache-2.0 |
| … | … | @@ -0,0 +1,85 @@ |
| 1 | import URL from 'bare-url' | |
| 2 | import { constants, type Builtins, type Conditions, type ResolutionsMap } from 'bare-module-resolve' | |
| 3 | ||
| 4 | type JSON = string | number | boolean | JSON[] | { [key: string]: JSON } | |
| 5 | ||
| 6 | interface ResolveOptions { | |
| 7 | builtinProtocol?: string | |
| 8 | builtins?: Builtins | |
| 9 | conditions?: Conditions | |
| 10 | extensions?: string[] | |
| 11 | host?: string | |
| 12 | hosts?: string[] | |
| 13 | linked?: boolean | |
| 14 | linkedProtocol?: string | |
| 15 | matchedConditions?: string[] | |
| 16 | resolutions?: ResolutionsMap | |
| 17 | } | |
| 18 | ||
| 19 | declare function resolve( | |
| 20 | specifier: string, | |
| 21 | parentURL: URL, | |
| 22 | readPackage?: (url: URL) => JSON | null | |
| 23 | ): Iterable<URL> | |
| 24 | ||
| 25 | declare function resolve( | |
| 26 | specifier: string, | |
| 27 | parentURL: URL, | |
| 28 | readPackage: (url: URL) => Promise<JSON | null> | |
| 29 | ): AsyncIterable<URL> | |
| 30 | ||
| 31 | declare function resolve( | |
| 32 | specifier: string, | |
| 33 | parentURL: URL, | |
| 34 | opts: ResolveOptions, | |
| 35 | readPackage?: (url: URL) => JSON | null | |
| 36 | ): Iterable<URL> | |
| 37 | ||
| 38 | declare function resolve( | |
| 39 | specifier: string, | |
| 40 | parentURL: URL, | |
| 41 | opts: ResolveOptions, | |
| 42 | readPackage: (url: URL) => Promise<JSON | null> | |
| 43 | ): AsyncIterable<URL> | |
| 44 | ||
| 45 | declare namespace resolve { | |
| 46 | export { constants, type ResolveOptions } | |
| 47 | ||
| 48 | export type Resolver = Generator< | |
| 49 | { resolution: URL } | { package: URL }, | |
| 50 | number, | |
| 51 | void | boolean | JSON | null | |
| 52 | > | |
| 53 | ||
| 54 | export function addon(specifier: string, parentURL: URL, opts?: ResolveOptions): Resolver | |
| 55 | ||
| 56 | export function url(specifier: string, parentURL: URL, opts?: ResolveOptions): Resolver | |
| 57 | ||
| 58 | export function package( | |
| 59 | packageSpecifier: string, | |
| 60 | packageVersion: string, | |
| 61 | parentURL: URL, | |
| 62 | opts?: ResolveOptions | |
| 63 | ): Resolver | |
| 64 | ||
| 65 | export function packageSelf( | |
| 66 | packageName: string, | |
| 67 | packageSubpath: string, | |
| 68 | packageVersion: string, | |
| 69 | parentURL: URL, | |
| 70 | opts?: ResolveOptions | |
| 71 | ): Resolver | |
| 72 | ||
| 73 | export function file(filename: string, parentURL: URL, opts?: ResolveOptions): Resolver | |
| 74 | ||
| 75 | export function directory( | |
| 76 | dirname: string, | |
| 77 | version: string, | |
| 78 | parentURL: URL, | |
| 79 | opts?: ResolveOptions | |
| 80 | ): Resolver | |
| 81 | ||
| 82 | export function linked(name: string, version?: string, opts?: ResolveOptions): Resolver | |
| 83 | } | |
| 84 | ||
| 85 | export = resolve |
| … | … | @@ -0,0 +1,471 @@ |
| 1 | const resolve = require('bare-module-resolve') | |
| 2 | const { Version } = require('bare-semver') | |
| 3 | const errors = require('./lib/errors') | |
| 4 | ||
| 5 | module.exports = exports = function resolve(specifier, parentURL, opts, readPackage) { | |
| 6 | if (typeof opts === 'function') { | |
| 7 | readPackage = opts | |
| 8 | opts = {} | |
| 9 | } else if (typeof readPackage !== 'function') { | |
| 10 | readPackage = defaultReadPackage | |
| 11 | } | |
| 12 | ||
| 13 | return { | |
| 14 | *[Symbol.iterator]() { | |
| 15 | const generator = exports.addon(specifier, parentURL, opts) | |
| 16 | ||
| 17 | let next = generator.next() | |
| 18 | ||
| 19 | while (next.done !== true) { | |
| 20 | const value = next.value | |
| 21 | ||
| 22 | if (value.package) { | |
| 23 | next = generator.next(readPackage(value.package)) | |
| 24 | } else { | |
| 25 | next = generator.next(yield value.resolution) | |
| 26 | } | |
| 27 | } | |
| 28 | ||
| 29 | return next.value | |
| 30 | }, | |
| 31 | ||
| 32 | async *[Symbol.asyncIterator]() { | |
| 33 | const generator = exports.addon(specifier, parentURL, opts) | |
| 34 | ||
| 35 | let next = generator.next() | |
| 36 | ||
| 37 | while (next.done !== true) { | |
| 38 | const value = next.value | |
| 39 | ||
| 40 | if (value.package) { | |
| 41 | next = generator.next(await readPackage(value.package)) | |
| 42 | } else { | |
| 43 | next = generator.next(yield value.resolution) | |
| 44 | } | |
| 45 | } | |
| 46 | ||
| 47 | return next.value | |
| 48 | } | |
| 49 | } | |
| 50 | } | |
| 51 | ||
| 52 | function defaultReadPackage() { | |
| 53 | return null | |
| 54 | } | |
| 55 | ||
| 56 | const { UNRESOLVED, YIELDED, RESOLVED } = resolve.constants | |
| 57 | ||
| 58 | exports.constants = { | |
| 59 | UNRESOLVED, | |
| 60 | YIELDED, | |
| 61 | RESOLVED | |
| 62 | } | |
| 63 | ||
| 64 | exports.addon = function* (specifier, parentURL, opts = {}) { | |
| 65 | const { resolutions = null } = opts | |
| 66 | ||
| 67 | if (exports.startsWithWindowsDriveLetter(specifier)) { | |
| 68 | specifier = '/' + specifier | |
| 69 | } | |
| 70 | ||
| 71 | let status | |
| 72 | ||
| 73 | if (resolutions) { | |
| 74 | status = yield* resolve.preresolved(specifier, resolutions, parentURL, opts) | |
| 75 | ||
| 76 | if (status) return status | |
| 77 | } | |
| 78 | ||
| 79 | status = yield* exports.url(specifier, parentURL, opts) | |
| 80 | ||
| 81 | if (status) return status | |
| 82 | ||
| 83 | let version = null | |
| 84 | ||
| 85 | const i = specifier.lastIndexOf('@') | |
| 86 | ||
| 87 | if (i > 0) { | |
| 88 | version = specifier.substring(i + 1) | |
| 89 | ||
| 90 | try { | |
| 91 | Version.parse(version) | |
| 92 | ||
| 93 | specifier = specifier.substring(0, i) | |
| 94 | } catch { | |
| 95 | version = null | |
| 96 | } | |
| 97 | } | |
| 98 | ||
| 99 | if ( | |
| 100 | specifier === '.' || | |
| 101 | specifier === '..' || | |
| 102 | specifier[0] === '/' || | |
| 103 | specifier[0] === '\\' || | |
| 104 | specifier.startsWith('./') || | |
| 105 | specifier.startsWith('.\\') || | |
| 106 | specifier.startsWith('../') || | |
| 107 | specifier.startsWith('..\\') | |
| 108 | ) { | |
| 109 | status = yield* exports.file(specifier, parentURL, opts) | |
| 110 | ||
| 111 | if (status === RESOLVED) return status | |
| 112 | ||
| 113 | return yield* exports.directory(specifier, version, parentURL, opts) | |
| 114 | } | |
| 115 | ||
| 116 | return yield* exports.package(specifier, version, parentURL, opts) | |
| 117 | } | |
| 118 | ||
| 119 | exports.url = function* (url, parentURL, opts = {}) { | |
| 120 | let resolution | |
| 121 | try { | |
| 122 | resolution = new URL(url) | |
| 123 | } catch { | |
| 124 | return UNRESOLVED | |
| 125 | } | |
| 126 | ||
| 127 | const resolved = yield { resolution } | |
| 128 | ||
| 129 | return resolved ? RESOLVED : YIELDED | |
| 130 | } | |
| 131 | ||
| 132 | exports.package = function* (packageSpecifier, packageVersion, parentURL, opts = {}) { | |
| 133 | if (packageSpecifier === '') { | |
| 134 | throw errors.INVALID_ADDON_SPECIFIER( | |
| 135 | `Addon specifier '${packageSpecifier}' is not a valid package name` | |
| 136 | ) | |
| 137 | } | |
| 138 | ||
| 139 | let packageName | |
| 140 | ||
| 141 | if (packageSpecifier[0] !== '@') { | |
| 142 | packageName = packageSpecifier.split('/', 1).join() | |
| 143 | } else { | |
| 144 | if (!packageSpecifier.includes('/')) { | |
| 145 | throw errors.INVALID_ADDON_SPECIFIER( | |
| 146 | `Addon specifier '${packageSpecifier}' is not a valid package name` | |
| 147 | ) | |
| 148 | } | |
| 149 | ||
| 150 | packageName = packageSpecifier.split('/', 2).join('/') | |
| 151 | } | |
| 152 | ||
| 153 | if (packageName[0] === '.' || packageName.includes('\\') || packageName.includes('%')) { | |
| 154 | throw errors.INVALID_ADDON_SPECIFIER( | |
| 155 | `Addon specifier '${packageSpecifier}' is not a valid package name` | |
| 156 | ) | |
| 157 | } | |
| 158 | ||
| 159 | const packageSubpath = '.' + packageSpecifier.substring(packageName.length) | |
| 160 | ||
| 161 | const status = yield* exports.packageSelf( | |
| 162 | packageName, | |
| 163 | packageSubpath, | |
| 164 | packageVersion, | |
| 165 | parentURL, | |
| 166 | opts | |
| 167 | ) | |
| 168 | ||
| 169 | if (status) return status | |
| 170 | ||
| 171 | parentURL = new URL(parentURL.href) | |
| 172 | ||
| 173 | do { | |
| 174 | const packageURL = new URL('node_modules/' + packageName + '/', parentURL) | |
| 175 | ||
| 176 | parentURL.pathname = parentURL.pathname.substring(0, parentURL.pathname.lastIndexOf('/')) | |
| 177 | ||
| 178 | const info = yield { package: new URL('package.json', packageURL) } | |
| 179 | ||
| 180 | if (info) { | |
| 181 | return yield* exports.directory(packageSubpath, packageVersion, packageURL, opts) | |
| 182 | } | |
| 183 | } while (parentURL.pathname !== '' && parentURL.pathname !== '/') | |
| 184 | ||
| 185 | return UNRESOLVED | |
| 186 | } | |
| 187 | ||
| 188 | exports.packageSelf = function* ( | |
| 189 | packageName, | |
| 190 | packageSubpath, | |
| 191 | packageVersion, | |
| 192 | parentURL, | |
| 193 | opts = {} | |
| 194 | ) { | |
| 195 | for (const packageURL of resolve.lookupPackageScope(parentURL, opts)) { | |
| 196 | const info = yield { package: packageURL } | |
| 197 | ||
| 198 | if (info) { | |
| 199 | if (info.name === packageName) { | |
| 200 | return yield* exports.directory(packageSubpath, packageVersion, packageURL, opts) | |
| 201 | } | |
| 202 | ||
| 203 | break | |
| 204 | } | |
| 205 | } | |
| 206 | ||
| 207 | return UNRESOLVED | |
| 208 | } | |
| 209 | ||
| 210 | exports.lookupPrebuildsScope = function* lookupPrebuildsScope(url, opts = {}) { | |
| 211 | const scopeURL = new URL(url.href) | |
| 212 | ||
| 213 | do { | |
| 214 | yield new URL('prebuilds/', scopeURL) | |
| 215 | ||
| 216 | scopeURL.pathname = scopeURL.pathname.substring(0, scopeURL.pathname.lastIndexOf('/')) | |
| 217 | ||
| 218 | if ( | |
| 219 | scopeURL.pathname.length === 3 && | |
| 220 | exports.isWindowsDriveLetter(scopeURL.pathname.substring(1)) | |
| 221 | ) { | |
| 222 | break | |
| 223 | } | |
| 224 | } while (scopeURL.pathname !== '' && scopeURL.pathname !== '/') | |
| 225 | } | |
| 226 | ||
| 227 | exports.file = function* (filename, parentURL, opts = {}) { | |
| 228 | if ( | |
| 229 | filename === '.' || | |
| 230 | filename === '..' || | |
| 231 | filename[filename.length - 1] === '/' || | |
| 232 | filename[filename.length - 1] === '\\' | |
| 233 | ) { | |
| 234 | return UNRESOLVED | |
| 235 | } | |
| 236 | ||
| 237 | if (parentURL.protocol === 'file:' && /%2f|%5c/i.test(filename)) { | |
| 238 | throw errors.INVALID_ADDON_SPECIFIER(`Addon specifier '${filename}' is invalid`) | |
| 239 | } | |
| 240 | ||
| 241 | const { extensions = [] } = opts | |
| 242 | ||
| 243 | let status = UNRESOLVED | |
| 244 | ||
| 245 | for (let ext of extensions) { | |
| 246 | if (filename.endsWith(ext)) ext = '' | |
| 247 | ||
| 248 | if (yield { resolution: new URL(filename + ext, parentURL) }) { | |
| 249 | return RESOLVED | |
| 250 | } | |
| 251 | ||
| 252 | status = YIELDED | |
| 253 | } | |
| 254 | ||
| 255 | return status | |
| 256 | } | |
| 257 | ||
| 258 | exports.directory = function* (dirname, version, parentURL, opts = {}) { | |
| 259 | const { | |
| 260 | host = null, // Shorthand for single host resolution | |
| 261 | hosts = host !== null ? [host] : [], | |
| 262 | builtins = [], | |
| 263 | matchedConditions = [] | |
| 264 | } = opts | |
| 265 | ||
| 266 | let directoryURL | |
| 267 | ||
| 268 | if (dirname[dirname.length - 1] === '/' || dirname[dirname.length - 1] === '\\') { | |
| 269 | directoryURL = new URL(dirname, parentURL) | |
| 270 | } else { | |
| 271 | directoryURL = new URL(dirname + '/', parentURL) | |
| 272 | } | |
| 273 | ||
| 274 | const unversioned = version === null | |
| 275 | ||
| 276 | let name = null | |
| 277 | ||
| 278 | const info = yield { package: new URL('package.json', directoryURL) } | |
| 279 | ||
| 280 | if (info) { | |
| 281 | if (typeof info.name === 'string' && info.name !== '') { | |
| 282 | if (info.name.includes('__')) { | |
| 283 | throw errors.INVALID_PACKAGE_NAME(`Package name '${info.name}' is invalid`) | |
| 284 | } | |
| 285 | ||
| 286 | name = info.name.replace(/\//g, '__').replace(/^@/, '') | |
| 287 | } else { | |
| 288 | return UNRESOLVED | |
| 289 | } | |
| 290 | ||
| 291 | if (typeof info.version === 'string' && info.version !== '') { | |
| 292 | if (version !== null && info.version !== version) return UNRESOLVED | |
| 293 | ||
| 294 | version = info.version | |
| 295 | } | |
| 296 | } else { | |
| 297 | return UNRESOLVED | |
| 298 | } | |
| 299 | ||
| 300 | let status | |
| 301 | ||
| 302 | status = yield* resolve.builtinTarget(name, version, builtins, opts) | |
| 303 | ||
| 304 | if (status) return status | |
| 305 | ||
| 306 | for (const prebuildsURL of exports.lookupPrebuildsScope(directoryURL, opts)) { | |
| 307 | status = UNRESOLVED | |
| 308 | ||
| 309 | for (const host of hosts) { | |
| 310 | const conditions = host.split('-') | |
| 311 | ||
| 312 | const universal = supportsUniversalPrebuilds(host) | |
| 313 | ? conditions.with(1, 'universal').join('-') | |
| 314 | : null | |
| 315 | ||
| 316 | matchedConditions.push(...conditions) | |
| 317 | ||
| 318 | if (version !== null) { | |
| 319 | status |= yield* exports.file(host + '/' + name + '@' + version, prebuildsURL, opts) | |
| 320 | ||
| 321 | if (universal) { | |
| 322 | status |= yield* exports.file(universal + '/' + name + '@' + version, prebuildsURL, opts) | |
| 323 | } | |
| 324 | } | |
| 325 | ||
| 326 | if (unversioned) { | |
| 327 | status |= yield* exports.file(host + '/' + name, prebuildsURL, opts) | |
| 328 | ||
| 329 | if (universal) { | |
| 330 | status |= yield* exports.file(universal + '/' + name, prebuildsURL, opts) | |
| 331 | } | |
| 332 | } | |
| 333 | ||
| 334 | for (const _ of conditions) matchedConditions.pop() | |
| 335 | } | |
| 336 | ||
| 337 | if (status === RESOLVED) return status | |
| 338 | } | |
| 339 | ||
| 340 | return yield* exports.linked(name, version, opts) | |
| 341 | } | |
| 342 | ||
| 343 | exports.linked = function* (name, version = null, opts = {}) { | |
| 344 | const { | |
| 345 | linked = true, | |
| 346 | host = null, // Shorthand for single host resolution | |
| 347 | hosts = host !== null ? [host] : [], | |
| 348 | matchedConditions = [] | |
| 349 | } = opts | |
| 350 | ||
| 351 | if (linked === false || hosts.length === 0) return UNRESOLVED | |
| 352 | ||
| 353 | let status = UNRESOLVED | |
| 354 | ||
| 355 | for (const host of hosts) { | |
| 356 | const [platform = null] = host.split('-', 1) | |
| 357 | ||
| 358 | if (platform === null) continue | |
| 359 | ||
| 360 | matchedConditions.push(platform) | |
| 361 | ||
| 362 | status |= yield* platformArtefact(name, version, platform, opts) | |
| 363 | ||
| 364 | matchedConditions.pop() | |
| 365 | } | |
| 366 | ||
| 367 | return status | |
| 368 | } | |
| 369 | ||
| 370 | function* platformArtefact(name, version = null, platform, opts = {}) { | |
| 371 | const { linkedProtocol = 'linked:' } = opts | |
| 372 | ||
| 373 | if (platform === 'darwin' || platform === 'ios') { | |
| 374 | if (version !== null) { | |
| 375 | if ( | |
| 376 | yield { | |
| 377 | resolution: new URL(`${linkedProtocol}${name}.${version}.framework/${name}.${version}`) | |
| 378 | } | |
| 379 | ) { | |
| 380 | return RESOLVED | |
| 381 | } | |
| 382 | ||
| 383 | if (platform === 'darwin') { | |
| 384 | if ( | |
| 385 | yield { | |
| 386 | resolution: new URL(`${linkedProtocol}lib${name}.${version}.dylib`) | |
| 387 | } | |
| 388 | ) { | |
| 389 | return RESOLVED | |
| 390 | } | |
| 391 | } | |
| 392 | } | |
| 393 | ||
| 394 | if ( | |
| 395 | yield { | |
| 396 | resolution: new URL(`${linkedProtocol}${name}.framework/${name}`) | |
| 397 | } | |
| 398 | ) { | |
| 399 | return RESOLVED | |
| 400 | } | |
| 401 | ||
| 402 | if (platform === 'darwin') { | |
| 403 | if ( | |
| 404 | yield { | |
| 405 | resolution: new URL(`${linkedProtocol}lib${name}.dylib`) | |
| 406 | } | |
| 407 | ) { | |
| 408 | return RESOLVED | |
| 409 | } | |
| 410 | } | |
| 411 | ||
| 412 | return YIELDED | |
| 413 | } | |
| 414 | ||
| 415 | if (platform === 'linux' || platform === 'android') { | |
| 416 | if (version !== null) { | |
| 417 | if ( | |
| 418 | yield { | |
| 419 | resolution: new URL(`${linkedProtocol}lib${name}.${version}.so`) | |
| 420 | } | |
| 421 | ) { | |
| 422 | return RESOLVED | |
| 423 | } | |
| 424 | } | |
| 425 | ||
| 426 | if ( | |
| 427 | yield { | |
| 428 | resolution: new URL(`${linkedProtocol}lib${name}.so`) | |
| 429 | } | |
| 430 | ) { | |
| 431 | return RESOLVED | |
| 432 | } | |
| 433 | ||
| 434 | return YIELDED | |
| 435 | } | |
| 436 | ||
| 437 | if (platform === 'win32') { | |
| 438 | if (version !== null) { | |
| 439 | if ( | |
| 440 | yield { | |
| 441 | resolution: new URL(`${linkedProtocol}${name}-${version}.dll`) | |
| 442 | } | |
| 443 | ) { | |
| 444 | return RESOLVED | |
| 445 | } | |
| 446 | } | |
| 447 | ||
| 448 | if ( | |
| 449 | yield { | |
| 450 | resolution: new URL(`${linkedProtocol}${name}.dll`) | |
| 451 | } | |
| 452 | ) { | |
| 453 | return RESOLVED | |
| 454 | } | |
| 455 | } | |
| 456 | ||
| 457 | return UNRESOLVED | |
| 458 | } | |
| 459 | ||
| 460 | exports.isWindowsDriveLetter = resolve.isWindowsDriveLetter | |
| 461 | ||
| 462 | exports.startsWithWindowsDriveLetter = resolve.startsWithWindowsDriveLetter | |
| 463 | ||
| 464 | function supportsUniversalPrebuilds(host) { | |
| 465 | return ( | |
| 466 | host === 'darwin-arm64' || | |
| 467 | host === 'darwin-x64' || | |
| 468 | host === 'ios-arm64-simulator' || | |
| 469 | host === 'ios-x64-simulator' | |
| 470 | ) | |
| 471 | } |
| … | … | @@ -0,0 +1,8 @@ |
| 1 | declare class AddonResolveError extends Error { | |
| 2 | readonly code: string | |
| 3 | ||
| 4 | static INVALID_ADDON_SPECIFIER(msg: string): AddonResolveError | |
| 5 | static INVALID_PACKAGE_NAME(msg: string): AddonResolveError | |
| 6 | } | |
| 7 | ||
| 8 | export = AddonResolveError |
| … | … | @@ -0,0 +1,30 @@ |
| 1 | module.exports = class AddonResolveError extends Error { | |
| 2 | constructor(msg, code, fn = AddonResolveError) { | |
| 3 | super(`${code}: ${msg}`) | |
| 4 | this.code = code | |
| 5 | ||
| 6 | if (Error.captureStackTrace) { | |
| 7 | Error.captureStackTrace(this, fn) | |
| 8 | } | |
| 9 | } | |
| 10 | ||
| 11 | get name() { | |
| 12 | return 'AddonResolveError' | |
| 13 | } | |
| 14 | ||
| 15 | static INVALID_ADDON_SPECIFIER(msg) { | |
| 16 | return new AddonResolveError( | |
| 17 | msg, | |
| 18 | 'INVALID_ADDON_SPECIFIER', | |
| 19 | AddonResolveError.INVALID_ADDON_SPECIFIER | |
| 20 | ) | |
| 21 | } | |
| 22 | ||
| 23 | static INVALID_PACKAGE_NAME(msg) { | |
| 24 | return new AddonResolveError( | |
| 25 | msg, | |
| 26 | 'INVALID_PACKAGE_NAME', | |
| 27 | AddonResolveError.INVALID_PACKAGE_NAME | |
| 28 | ) | |
| 29 | } | |
| 30 | } |
| … | … | @@ -0,0 +1,52 @@ |
| 1 | { | |
| 2 | "name": "bare-addon-resolve", | |
| 3 | "version": "1.10.0", | |
| 4 | "description": "Low-level addon resolution algorithm for Bare", | |
| 5 | "exports": { | |
| 6 | "./package": "./package.json", | |
| 7 | ".": { | |
| 8 | "types": "./index.d.ts", | |
| 9 | "default": "./index.js" | |
| 10 | }, | |
| 11 | "./errors": { | |
| 12 | "types": "./lib/errors.d.ts", | |
| 13 | "default": "./lib/errors.js" | |
| 14 | } | |
| 15 | }, | |
| 16 | "files": [ | |
| 17 | "index.js", | |
| 18 | "index.d.ts", | |
| 19 | "lib" | |
| 20 | ], | |
| 21 | "scripts": { | |
| 22 | "test": "prettier . --check && bare test.js" | |
| 23 | }, | |
| 24 | "repository": { | |
| 25 | "type": "git", | |
| 26 | "url": "git+https://github.com/holepunchto/bare-addon-resolve.git" | |
| 27 | }, | |
| 28 | "author": "Holepunch", | |
| 29 | "license": "Apache-2.0", | |
| 30 | "bugs": { | |
| 31 | "url": "https://github.com/holepunchto/bare-addon-resolve/issues" | |
| 32 | }, | |
| 33 | "homepage": "https://github.com/holepunchto/bare-addon-resolve#readme", | |
| 34 | "dependencies": { | |
| 35 | "bare-module-resolve": "^1.10.0", | |
| 36 | "bare-semver": "^1.0.0" | |
| 37 | }, | |
| 38 | "devDependencies": { | |
| 39 | "bare-url": "^2.1.3", | |
| 40 | "brittle": "^3.2.1", | |
| 41 | "prettier": "^3.3.3", | |
| 42 | "prettier-config-holepunch": "^2.0.0" | |
| 43 | }, | |
| 44 | "peerDependencies": { | |
| 45 | "bare-url": "*" | |
| 46 | }, | |
| 47 | "peerDependenciesMeta": { | |
| 48 | "bare-url": { | |
| 49 | "optional": true | |
| 50 | } | |
| 51 | } | |
| 52 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,19 @@ |
| 1 | # bare-ansi-escapes | |
| 2 | ||
| 3 | Parse and produce ANSI escape sequences. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i bare-ansi-escapes | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const KeyDecoder = require('bare-ansi-escapes/key-decoder') | |
| 13 | ||
| 14 | readableStream.pipe(new KeyDecoder()).on('data', (key) => console.log(key)) | |
| 15 | ``` | |
| 16 | ||
| 17 | ## License | |
| 18 | ||
| 19 | Apache-2.0 |
| … | … | @@ -0,0 +1,50 @@ |
| 1 | export const constants: { ESC: string; CSI: string; SGR: (n: number) => string } | |
| 2 | ||
| 3 | export const cursorHide: string | |
| 4 | export const cursorShow: string | |
| 5 | ||
| 6 | export function cursorUp(n?: number): string | |
| 7 | export function cursorDown(n?: number): string | |
| 8 | export function cursorForward(n?: number): string | |
| 9 | export function cursorBack(n?: number): string | |
| 10 | export function cursorNextLine(n?: number): string | |
| 11 | export function cursorPreviousLine(n?: number): string | |
| 12 | ||
| 13 | export function cursorPosition(column: number, row?: number): string | |
| 14 | ||
| 15 | export const eraseDisplayEnd: string | |
| 16 | export const eraseDisplayStart: string | |
| 17 | export const eraseDisplay: string | |
| 18 | export const eraseLineEnd: string | |
| 19 | export const eraseLineStart: string | |
| 20 | export const eraseLine: string | |
| 21 | ||
| 22 | export function scrollUp(n?: number): string | |
| 23 | export function scrollDown(n?: number): string | |
| 24 | ||
| 25 | export const modifierReset: string | |
| 26 | export const modifierBold: string | |
| 27 | export const modifierDim: string | |
| 28 | export const modifierItalic: string | |
| 29 | export const modifierUnderline: string | |
| 30 | export const modifierNormal: string | |
| 31 | export const modifierNotItalic: string | |
| 32 | export const modifierNotUnderline: string | |
| 33 | ||
| 34 | export const colorBlack: string | |
| 35 | export const colorRed: string | |
| 36 | export const colorGreen: string | |
| 37 | export const colorYellow: string | |
| 38 | export const colorBlue: string | |
| 39 | export const colorMagenta: string | |
| 40 | export const colorCyan: string | |
| 41 | export const colorWhite: string | |
| 42 | export const colorDefault: string | |
| 43 | export const colorBrightBlack: string | |
| 44 | export const colorBrightRed: string | |
| 45 | export const colorBrightGreen: string | |
| 46 | export const colorBrightYellow: string | |
| 47 | export const colorBrightBlue: string | |
| 48 | export const colorBrightMagenta: string | |
| 49 | export const colorBrightCyan: string | |
| 50 | export const colorBrightWhite: string |
| … | … | @@ -0,0 +1,90 @@ |
| 1 | const ESC = '\x1b' | |
| 2 | const CSI = ESC + '[' | |
| 3 | const SGR = (n) => CSI + n + 'm' | |
| 4 | ||
| 5 | exports.constants = { | |
| 6 | ESC, | |
| 7 | CSI, | |
| 8 | SGR | |
| 9 | } | |
| 10 | ||
| 11 | // https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences | |
| 12 | ||
| 13 | exports.cursorHide = CSI + '?25l' | |
| 14 | exports.cursorShow = CSI + '?25h' | |
| 15 | ||
| 16 | exports.cursorUp = function cursorUp(n = 1) { | |
| 17 | return CSI + n + 'A' | |
| 18 | } | |
| 19 | ||
| 20 | exports.cursorDown = function cursorDown(n = 1) { | |
| 21 | return CSI + n + 'B' | |
| 22 | } | |
| 23 | ||
| 24 | exports.cursorForward = function cursorForward(n = 1) { | |
| 25 | return CSI + n + 'C' | |
| 26 | } | |
| 27 | ||
| 28 | exports.cursorBack = function cursorBack(n = 1) { | |
| 29 | return CSI + n + 'D' | |
| 30 | } | |
| 31 | ||
| 32 | exports.cursorNextLine = function cursorNextLine(n = 1) { | |
| 33 | return CSI + n + 'E' | |
| 34 | } | |
| 35 | ||
| 36 | exports.cursorPreviousLine = function cursorPreviousLine(n = 1) { | |
| 37 | return CSI + n + 'F' | |
| 38 | } | |
| 39 | ||
| 40 | exports.cursorPosition = function cursorPosition(column, row = 0) { | |
| 41 | if (row === 0) return CSI + (column + 1) + 'G' | |
| 42 | ||
| 43 | return CSI + (row + 1) + ';' + (column + 1) + 'H' | |
| 44 | } | |
| 45 | ||
| 46 | exports.eraseDisplayEnd = CSI + 'J' | |
| 47 | exports.eraseDisplayStart = CSI + '1J' | |
| 48 | exports.eraseDisplay = CSI + '2J' | |
| 49 | exports.eraseLineEnd = CSI + 'K' | |
| 50 | exports.eraseLineStart = CSI + '1K' | |
| 51 | exports.eraseLine = CSI + '2K' | |
| 52 | ||
| 53 | exports.scrollUp = function scrollUp(n = 1) { | |
| 54 | return CSI + n + 'S' | |
| 55 | } | |
| 56 | ||
| 57 | exports.scrollDown = function scrollDown(n = 1) { | |
| 58 | return CSI + n + 'T' | |
| 59 | } | |
| 60 | ||
| 61 | // https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters | |
| 62 | ||
| 63 | exports.modifierReset = SGR(0) | |
| 64 | exports.modifierBold = SGR(1) | |
| 65 | exports.modifierDim = SGR(2) | |
| 66 | exports.modifierItalic = SGR(3) | |
| 67 | exports.modifierUnderline = SGR(4) | |
| 68 | exports.modifierNormal = SGR(22) | |
| 69 | exports.modifierNotItalic = SGR(23) | |
| 70 | exports.modifierNotUnderline = SGR(24) | |
| 71 | ||
| 72 | // https://en.wikipedia.org/wiki/ANSI_escape_code#Colors | |
| 73 | ||
| 74 | exports.colorBlack = SGR(30) | |
| 75 | exports.colorRed = SGR(31) | |
| 76 | exports.colorGreen = SGR(32) | |
| 77 | exports.colorYellow = SGR(33) | |
| 78 | exports.colorBlue = SGR(34) | |
| 79 | exports.colorMagenta = SGR(35) | |
| 80 | exports.colorCyan = SGR(36) | |
| 81 | exports.colorWhite = SGR(37) | |
| 82 | exports.colorDefault = SGR(39) | |
| 83 | exports.colorBrightBlack = SGR(90) | |
| 84 | exports.colorBrightRed = SGR(91) | |
| 85 | exports.colorBrightGreen = SGR(92) | |
| 86 | exports.colorBrightYellow = SGR(93) | |
| 87 | exports.colorBrightBlue = SGR(94) | |
| 88 | exports.colorBrightMagenta = SGR(95) | |
| 89 | exports.colorBrightCyan = SGR(96) | |
| 90 | exports.colorBrightWhite = SGR(97) |
| … | … | @@ -0,0 +1,44 @@ |
| 1 | import { Transform, TransformEvents } from 'bare-stream' | |
| 2 | import { BufferEncoding } from 'bare-buffer' | |
| 3 | ||
| 4 | interface KeyDecoderOptions { | |
| 5 | encoding?: BufferEncoding | |
| 6 | escapeCodeTimeout?: number | |
| 7 | } | |
| 8 | ||
| 9 | interface KeyDecoderEvents extends TransformEvents { | |
| 10 | data: [key: Key] | |
| 11 | } | |
| 12 | ||
| 13 | interface KeyDecoder<M extends KeyDecoderEvents = KeyDecoderEvents> | |
| 14 | extends Transform<M> { | |
| 15 | readonly encoding: BufferEncoding | |
| 16 | } | |
| 17 | ||
| 18 | declare class KeyDecoder { | |
| 19 | constructor(opts?: KeyDecoderOptions) | |
| 20 | } | |
| 21 | ||
| 22 | interface Key { | |
| 23 | readonly name: string | |
| 24 | readonly sequence: string | |
| 25 | readonly ctrl: boolean | |
| 26 | readonly meta: boolean | |
| 27 | readonly shift: boolean | |
| 28 | } | |
| 29 | ||
| 30 | declare class Key { | |
| 31 | constructor( | |
| 32 | name: string | number, | |
| 33 | sequence: string, | |
| 34 | ctrl: boolean, | |
| 35 | meta: boolean, | |
| 36 | shift: boolean | |
| 37 | ) | |
| 38 | } | |
| 39 | ||
| 40 | declare namespace KeyDecoder { | |
| 41 | export type { KeyDecoderOptions, KeyDecoderEvents, Key } | |
| 42 | } | |
| 43 | ||
| 44 | export = KeyDecoder |
| … | … | @@ -0,0 +1,480 @@ |
| 1 | const { Transform } = require('bare-stream') | |
| 2 | ||
| 3 | const { | |
| 4 | constants: { ESC } | |
| 5 | } = require('.') | |
| 6 | ||
| 7 | // https://en.wikipedia.org/wiki/ANSI_escape_code#Terminal_input_sequences | |
| 8 | ||
| 9 | module.exports = class KeyDecoder extends Transform { | |
| 10 | constructor(opts = {}) { | |
| 11 | const { encoding = 'utf8', escapeCodeTimeout = 500 } = opts | |
| 12 | ||
| 13 | super() | |
| 14 | ||
| 15 | this.encoding = encoding | |
| 16 | ||
| 17 | this._escapeDelay = escapeCodeTimeout | |
| 18 | this._escapeTimer = null | |
| 19 | ||
| 20 | this._parser = parseKeys(this) | |
| 21 | this._parser.next() | |
| 22 | } | |
| 23 | ||
| 24 | _transform(data, encoding, cb) { | |
| 25 | clearTimeout(this._escapeTimer) | |
| 26 | ||
| 27 | if (data[0] > 127 && data.length === 1) { | |
| 28 | data[0] -= 128 | |
| 29 | data = ESC + data.toString(this.encoding) | |
| 30 | } else { | |
| 31 | data = data.toString(this.encoding) | |
| 32 | } | |
| 33 | ||
| 34 | let n = 0 | |
| 35 | ||
| 36 | for (const c of data) { | |
| 37 | n += c.length | |
| 38 | ||
| 39 | this._parser.next(c) | |
| 40 | ||
| 41 | if (n === data.length && c === ESC) { | |
| 42 | this._escapeTimer = setTimeout( | |
| 43 | this._escape.bind(this), | |
| 44 | this._escapeDelay | |
| 45 | ) | |
| 46 | } | |
| 47 | } | |
| 48 | ||
| 49 | cb(null) | |
| 50 | } | |
| 51 | ||
| 52 | _escape() { | |
| 53 | this._parser.next('') | |
| 54 | } | |
| 55 | } | |
| 56 | ||
| 57 | class Key { | |
| 58 | constructor(name, sequence, ctrl, meta, shift) { | |
| 59 | this.name = typeof name === 'number' ? String.fromCharCode(name) : name | |
| 60 | this.sequence = sequence | |
| 61 | this.ctrl = ctrl | |
| 62 | this.meta = meta | |
| 63 | this.shift = shift | |
| 64 | } | |
| 65 | } | |
| 66 | ||
| 67 | function charLengthAt(str, i) { | |
| 68 | if (str.length <= i) { | |
| 69 | return 1 | |
| 70 | } | |
| 71 | ||
| 72 | return str.codePointAt(i) >= 0x10000 ? 2 : 1 | |
| 73 | } | |
| 74 | ||
| 75 | function* parseKeys(stream) { | |
| 76 | while (true) { | |
| 77 | let c = yield | |
| 78 | let s = c | |
| 79 | let escaped = false | |
| 80 | ||
| 81 | let name = null | |
| 82 | let ctrl = false | |
| 83 | let meta = false | |
| 84 | let shift = false | |
| 85 | ||
| 86 | if (c === ESC) { | |
| 87 | escaped = true | |
| 88 | ||
| 89 | s += c = yield | |
| 90 | ||
| 91 | if (c === ESC) { | |
| 92 | s += c = yield | |
| 93 | } | |
| 94 | } | |
| 95 | ||
| 96 | if (escaped && (c === 'O' || c === '[')) { | |
| 97 | let code = c | |
| 98 | let modifier = 0 | |
| 99 | ||
| 100 | if (c === 'O') { | |
| 101 | s += c = yield | |
| 102 | ||
| 103 | if (c >= '0' && c <= '9') { | |
| 104 | modifier = (c >> 0) - 1 | |
| 105 | s += c = yield | |
| 106 | } | |
| 107 | ||
| 108 | code += c | |
| 109 | } else if (c === '[') { | |
| 110 | s += c = yield | |
| 111 | ||
| 112 | if (c === '[') { | |
| 113 | code += c | |
| 114 | s += c = yield | |
| 115 | } | |
| 116 | ||
| 117 | const start = s.length - 1 | |
| 118 | ||
| 119 | if (c >= '0' && c <= '9') { | |
| 120 | s += c = yield | |
| 121 | ||
| 122 | if (c >= '0' && c <= '9') { | |
| 123 | s += c = yield | |
| 124 | ||
| 125 | if (c >= '0' && c <= '9') { | |
| 126 | s += c = yield | |
| 127 | } | |
| 128 | } | |
| 129 | } | |
| 130 | ||
| 131 | if (c === ';') { | |
| 132 | s += c = yield | |
| 133 | ||
| 134 | if (c >= '0' && c <= '9') { | |
| 135 | s += yield | |
| 136 | } | |
| 137 | } | |
| 138 | ||
| 139 | const cmd = s.slice(start) | |
| 140 | ||
| 141 | let match | |
| 142 | ||
| 143 | if ((match = /^(?:(\d\d?)(?:;(\d))?([~^$])|(\d{3}~))$/.exec(cmd))) { | |
| 144 | if (match[4]) { | |
| 145 | code += match[4] | |
| 146 | } else { | |
| 147 | code += match[1] + match[3] | |
| 148 | modifier = (match[2] || 1) - 1 | |
| 149 | } | |
| 150 | } else if ((match = /^((\d;)?(\d))?([A-Za-z])$/.exec(cmd))) { | |
| 151 | code += match[4] | |
| 152 | modifier = (match[3] || 1) - 1 | |
| 153 | } else { | |
| 154 | code += cmd | |
| 155 | } | |
| 156 | } | |
| 157 | ||
| 158 | ctrl = !!(modifier & 4) | |
| 159 | meta = !!(modifier & 10) | |
| 160 | shift = !!(modifier & 1) | |
| 161 | ||
| 162 | switch (code) { | |
| 163 | /* xterm/gnome ESC [ letter (with modifier) */ | |
| 164 | case '[P': | |
| 165 | name = 'f1' | |
| 166 | break | |
| 167 | case '[Q': | |
| 168 | name = 'f2' | |
| 169 | break | |
| 170 | case '[R': | |
| 171 | name = 'f3' | |
| 172 | break | |
| 173 | case '[S': | |
| 174 | name = 'f4' | |
| 175 | break | |
| 176 | ||
| 177 | /* xterm/gnome ESC O letter (without modifier) */ | |
| 178 | case 'OP': | |
| 179 | name = 'f1' | |
| 180 | break | |
| 181 | case 'OQ': | |
| 182 | name = 'f2' | |
| 183 | break | |
| 184 | case 'OR': | |
| 185 | name = 'f3' | |
| 186 | break | |
| 187 | case 'OS': | |
| 188 | name = 'f4' | |
| 189 | break | |
| 190 | ||
| 191 | /* xterm/rxvt ESC [ number ~ */ | |
| 192 | case '[11~': | |
| 193 | name = 'f1' | |
| 194 | break | |
| 195 | case '[12~': | |
| 196 | name = 'f2' | |
| 197 | break | |
| 198 | case '[13~': | |
| 199 | name = 'f3' | |
| 200 | break | |
| 201 | case '[14~': | |
| 202 | name = 'f4' | |
| 203 | break | |
| 204 | ||
| 205 | /* paste bracket mode */ | |
| 206 | case '[200~': | |
| 207 | name = 'paste-start' | |
| 208 | break | |
| 209 | case '[201~': | |
| 210 | name = 'paste-end' | |
| 211 | break | |
| 212 | ||
| 213 | /* from Cygwin and used in libuv */ | |
| 214 | case '[[A': | |
| 215 | name = 'f1' | |
| 216 | break | |
| 217 | case '[[B': | |
| 218 | name = 'f2' | |
| 219 | break | |
| 220 | case '[[C': | |
| 221 | name = 'f3' | |
| 222 | break | |
| 223 | case '[[D': | |
| 224 | name = 'f4' | |
| 225 | break | |
| 226 | case '[[E': | |
| 227 | name = 'f5' | |
| 228 | break | |
| 229 | ||
| 230 | /* common */ | |
| 231 | case '[15~': | |
| 232 | name = 'f5' | |
| 233 | break | |
| 234 | case '[17~': | |
| 235 | name = 'f6' | |
| 236 | break | |
| 237 | case '[18~': | |
| 238 | name = 'f7' | |
| 239 | break | |
| 240 | case '[19~': | |
| 241 | name = 'f8' | |
| 242 | break | |
| 243 | case '[20~': | |
| 244 | name = 'f9' | |
| 245 | break | |
| 246 | case '[21~': | |
| 247 | name = 'f10' | |
| 248 | break | |
| 249 | case '[23~': | |
| 250 | name = 'f11' | |
| 251 | break | |
| 252 | case '[24~': | |
| 253 | name = 'f12' | |
| 254 | break | |
| 255 | ||
| 256 | /* xterm ESC [ letter */ | |
| 257 | case '[A': | |
| 258 | name = 'up' | |
| 259 | break | |
| 260 | case '[B': | |
| 261 | name = 'down' | |
| 262 | break | |
| 263 | case '[C': | |
| 264 | name = 'right' | |
| 265 | break | |
| 266 | case '[D': | |
| 267 | name = 'left' | |
| 268 | break | |
| 269 | case '[E': | |
| 270 | name = 'clear' | |
| 271 | break | |
| 272 | case '[F': | |
| 273 | name = 'end' | |
| 274 | break | |
| 275 | case '[H': | |
| 276 | name = 'home' | |
| 277 | break | |
| 278 | ||
| 279 | /* xterm/gnome ESC O letter */ | |
| 280 | case 'OA': | |
| 281 | name = 'up' | |
| 282 | break | |
| 283 | case 'OB': | |
| 284 | name = 'down' | |
| 285 | break | |
| 286 | case 'OC': | |
| 287 | name = 'right' | |
| 288 | break | |
| 289 | case 'OD': | |
| 290 | name = 'left' | |
| 291 | break | |
| 292 | case 'OE': | |
| 293 | name = 'clear' | |
| 294 | break | |
| 295 | case 'OF': | |
| 296 | name = 'end' | |
| 297 | break | |
| 298 | case 'OH': | |
| 299 | name = 'home' | |
| 300 | break | |
| 301 | ||
| 302 | /* xterm/rxvt ESC [ number ~ */ | |
| 303 | case '[1~': | |
| 304 | name = 'home' | |
| 305 | break | |
| 306 | case '[2~': | |
| 307 | name = 'insert' | |
| 308 | break | |
| 309 | case '[3~': | |
| 310 | name = 'delete' | |
| 311 | break | |
| 312 | case '[4~': | |
| 313 | name = 'end' | |
| 314 | break | |
| 315 | case '[5~': | |
| 316 | name = 'pageup' | |
| 317 | break | |
| 318 | case '[6~': | |
| 319 | name = 'pagedown' | |
| 320 | break | |
| 321 | ||
| 322 | /* putty */ | |
| 323 | case '[[5~': | |
| 324 | name = 'pageup' | |
| 325 | break | |
| 326 | case '[[6~': | |
| 327 | name = 'pagedown' | |
| 328 | break | |
| 329 | ||
| 330 | /* rxvt */ | |
| 331 | case '[7~': | |
| 332 | name = 'home' | |
| 333 | break | |
| 334 | case '[8~': | |
| 335 | name = 'end' | |
| 336 | break | |
| 337 | ||
| 338 | /* rxvt keys with modifiers */ | |
| 339 | case '[a': | |
| 340 | name = 'up' | |
| 341 | shift = true | |
| 342 | break | |
| 343 | case '[b': | |
| 344 | name = 'down' | |
| 345 | shift = true | |
| 346 | break | |
| 347 | case '[c': | |
| 348 | name = 'right' | |
| 349 | shift = true | |
| 350 | break | |
| 351 | case '[d': | |
| 352 | name = 'left' | |
| 353 | shift = true | |
| 354 | break | |
| 355 | case '[e': | |
| 356 | name = 'clear' | |
| 357 | shift = true | |
| 358 | break | |
| 359 | ||
| 360 | case '[2$': | |
| 361 | name = 'insert' | |
| 362 | shift = true | |
| 363 | break | |
| 364 | case '[3$': | |
| 365 | name = 'delete' | |
| 366 | shift = true | |
| 367 | break | |
| 368 | case '[5$': | |
| 369 | name = 'pageup' | |
| 370 | shift = true | |
| 371 | break | |
| 372 | case '[6$': | |
| 373 | name = 'pagedown' | |
| 374 | shift = true | |
| 375 | break | |
| 376 | case '[7$': | |
| 377 | name = 'home' | |
| 378 | shift = true | |
| 379 | break | |
| 380 | case '[8$': | |
| 381 | name = 'end' | |
| 382 | shift = true | |
| 383 | break | |
| 384 | ||
| 385 | case 'Oa': | |
| 386 | name = 'up' | |
| 387 | ctrl = true | |
| 388 | break | |
| 389 | case 'Ob': | |
| 390 | name = 'down' | |
| 391 | ctrl = true | |
| 392 | break | |
| 393 | case 'Oc': | |
| 394 | name = 'right' | |
| 395 | ctrl = true | |
| 396 | break | |
| 397 | case 'Od': | |
| 398 | name = 'left' | |
| 399 | ctrl = true | |
| 400 | break | |
| 401 | case 'Oe': | |
| 402 | name = 'clear' | |
| 403 | ctrl = true | |
| 404 | break | |
| 405 | ||
| 406 | case '[2^': | |
| 407 | name = 'insert' | |
| 408 | ctrl = true | |
| 409 | break | |
| 410 | case '[3^': | |
| 411 | name = 'delete' | |
| 412 | ctrl = true | |
| 413 | break | |
| 414 | case '[5^': | |
| 415 | name = 'pageup' | |
| 416 | ctrl = true | |
| 417 | break | |
| 418 | case '[6^': | |
| 419 | name = 'pagedown' | |
| 420 | ctrl = true | |
| 421 | break | |
| 422 | case '[7^': | |
| 423 | name = 'home' | |
| 424 | ctrl = true | |
| 425 | break | |
| 426 | case '[8^': | |
| 427 | name = 'end' | |
| 428 | ctrl = true | |
| 429 | break | |
| 430 | ||
| 431 | case '[Z': | |
| 432 | name = 'tab' | |
| 433 | shift = true | |
| 434 | break | |
| 435 | default: | |
| 436 | name = 'undefined' | |
| 437 | break | |
| 438 | } | |
| 439 | } else if (c === '\r') { | |
| 440 | name = 'return' | |
| 441 | meta = escaped | |
| 442 | } else if (c === '\n') { | |
| 443 | name = 'linefeed' | |
| 444 | meta = escaped | |
| 445 | } else if (c === '\t') { | |
| 446 | name = 'tab' | |
| 447 | meta = escaped | |
| 448 | } else if (c === '\b' || c === '\x7f') { | |
| 449 | name = 'backspace' | |
| 450 | meta = escaped | |
| 451 | } else if (c === ESC) { | |
| 452 | name = 'escape' | |
| 453 | meta = escaped | |
| 454 | } else if (c === ' ') { | |
| 455 | name = 'space' | |
| 456 | meta = escaped | |
| 457 | } else if (!escaped && c <= '\x1a') { | |
| 458 | name = String.fromCharCode(c.charCodeAt(0) + 'a'.charCodeAt(0) - 1) | |
| 459 | ctrl = true | |
| 460 | } else if (/^[0-9A-Za-z]$/.exec(c) !== null) { | |
| 461 | name = c.toLowerCase() | |
| 462 | shift = /^[A-Z]$/.exec(c) !== null | |
| 463 | meta = escaped | |
| 464 | } else if (escaped) { | |
| 465 | name = c.length ? null : 'escape' | |
| 466 | meta = true | |
| 467 | } | |
| 468 | ||
| 469 | const sequence = s | |
| 470 | ||
| 471 | if ( | |
| 472 | s.length > 0 && | |
| 473 | (name !== null || escaped || charLengthAt(s, 0) === s.length) | |
| 474 | ) { | |
| 475 | stream.push( | |
| 476 | new Key(name === null ? sequence : name, sequence, ctrl, meta, shift) | |
| 477 | ) | |
| 478 | } | |
| 479 | } | |
| 480 | } |
| … | … | @@ -0,0 +1,52 @@ |
| 1 | { | |
| 2 | "name": "bare-ansi-escapes", | |
| 3 | "version": "2.2.3", | |
| 4 | "description": "Parse and produce ANSI escape sequences", | |
| 5 | "exports": { | |
| 6 | "./package": "./package.json", | |
| 7 | ".": { | |
| 8 | "types": "./index.d.ts", | |
| 9 | "default": "./index.js" | |
| 10 | }, | |
| 11 | "./key-decoder": { | |
| 12 | "types": "./key-decoder.d.ts", | |
| 13 | "default": "./key-decoder.js" | |
| 14 | } | |
| 15 | }, | |
| 16 | "files": [ | |
| 17 | "index.js", | |
| 18 | "index.d.ts", | |
| 19 | "key-decoder.js", | |
| 20 | "key-decoder.d.ts" | |
| 21 | ], | |
| 22 | "scripts": { | |
| 23 | "test": "prettier . --check && bare test.js" | |
| 24 | }, | |
| 25 | "repository": { | |
| 26 | "type": "git", | |
| 27 | "url": "git+https://github.com/holepunchto/bare-ansi-escapes.git" | |
| 28 | }, | |
| 29 | "author": "Holepunch", | |
| 30 | "license": "Apache-2.0", | |
| 31 | "bugs": { | |
| 32 | "url": "https://github.com/holepunchto/bare-ansi-escapes/issues" | |
| 33 | }, | |
| 34 | "homepage": "https://github.com/holepunchto/bare-ansi-escapes#readme", | |
| 35 | "dependencies": { | |
| 36 | "bare-stream": "^2.6.5" | |
| 37 | }, | |
| 38 | "devDependencies": { | |
| 39 | "bare-buffer": "^3.0.2", | |
| 40 | "brittle": "^3.3.2", | |
| 41 | "prettier": "^3.4.2", | |
| 42 | "prettier-config-standard": "^7.0.0" | |
| 43 | }, | |
| 44 | "peerDependencies": { | |
| 45 | "bare-buffer": "*" | |
| 46 | }, | |
| 47 | "peerDependenciesMeta": { | |
| 48 | "bare-buffer": { | |
| 49 | "optional": true | |
| 50 | } | |
| 51 | } | |
| 52 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,11 @@ |
| 1 | # bare-assert | |
| 2 | ||
| 3 | Assertion library for JavaScript. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i bare-assert | |
| 7 | ``` | |
| 8 | ||
| 9 | ## License | |
| 10 | ||
| 11 | Apache-2.0 |
| … | … | @@ -0,0 +1,44 @@ |
| 1 | declare function assert(value: any, message?: string | Error): void | |
| 2 | ||
| 3 | declare namespace assert { | |
| 4 | export class AssertionError extends Error { | |
| 5 | constructor(opts?: { | |
| 6 | message?: string | |
| 7 | actual?: any | |
| 8 | expected?: any | |
| 9 | operator?: string | |
| 10 | }) | |
| 11 | ||
| 12 | actual?: any | |
| 13 | expected?: any | |
| 14 | operator?: string | |
| 15 | } | |
| 16 | ||
| 17 | export function ok(value: any, message?: string | Error): void | |
| 18 | ||
| 19 | export function equal( | |
| 20 | actual: any, | |
| 21 | expected: any, | |
| 22 | message?: string | Error | |
| 23 | ): void | |
| 24 | ||
| 25 | export function notEqual( | |
| 26 | actual: any, | |
| 27 | expected: any, | |
| 28 | message?: string | Error | |
| 29 | ): void | |
| 30 | ||
| 31 | export function strictEqual( | |
| 32 | actual: any, | |
| 33 | expected: any, | |
| 34 | message?: string | Error | |
| 35 | ): void | |
| 36 | ||
| 37 | export function notStrictEqual( | |
| 38 | actual: any, | |
| 39 | expected: any, | |
| 40 | message?: string | Error | |
| 41 | ): void | |
| 42 | } | |
| 43 | ||
| 44 | export = assert |
| … | … | @@ -0,0 +1,95 @@ |
| 1 | const inspect = require('bare-inspect') | |
| 2 | ||
| 3 | class AssertionError extends Error { | |
| 4 | constructor(opts = {}) { | |
| 5 | let { message = null, actual, expected, operator } = opts | |
| 6 | ||
| 7 | if (message === null) { | |
| 8 | message = `${inspect(actual)} ${operator} ${inspect(expected)}` | |
| 9 | } | |
| 10 | ||
| 11 | super(message) | |
| 12 | ||
| 13 | this.actual = actual | |
| 14 | this.expected = expected | |
| 15 | this.operator = operator | |
| 16 | } | |
| 17 | ||
| 18 | get name() { | |
| 19 | return 'AssertionError' | |
| 20 | } | |
| 21 | ||
| 22 | get code() { | |
| 23 | 'ASSERTION' | |
| 24 | } | |
| 25 | } | |
| 26 | ||
| 27 | function assertFail(opts, fn) { | |
| 28 | if (opts.message instanceof Error) throw opts.message | |
| 29 | ||
| 30 | const err = new AssertionError(opts) | |
| 31 | ||
| 32 | if (Error.captureStackTrace) Error.captureStackTrace(err, fn) | |
| 33 | ||
| 34 | throw err | |
| 35 | } | |
| 36 | ||
| 37 | module.exports = exports = function assert(actual, message) { | |
| 38 | if (actual) return | |
| 39 | ||
| 40 | assertFail({ message, actual, expected: true, operator: '==' }, assert) | |
| 41 | } | |
| 42 | ||
| 43 | exports.AssertionError = AssertionError | |
| 44 | ||
| 45 | exports.fail = function fail(message) { | |
| 46 | if (message === undefined) message = 'Failed' | |
| 47 | ||
| 48 | assertFail({ message, operator: 'fail' }, fail) | |
| 49 | } | |
| 50 | ||
| 51 | exports.ok = function ok(actual, message) { | |
| 52 | if (actual) return | |
| 53 | ||
| 54 | assertFail({ message, actual, expected: true, operator: '==' }, ok) | |
| 55 | } | |
| 56 | ||
| 57 | exports.notOk = function ok(actual, message) { | |
| 58 | if (!actual) return | |
| 59 | ||
| 60 | assertFail({ message, actual, expected: false, operator: '==' }, ok) | |
| 61 | } | |
| 62 | ||
| 63 | exports.equal = function equal(actual, expected, message) { | |
| 64 | if (actual == expected || (actual !== actual && expected !== expected)) { | |
| 65 | return | |
| 66 | } | |
| 67 | ||
| 68 | assertFail({ message, actual, expected, operator: '==' }, equal) | |
| 69 | } | |
| 70 | ||
| 71 | exports.notEqual = function notEqual(actual, expected, message) { | |
| 72 | if (actual != expected && (actual === actual || expected === expected)) { | |
| 73 | return | |
| 74 | } | |
| 75 | ||
| 76 | assertFail({ message, actual, expected, operator: '!=' }, notEqual) | |
| 77 | } | |
| 78 | ||
| 79 | exports.strictEqual = function strictEqual(actual, expected, message) { | |
| 80 | if (Object.is(actual, expected)) return | |
| 81 | ||
| 82 | assertFail( | |
| 83 | { message, actual, expected, operator: 'strictEqual' }, | |
| 84 | strictEqual | |
| 85 | ) | |
| 86 | } | |
| 87 | ||
| 88 | exports.notStrictEqual = function notStrictEqual(actual, expected, message) { | |
| 89 | if (!Object.is(actual, expected)) return | |
| 90 | ||
| 91 | assertFail( | |
| 92 | { message, actual, expected, operator: 'notStrictEqual' }, | |
| 93 | notStrictEqual | |
| 94 | ) | |
| 95 | } |
| … | … | @@ -0,0 +1,37 @@ |
| 1 | { | |
| 2 | "name": "bare-assert", | |
| 3 | "version": "1.2.0", | |
| 4 | "description": "Assertion library for JavaScript", | |
| 5 | "exports": { | |
| 6 | ".": { | |
| 7 | "types": "./index.d.ts", | |
| 8 | "default": "./index.js" | |
| 9 | }, | |
| 10 | "./package": "./package.json" | |
| 11 | }, | |
| 12 | "files": [ | |
| 13 | "index.js", | |
| 14 | "index.d.ts" | |
| 15 | ], | |
| 16 | "scripts": { | |
| 17 | "test": "prettier . --check && bare test.js" | |
| 18 | }, | |
| 19 | "repository": { | |
| 20 | "type": "git", | |
| 21 | "url": "git+https://github.com/holepunchto/bare-assert.git" | |
| 22 | }, | |
| 23 | "author": "Holepunch", | |
| 24 | "license": "Apache-2.0", | |
| 25 | "bugs": { | |
| 26 | "url": "https://github.com/holepunchto/bare-assert/issues" | |
| 27 | }, | |
| 28 | "homepage": "https://github.com/holepunchto/bare-assert#readme", | |
| 29 | "dependencies": { | |
| 30 | "bare-inspect": "^3.1.2" | |
| 31 | }, | |
| 32 | "devDependencies": { | |
| 33 | "brittle": "^3.1.1", | |
| 34 | "prettier": "^3.4.2", | |
| 35 | "prettier-config-standard": "^7.0.0" | |
| 36 | } | |
| 37 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,25 @@ |
| 1 | # bare-events | |
| 2 | ||
| 3 | Event emitters for JavaScript. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install bare-events | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const EventEmitter = require('bare-events') | |
| 13 | ||
| 14 | const e = new EventEmitter() | |
| 15 | ||
| 16 | e.on('hello', function (data) { | |
| 17 | console.log(data) | |
| 18 | }) | |
| 19 | ||
| 20 | e.emit('hello', 'world') | |
| 21 | ``` | |
| 22 | ||
| 23 | ## License | |
| 24 | ||
| 25 | Apache-2.0 |
| … | … | @@ -0,0 +1,15 @@ |
| 1 | import * as events from './web' | |
| 2 | ||
| 3 | type EventConstructor = typeof events.Event | |
| 4 | type CustomEventConstructor = typeof events.CustomEvent | |
| 5 | type EventTargetConstructor = typeof events.EventTarget | |
| 6 | ||
| 7 | declare global { | |
| 8 | type Event = events.Event | |
| 9 | type CustomEvent<T = any> = events.CustomEvent<T> | |
| 10 | type EventTarget = events.EventTarget | |
| 11 | ||
| 12 | const Event: EventConstructor | |
| 13 | const CustomEvent: CustomEventConstructor | |
| 14 | const EventTarget: EventTargetConstructor | |
| 15 | } |
| … | … | @@ -0,0 +1,5 @@ |
| 1 | const events = require('./web') | |
| 2 | ||
| 3 | global.Event = events.Event | |
| 4 | global.CustomEvent = events.CustomEvent | |
| 5 | global.EventTarget = events.EventTarget |
| … | … | @@ -0,0 +1,81 @@ |
| 1 | import { AbortSignal } from 'bare-abort-controller' | |
| 2 | ||
| 3 | interface EventMap { | |
| 4 | [event: string | symbol]: unknown[] | |
| 5 | } | |
| 6 | ||
| 7 | interface EventHandler<in A extends unknown[] = unknown[], out R = unknown> { | |
| 8 | (...args: A): R | |
| 9 | } | |
| 10 | ||
| 11 | declare class EventEmitterError extends Error { | |
| 12 | static OPERATION_ABORTED(cause: Error, msg?: string): EventEmitterError | |
| 13 | static UNHANDLED_ERROR(cause: Error, msg?: string): EventEmitterError | |
| 14 | } | |
| 15 | ||
| 16 | interface EventEmitter<in out M extends EventMap = EventMap> { | |
| 17 | addListener<E extends keyof M, R>(name: E, fn: EventHandler<M[E], R>): this | |
| 18 | ||
| 19 | addOnceListener<E extends keyof M, R>(name: E, fn: EventHandler<M[E], R>): this | |
| 20 | ||
| 21 | prependListener<E extends keyof M, R>(name: E, fn: EventHandler<M[E], R>): this | |
| 22 | ||
| 23 | prependOnceListener<E extends keyof M, R>(name: E, fn: EventHandler<M[E], R>): this | |
| 24 | ||
| 25 | removeListener<E extends keyof M, R>(name: E, fn: EventHandler<M[E], R>): this | |
| 26 | ||
| 27 | removeAllListeners<E extends keyof M>(name?: E): this | |
| 28 | ||
| 29 | on<E extends keyof M, R>(name: E, fn: EventHandler<M[E], R>): this | |
| 30 | ||
| 31 | once<E extends keyof M, R>(name: E, fn: EventHandler<M[E], R>): this | |
| 32 | ||
| 33 | off<E extends keyof M, R>(name: E, fn: EventHandler<M[E], R>): this | |
| 34 | ||
| 35 | emit<E extends keyof M>(name: E, ...args: M[E]): boolean | |
| 36 | ||
| 37 | listeners<E extends keyof M, R>(name: E): EventHandler<M[E], R> | |
| 38 | ||
| 39 | listenerCount<E extends keyof M>(name: E): number | |
| 40 | ||
| 41 | getMaxListeners(): number | |
| 42 | setMaxListeners(n: number): void | |
| 43 | } | |
| 44 | ||
| 45 | declare class EventEmitter<in out M extends EventMap = EventMap> {} | |
| 46 | ||
| 47 | declare namespace EventEmitter { | |
| 48 | export function on<M extends EventMap, E extends keyof M>( | |
| 49 | emitter: EventEmitter<M>, | |
| 50 | name: E, | |
| 51 | opts?: { signal?: AbortSignal } | |
| 52 | ): AsyncIterableIterator<M[E]> | |
| 53 | ||
| 54 | export function once<M extends EventMap, E extends keyof M>( | |
| 55 | emitter: EventEmitter<M>, | |
| 56 | name: E, | |
| 57 | opts?: { signal?: AbortSignal } | |
| 58 | ): Promise<M[E]> | |
| 59 | ||
| 60 | export function forward<F extends EventMap, E extends keyof F, T extends Pick<F, E>>( | |
| 61 | from: EventEmitter<F>, | |
| 62 | to: EventEmitter<T>, | |
| 63 | names: E | E[], | |
| 64 | opts?: { emit?: (name: E, ...args: T[E]) => void } | |
| 65 | ): void | |
| 66 | ||
| 67 | export function listenerCount<M extends EventMap, E extends keyof M>( | |
| 68 | emitter: EventEmitter<M>, | |
| 69 | name: E | |
| 70 | ): number | |
| 71 | ||
| 72 | export function getMaxListeners(emitter: EventEmitter): number | |
| 73 | ||
| 74 | export function setMaxListeners(n: number, ...emitters: EventEmitter[]): void | |
| 75 | ||
| 76 | export let defaultMaxListeners: number | |
| 77 | ||
| 78 | export { EventEmitter, EventEmitterError as errors, EventMap, EventHandler } | |
| 79 | } | |
| 80 | ||
| 81 | export = EventEmitter |
| … | … | @@ -0,0 +1,367 @@ |
| 1 | const errors = require('./lib/errors') | |
| 2 | ||
| 3 | class EventListener { | |
| 4 | constructor() { | |
| 5 | this.list = [] | |
| 6 | this.count = 0 | |
| 7 | } | |
| 8 | ||
| 9 | append(ctx, name, fn, once) { | |
| 10 | this.count++ | |
| 11 | ctx.emit('newListener', name, fn) // Emit BEFORE adding | |
| 12 | this.list.push([fn, once]) | |
| 13 | } | |
| 14 | ||
| 15 | prepend(ctx, name, fn, once) { | |
| 16 | this.count++ | |
| 17 | ctx.emit('newListener', name, fn) // Emit BEFORE adding | |
| 18 | this.list.unshift([fn, once]) | |
| 19 | } | |
| 20 | ||
| 21 | remove(ctx, name, fn) { | |
| 22 | for (let i = 0, n = this.list.length; i < n; i++) { | |
| 23 | const l = this.list[i] | |
| 24 | ||
| 25 | if (l[0] === fn) { | |
| 26 | this.list.splice(i, 1) | |
| 27 | ||
| 28 | if (this.count === 1) delete ctx._events[name] | |
| 29 | ||
| 30 | ctx.emit('removeListener', name, fn) // Emit AFTER removing | |
| 31 | ||
| 32 | this.count-- | |
| 33 | return | |
| 34 | } | |
| 35 | } | |
| 36 | } | |
| 37 | ||
| 38 | removeAll(ctx, name) { | |
| 39 | const list = [...this.list] | |
| 40 | this.list = [] | |
| 41 | ||
| 42 | if (this.count === list.length) delete ctx._events[name] | |
| 43 | ||
| 44 | for (let i = list.length - 1; i >= 0; i--) { | |
| 45 | ctx.emit('removeListener', name, list[i][0]) // Emit AFTER removing | |
| 46 | } | |
| 47 | ||
| 48 | this.count -= list.length | |
| 49 | } | |
| 50 | ||
| 51 | emit(ctx, name, ...args) { | |
| 52 | const list = [...this.list] | |
| 53 | ||
| 54 | for (let i = 0, n = list.length; i < n; i++) { | |
| 55 | const l = list[i] | |
| 56 | ||
| 57 | if (l[1] === true) this.remove(ctx, name, l[0]) | |
| 58 | ||
| 59 | Reflect.apply(l[0], ctx, args) | |
| 60 | } | |
| 61 | ||
| 62 | return list.length > 0 | |
| 63 | } | |
| 64 | } | |
| 65 | ||
| 66 | function appendListener(ctx, name, fn, once) { | |
| 67 | if (ctx._events === undefined) ctx._events = Object.create(null) | |
| 68 | const e = ctx._events[name] || (ctx._events[name] = new EventListener()) | |
| 69 | e.append(ctx, name, fn, once) | |
| 70 | return ctx | |
| 71 | } | |
| 72 | ||
| 73 | function prependListener(ctx, name, fn, once) { | |
| 74 | if (ctx._events === undefined) ctx._events = Object.create(null) | |
| 75 | const e = ctx._events[name] || (ctx._events[name] = new EventListener()) | |
| 76 | e.prepend(ctx, name, fn, once) | |
| 77 | return ctx | |
| 78 | } | |
| 79 | ||
| 80 | function removeListener(ctx, name, fn) { | |
| 81 | if (ctx._events === undefined) return ctx | |
| 82 | const e = ctx._events[name] | |
| 83 | if (e !== undefined) e.remove(ctx, name, fn) | |
| 84 | return ctx | |
| 85 | } | |
| 86 | ||
| 87 | function throwUnhandledError(...args) { | |
| 88 | let err | |
| 89 | ||
| 90 | if (args.length > 0) err = args[0] | |
| 91 | ||
| 92 | if (err instanceof Error === false) err = errors.UNHANDLED_ERROR(err) | |
| 93 | ||
| 94 | if (Error.captureStackTrace) { | |
| 95 | Error.captureStackTrace(err, exports.prototype.emit) | |
| 96 | } | |
| 97 | ||
| 98 | queueMicrotask(() => { | |
| 99 | throw err | |
| 100 | }) | |
| 101 | } | |
| 102 | ||
| 103 | module.exports = exports = class EventEmitter { | |
| 104 | constructor() { | |
| 105 | this._events = Object.create(null) | |
| 106 | } | |
| 107 | ||
| 108 | addListener(name, fn) { | |
| 109 | return appendListener(this, name, fn, false) | |
| 110 | } | |
| 111 | ||
| 112 | addOnceListener(name, fn) { | |
| 113 | return appendListener(this, name, fn, true) | |
| 114 | } | |
| 115 | ||
| 116 | prependListener(name, fn) { | |
| 117 | return prependListener(this, name, fn, false) | |
| 118 | } | |
| 119 | ||
| 120 | prependOnceListener(name, fn) { | |
| 121 | return prependListener(this, name, fn, true) | |
| 122 | } | |
| 123 | ||
| 124 | removeListener(name, fn) { | |
| 125 | return removeListener(this, name, fn) | |
| 126 | } | |
| 127 | ||
| 128 | on(name, fn) { | |
| 129 | return appendListener(this, name, fn, false) | |
| 130 | } | |
| 131 | ||
| 132 | once(name, fn) { | |
| 133 | return appendListener(this, name, fn, true) | |
| 134 | } | |
| 135 | ||
| 136 | off(name, fn) { | |
| 137 | return removeListener(this, name, fn) | |
| 138 | } | |
| 139 | ||
| 140 | emit(name, ...args) { | |
| 141 | if (name === 'error' && this._events !== undefined && this._events.error === undefined) { | |
| 142 | throwUnhandledError(...args) | |
| 143 | } | |
| 144 | ||
| 145 | if (this._events === undefined) return false | |
| 146 | const e = this._events[name] | |
| 147 | return e === undefined ? false : e.emit(this, name, ...args) | |
| 148 | } | |
| 149 | ||
| 150 | listeners(name) { | |
| 151 | if (this._events === undefined) return [] | |
| 152 | const e = this._events[name] | |
| 153 | return e === undefined ? [] : [...e.list] | |
| 154 | } | |
| 155 | ||
| 156 | listenerCount(name) { | |
| 157 | if (this._events === undefined) return 0 | |
| 158 | const e = this._events[name] | |
| 159 | return e === undefined ? 0 : e.list.length | |
| 160 | } | |
| 161 | ||
| 162 | getMaxListeners() { | |
| 163 | return EventEmitter.defaultMaxListeners | |
| 164 | } | |
| 165 | ||
| 166 | setMaxListeners(n) {} | |
| 167 | ||
| 168 | removeAllListeners(name) { | |
| 169 | if (arguments.length === 0) { | |
| 170 | for (const key of Reflect.ownKeys(this._events)) { | |
| 171 | if (key === 'removeListener') continue | |
| 172 | this.removeAllListeners(key) | |
| 173 | } | |
| 174 | this.removeAllListeners('removeListener') | |
| 175 | } else { | |
| 176 | const e = this._events[name] | |
| 177 | if (e !== undefined) e.removeAll(this, name) | |
| 178 | } | |
| 179 | return this | |
| 180 | } | |
| 181 | } | |
| 182 | ||
| 183 | exports.EventEmitter = exports | |
| 184 | ||
| 185 | exports.errors = errors | |
| 186 | ||
| 187 | exports.defaultMaxListeners = 10 | |
| 188 | ||
| 189 | exports.on = function on(emitter, name, opts = {}) { | |
| 190 | const { signal } = opts | |
| 191 | ||
| 192 | if (signal && signal.aborted) { | |
| 193 | throw errors.OPERATION_ABORTED(signal.reason) | |
| 194 | } | |
| 195 | ||
| 196 | let error = null | |
| 197 | let done = false | |
| 198 | ||
| 199 | const events = [] | |
| 200 | const promises = [] | |
| 201 | ||
| 202 | if (name !== 'error') emitter.on('error', onerror) | |
| 203 | ||
| 204 | if (signal) signal.addEventListener('abort', onabort) | |
| 205 | ||
| 206 | emitter.on(name, onevent) | |
| 207 | ||
| 208 | return { | |
| 209 | next() { | |
| 210 | if (events.length) { | |
| 211 | return Promise.resolve({ value: events.shift(), done: false }) | |
| 212 | } | |
| 213 | ||
| 214 | if (error) { | |
| 215 | const err = error | |
| 216 | ||
| 217 | error = null | |
| 218 | ||
| 219 | return Promise.reject(err) | |
| 220 | } | |
| 221 | ||
| 222 | if (done) return onclose() | |
| 223 | ||
| 224 | return new Promise((resolve, reject) => promises.push({ resolve, reject })) | |
| 225 | }, | |
| 226 | ||
| 227 | return() { | |
| 228 | return onclose() | |
| 229 | }, | |
| 230 | ||
| 231 | throw(err) { | |
| 232 | return onerror(err) | |
| 233 | }, | |
| 234 | ||
| 235 | [Symbol.asyncIterator]() { | |
| 236 | return this | |
| 237 | } | |
| 238 | } | |
| 239 | ||
| 240 | function onevent(...args) { | |
| 241 | if (promises.length) { | |
| 242 | promises.shift().resolve({ value: args, done: false }) | |
| 243 | } else { | |
| 244 | events.push(args) | |
| 245 | } | |
| 246 | } | |
| 247 | ||
| 248 | function onerror(err) { | |
| 249 | emitter.off(name, onevent).off('error', onerror) | |
| 250 | ||
| 251 | if (promises.length) { | |
| 252 | promises.shift().reject(err) | |
| 253 | } else { | |
| 254 | error = err | |
| 255 | } | |
| 256 | ||
| 257 | return Promise.resolve({ done: true }) | |
| 258 | } | |
| 259 | ||
| 260 | function onabort() { | |
| 261 | signal.removeEventListener('abort', onabort) | |
| 262 | ||
| 263 | onerror(errors.OPERATION_ABORTED(signal.reason)) | |
| 264 | } | |
| 265 | ||
| 266 | function onclose() { | |
| 267 | emitter.off(name, onevent) | |
| 268 | ||
| 269 | if (name !== 'error') emitter.off('error', onerror) | |
| 270 | ||
| 271 | if (signal) signal.removeEventListener('abort', onabort) | |
| 272 | ||
| 273 | done = true | |
| 274 | ||
| 275 | if (promises.length) promises.shift().resolve({ done: true }) | |
| 276 | ||
| 277 | return Promise.resolve({ done: true }) | |
| 278 | } | |
| 279 | } | |
| 280 | ||
| 281 | exports.once = function once(emitter, name, opts = {}) { | |
| 282 | const { signal } = opts | |
| 283 | ||
| 284 | if (signal && signal.aborted) { | |
| 285 | return Promise.reject(errors.OPERATION_ABORTED(signal.reason)) | |
| 286 | } | |
| 287 | ||
| 288 | return new Promise((resolve, reject) => { | |
| 289 | if (name !== 'error') emitter.on('error', onerror) | |
| 290 | ||
| 291 | if (signal) signal.addEventListener('abort', onabort) | |
| 292 | ||
| 293 | emitter.once(name, onevent) | |
| 294 | ||
| 295 | function onevent(...args) { | |
| 296 | if (name !== 'error') emitter.off('error', onerror) | |
| 297 | ||
| 298 | if (signal) signal.removeEventListener('abort', onabort) | |
| 299 | ||
| 300 | resolve(args) | |
| 301 | } | |
| 302 | ||
| 303 | function onerror(err) { | |
| 304 | emitter.off(name, onevent) | |
| 305 | ||
| 306 | if (name !== 'error') emitter.off('error', onerror) | |
| 307 | ||
| 308 | reject(err) | |
| 309 | } | |
| 310 | ||
| 311 | function onabort() { | |
| 312 | signal.removeEventListener('abort', onabort) | |
| 313 | ||
| 314 | onerror(errors.OPERATION_ABORTED(signal.reason)) | |
| 315 | } | |
| 316 | }) | |
| 317 | } | |
| 318 | ||
| 319 | exports.forward = function forward(from, to, names, opts = {}) { | |
| 320 | if (typeof names === 'string') names = [names] | |
| 321 | ||
| 322 | const { emit = to.emit.bind(to) } = opts | |
| 323 | ||
| 324 | const listeners = names.map( | |
| 325 | (name) => | |
| 326 | function onevent(...args) { | |
| 327 | emit(name, ...args) | |
| 328 | } | |
| 329 | ) | |
| 330 | ||
| 331 | to.on('newListener', (name) => { | |
| 332 | const i = names.indexOf(name) | |
| 333 | ||
| 334 | if (i !== -1 && to.listenerCount(name) === 0) { | |
| 335 | from.on(name, listeners[i]) | |
| 336 | } | |
| 337 | }).on('removeListener', (name) => { | |
| 338 | const i = names.indexOf(name) | |
| 339 | ||
| 340 | if (i !== -1 && to.listenerCount(name) === 0) { | |
| 341 | from.off(name, listeners[i]) | |
| 342 | } | |
| 343 | }) | |
| 344 | } | |
| 345 | ||
| 346 | exports.listenerCount = function listenerCount(emitter, name) { | |
| 347 | return emitter.listenerCount(name) | |
| 348 | } | |
| 349 | ||
| 350 | exports.getMaxListeners = function getMaxListeners(emitter) { | |
| 351 | if (typeof emitter.getMaxListeners === 'function') { | |
| 352 | return emitter.getMaxListeners() | |
| 353 | } | |
| 354 | ||
| 355 | return exports.defaultMaxListeners | |
| 356 | } | |
| 357 | ||
| 358 | exports.setMaxListeners = function setMaxListeners(n, ...emitters) { | |
| 359 | if (emitters.length === 0) exports.defaultMaxListeners = n | |
| 360 | else { | |
| 361 | for (const emitter of emitters) { | |
| 362 | if (typeof emitter.setMaxListeners === 'function') { | |
| 363 | emitter.setMaxListeners(n) | |
| 364 | } | |
| 365 | } | |
| 366 | } | |
| 367 | } |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | module.exports = class EventEmitterError extends Error { | |
| 2 | constructor(msg, code, fn = EventEmitterError, opts) { | |
| 3 | super(`${code}: ${msg}`, opts) | |
| 4 | this.code = code | |
| 5 | ||
| 6 | if (Error.captureStackTrace) { | |
| 7 | Error.captureStackTrace(this, fn) | |
| 8 | } | |
| 9 | } | |
| 10 | ||
| 11 | get name() { | |
| 12 | return 'EventEmitterError' | |
| 13 | } | |
| 14 | ||
| 15 | static OPERATION_ABORTED(cause, msg = 'Operation aborted') { | |
| 16 | return new EventEmitterError(msg, 'OPERATION_ABORTED', EventEmitterError.OPERATION_ABORTED, { | |
| 17 | cause | |
| 18 | }) | |
| 19 | } | |
| 20 | ||
| 21 | static UNHANDLED_ERROR(cause, msg = 'Unhandled error') { | |
| 22 | return new EventEmitterError(msg, 'UNHANDLED_ERROR', EventEmitterError.UNHANDLED_ERROR, { | |
| 23 | cause | |
| 24 | }) | |
| 25 | } | |
| 26 | } |
| … | … | @@ -0,0 +1,61 @@ |
| 1 | { | |
| 2 | "name": "bare-events", | |
| 3 | "version": "2.8.2", | |
| 4 | "description": "Event emitters for JavaScript", | |
| 5 | "exports": { | |
| 6 | "./package": "./package.json", | |
| 7 | ".": { | |
| 8 | "types": "./index.d.ts", | |
| 9 | "default": "./index.js" | |
| 10 | }, | |
| 11 | "./global": { | |
| 12 | "types": "./global.d.ts", | |
| 13 | "default": "./global.js" | |
| 14 | }, | |
| 15 | "./web": { | |
| 16 | "types": "./web.d.ts", | |
| 17 | "default": "./web.js" | |
| 18 | }, | |
| 19 | "./errors": "./lib/errors.js" | |
| 20 | }, | |
| 21 | "files": [ | |
| 22 | "index.js", | |
| 23 | "index.d.ts", | |
| 24 | "global.js", | |
| 25 | "global.d.ts", | |
| 26 | "web.js", | |
| 27 | "web.d.ts", | |
| 28 | "lib" | |
| 29 | ], | |
| 30 | "scripts": { | |
| 31 | "test": "npm run lint && npm run test:bare && npm run test:node", | |
| 32 | "test:bare": "bare test.js", | |
| 33 | "test:node": "node test.js", | |
| 34 | "lint": "prettier . --check" | |
| 35 | }, | |
| 36 | "repository": { | |
| 37 | "type": "git", | |
| 38 | "url": "git+https://github.com/holepunchto/bare-events.git" | |
| 39 | }, | |
| 40 | "author": "Holepunch", | |
| 41 | "license": "Apache-2.0", | |
| 42 | "bugs": { | |
| 43 | "url": "https://github.com/holepunchto/bare-events/issues" | |
| 44 | }, | |
| 45 | "homepage": "https://github.com/holepunchto/bare-events#readme", | |
| 46 | "devDependencies": { | |
| 47 | "bare-abort-controller": "^1.0.0", | |
| 48 | "brittle": "^3.3.2", | |
| 49 | "prettier": "^3.4.2", | |
| 50 | "prettier-config-holepunch": "^2.0.0", | |
| 51 | "uncaughts": "^1.1.1" | |
| 52 | }, | |
| 53 | "peerDependencies": { | |
| 54 | "bare-abort-controller": "*" | |
| 55 | }, | |
| 56 | "peerDependenciesMeta": { | |
| 57 | "bare-abort-controller": { | |
| 58 | "optional": true | |
| 59 | } | |
| 60 | } | |
| 61 | } |
| … | … | @@ -0,0 +1,79 @@ |
| 1 | interface AbortSignal extends EventTarget {} | |
| 2 | ||
| 3 | export interface EventOptions { | |
| 4 | bubbles?: boolean | |
| 5 | cancelable?: boolean | |
| 6 | composed?: boolean | |
| 7 | } | |
| 8 | ||
| 9 | export interface Event { | |
| 10 | readonly type: string | |
| 11 | readonly target: EventTarget | null | |
| 12 | readonly currentTarget: EventTarget | null | |
| 13 | readonly bubbles: boolean | |
| 14 | readonly cancelable: boolean | |
| 15 | readonly composed: boolean | |
| 16 | readonly defaultPrevented: boolean | |
| 17 | readonly isTrusted: boolean | |
| 18 | ||
| 19 | preventDefault(): void | |
| 20 | stopPropagation(): void | |
| 21 | stopImmediatePropagation(): void | |
| 22 | } | |
| 23 | ||
| 24 | export class Event { | |
| 25 | constructor(type: string, options?: EventOptions) | |
| 26 | } | |
| 27 | ||
| 28 | export interface CustomEventOptions<T = any> extends EventOptions { | |
| 29 | detail?: T | |
| 30 | } | |
| 31 | ||
| 32 | export interface CustomEvent<T = any> extends Event { | |
| 33 | readonly detail: T | |
| 34 | } | |
| 35 | ||
| 36 | export class CustomEvent<T = any> { | |
| 37 | constructor(type: string, options?: CustomEventOptions<T>) | |
| 38 | } | |
| 39 | ||
| 40 | export interface AddEventListenerOptions { | |
| 41 | capture?: boolean | |
| 42 | passive?: boolean | |
| 43 | once?: boolean | |
| 44 | signal?: AbortSignal | null | |
| 45 | } | |
| 46 | ||
| 47 | export interface RemoveEventListenerOptions { | |
| 48 | capture?: boolean | |
| 49 | } | |
| 50 | ||
| 51 | export interface EventTarget { | |
| 52 | addEventListener( | |
| 53 | type: string, | |
| 54 | callback: EventListener, | |
| 55 | options?: AddEventListenerOptions | boolean | |
| 56 | ): void | |
| 57 | ||
| 58 | removeEventListener( | |
| 59 | type: string, | |
| 60 | callback: EventListener, | |
| 61 | options?: RemoveEventListenerOptions | boolean | |
| 62 | ): void | |
| 63 | ||
| 64 | dispatchEvent(event: Event): boolean | |
| 65 | } | |
| 66 | ||
| 67 | export class EventTarget { | |
| 68 | constructor() | |
| 69 | } | |
| 70 | ||
| 71 | export type EventListener = EventCallback | EventHandler | |
| 72 | ||
| 73 | export interface EventCallback { | |
| 74 | (event: Event): void | |
| 75 | } | |
| 76 | ||
| 77 | export interface EventHandler { | |
| 78 | handleEvent(event: Event): void | |
| 79 | } |
| … | … | @@ -0,0 +1,335 @@ |
| 1 | // Event state | |
| 2 | const BUBBLES = 0x1 | |
| 3 | const CANCELABLE = 0x2 | |
| 4 | const COMPOSED = 0x4 | |
| 5 | const CANCELED = 0x8 | |
| 6 | const DISPATCH = 0x10 | |
| 7 | const STOP = 0x20 | |
| 8 | ||
| 9 | // EventTarget state | |
| 10 | const CAPTURE = 0x1 | |
| 11 | const PASSIVE = 0x2 | |
| 12 | const ONCE = 0x4 | |
| 13 | ||
| 14 | // https://dom.spec.whatwg.org/#event | |
| 15 | class Event { | |
| 16 | // https://dom.spec.whatwg.org/#dom-event-event | |
| 17 | constructor(type, options = {}) { | |
| 18 | const { bubbles = false, cancelable = false, composed = false } = options | |
| 19 | ||
| 20 | this._type = type | |
| 21 | this._target = null | |
| 22 | this._state = 0 | |
| 23 | ||
| 24 | if (bubbles) this._state |= BUBBLES | |
| 25 | if (cancelable) this._state |= CANCELABLE | |
| 26 | if (composed) this._state |= COMPOSED | |
| 27 | } | |
| 28 | ||
| 29 | // https://dom.spec.whatwg.org/#dom-event-type | |
| 30 | get type() { | |
| 31 | return this._type | |
| 32 | } | |
| 33 | ||
| 34 | // https://dom.spec.whatwg.org/#dom-event-target | |
| 35 | get target() { | |
| 36 | return this._target | |
| 37 | } | |
| 38 | ||
| 39 | // https://dom.spec.whatwg.org/#dom-event-currenttarget | |
| 40 | get currentTarget() { | |
| 41 | return null | |
| 42 | } | |
| 43 | ||
| 44 | // https://dom.spec.whatwg.org/#dom-event-bubbles | |
| 45 | get bubbles() { | |
| 46 | return (this._state & BUBBLES) !== 0 | |
| 47 | } | |
| 48 | ||
| 49 | // https://dom.spec.whatwg.org/#dom-event-cancelable | |
| 50 | get cancelable() { | |
| 51 | return (this._state & CANCELABLE) !== 0 | |
| 52 | } | |
| 53 | ||
| 54 | // https://dom.spec.whatwg.org/#dom-event-composed | |
| 55 | get composed() { | |
| 56 | return (this._state & COMPOSED) !== 0 | |
| 57 | } | |
| 58 | ||
| 59 | // https://dom.spec.whatwg.org/#dom-event-defaultprevented | |
| 60 | get defaultPrevented() { | |
| 61 | return (this._state & CANCELED) !== 0 | |
| 62 | } | |
| 63 | ||
| 64 | // https://dom.spec.whatwg.org/#dom-event-istrusted | |
| 65 | get isTrusted() { | |
| 66 | return false | |
| 67 | } | |
| 68 | ||
| 69 | // https://dom.spec.whatwg.org/#dom-event-preventdefault | |
| 70 | preventDefault() { | |
| 71 | if (this._state & CANCELABLE) this._state |= CANCELED | |
| 72 | } | |
| 73 | ||
| 74 | // https://dom.spec.whatwg.org/#dom-event-stoppropagation | |
| 75 | stopPropagation() {} | |
| 76 | ||
| 77 | // https://dom.spec.whatwg.org/#dom-event-stopimmediatepropagation | |
| 78 | stopImmediatePropagation() { | |
| 79 | this._state |= STOP | |
| 80 | } | |
| 81 | ||
| 82 | toJSON() { | |
| 83 | return { | |
| 84 | type: this.type, | |
| 85 | target: this.target, | |
| 86 | bubbles: this.bubbles, | |
| 87 | cancelable: this.cancelable, | |
| 88 | composed: this.composed, | |
| 89 | defaultPrevented: this.defaultPrevented, | |
| 90 | isTrusted: this.isTrusted | |
| 91 | } | |
| 92 | } | |
| 93 | ||
| 94 | [Symbol.for('bare.inspect')]() { | |
| 95 | return { | |
| 96 | __proto__: { constructor: Event }, | |
| 97 | ||
| 98 | type: this.type, | |
| 99 | target: this.target, | |
| 100 | bubbles: this.bubbles, | |
| 101 | cancelable: this.cancelable, | |
| 102 | composed: this.composed, | |
| 103 | defaultPrevented: this.defaultPrevented, | |
| 104 | isTrusted: this.isTrusted | |
| 105 | } | |
| 106 | } | |
| 107 | } | |
| 108 | ||
| 109 | exports.Event = Event | |
| 110 | ||
| 111 | // https://dom.spec.whatwg.org/#customevent | |
| 112 | exports.CustomEvent = class CustomEvent extends Event { | |
| 113 | constructor(type, options = {}) { | |
| 114 | super(type, options) | |
| 115 | ||
| 116 | const { detail = null } = options | |
| 117 | ||
| 118 | this._detail = detail | |
| 119 | } | |
| 120 | ||
| 121 | // https://dom.spec.whatwg.org/#dom-customevent-detail | |
| 122 | get detail() { | |
| 123 | return this._detail | |
| 124 | } | |
| 125 | } | |
| 126 | ||
| 127 | // https://dom.spec.whatwg.org/#eventtarget | |
| 128 | exports.EventTarget = class EventTarget { | |
| 129 | // https://dom.spec.whatwg.org/#dom-eventtarget-eventtarget | |
| 130 | constructor() { | |
| 131 | this._listeners = new Map() | |
| 132 | } | |
| 133 | ||
| 134 | // https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener | |
| 135 | addEventListener(type, callback = null, options = {}) { | |
| 136 | if (typeof options === 'boolean') options = { capture: options } | |
| 137 | ||
| 138 | const { capture = false, passive = false, once = false, signal = null } = options | |
| 139 | ||
| 140 | if (signal !== null && signal.aborted) return | |
| 141 | if (callback === null) return | |
| 142 | ||
| 143 | const listener = new EventListener(type, callback, capture, passive, once, signal) | |
| 144 | ||
| 145 | const listeners = this._listeners.get(type) | |
| 146 | ||
| 147 | if (listeners === undefined) this._listeners.set(type, listener) | |
| 148 | else { | |
| 149 | for (const existing of listeners) { | |
| 150 | if (callback === existing.callback && capture === existing.capture) { | |
| 151 | return // Duplicate listener | |
| 152 | } | |
| 153 | } | |
| 154 | ||
| 155 | listener.link(listeners) | |
| 156 | ||
| 157 | if (signal !== null) { | |
| 158 | signal.addEventListener('abort', onabort) | |
| 159 | ||
| 160 | function onabort() { | |
| 161 | listener.unlink() | |
| 162 | } | |
| 163 | } | |
| 164 | } | |
| 165 | } | |
| 166 | ||
| 167 | // https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener | |
| 168 | removeEventListener(type, callback = null, options = {}) { | |
| 169 | if (typeof options === 'boolean') options = { capture: options } | |
| 170 | ||
| 171 | const { capture = false } = options | |
| 172 | ||
| 173 | const listeners = this._listeners.get(type) | |
| 174 | ||
| 175 | if (listeners === undefined) return | |
| 176 | ||
| 177 | for (const existing of listeners) { | |
| 178 | if (callback === existing.callback && capture === existing.capture) { | |
| 179 | const next = existing.unlink() | |
| 180 | ||
| 181 | if (listeners === existing) this._listeners.set(type, next) | |
| 182 | ||
| 183 | return | |
| 184 | } | |
| 185 | } | |
| 186 | } | |
| 187 | ||
| 188 | // https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent | |
| 189 | dispatchEvent(event) { | |
| 190 | event._target = this | |
| 191 | event._state |= DISPATCH | |
| 192 | ||
| 193 | const listeners = this._listeners.get(event.type) | |
| 194 | ||
| 195 | try { | |
| 196 | if (listeners === undefined) return true | |
| 197 | ||
| 198 | for (const listener of listeners) { | |
| 199 | // https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke | |
| 200 | ||
| 201 | if (listener.once) listener.unlink() | |
| 202 | ||
| 203 | let callback = listener.callback | |
| 204 | let context = this | |
| 205 | ||
| 206 | if (typeof callback === 'object') { | |
| 207 | context = callback | |
| 208 | callback = callback.handleEvent | |
| 209 | } | |
| 210 | ||
| 211 | Reflect.apply(callback, context, [event]) | |
| 212 | ||
| 213 | if (event._state & STOP) break | |
| 214 | } | |
| 215 | ||
| 216 | return (event._state & CANCELED) === 0 | |
| 217 | } finally { | |
| 218 | event._state &= ~DISPATCH | |
| 219 | event._state &= ~STOP | |
| 220 | } | |
| 221 | } | |
| 222 | ||
| 223 | toJSON() { | |
| 224 | return {} | |
| 225 | } | |
| 226 | ||
| 227 | [Symbol.for('bare.inspect')]() { | |
| 228 | return { | |
| 229 | __proto__: { constructor: EventTarget } | |
| 230 | } | |
| 231 | } | |
| 232 | } | |
| 233 | ||
| 234 | // https://dom.spec.whatwg.org/#concept-event-listener | |
| 235 | class EventListener { | |
| 236 | constructor(type, callback, capture, passive, once, signal) { | |
| 237 | this._type = type | |
| 238 | this._callback = callback | |
| 239 | this._signal = signal | |
| 240 | this._state = 0 | |
| 241 | ||
| 242 | if (capture) this._state |= CAPTURE | |
| 243 | if (passive) this._state |= PASSIVE | |
| 244 | if (once) this._state |= ONCE | |
| 245 | ||
| 246 | this._previous = this | |
| 247 | this._next = this | |
| 248 | } | |
| 249 | ||
| 250 | get type() { | |
| 251 | return this._type | |
| 252 | } | |
| 253 | ||
| 254 | get callback() { | |
| 255 | return this._callback | |
| 256 | } | |
| 257 | ||
| 258 | get capture() { | |
| 259 | return (this._state & CAPTURE) !== 0 | |
| 260 | } | |
| 261 | ||
| 262 | get passive() { | |
| 263 | return (this._state & PASSIVE) !== 0 | |
| 264 | } | |
| 265 | ||
| 266 | get once() { | |
| 267 | return (this._state & ONCE) !== 0 | |
| 268 | } | |
| 269 | ||
| 270 | get removed() { | |
| 271 | return this._previous === this && this._next === this | |
| 272 | } | |
| 273 | ||
| 274 | link(listener) { | |
| 275 | const next = this._next | |
| 276 | const previous = listener._previous | |
| 277 | ||
| 278 | this._next = listener | |
| 279 | listener._previous = this | |
| 280 | ||
| 281 | previous._next = next | |
| 282 | next._previous = previous | |
| 283 | ||
| 284 | return listener | |
| 285 | } | |
| 286 | ||
| 287 | unlink() { | |
| 288 | if (this.removed) return this | |
| 289 | ||
| 290 | const next = this._next | |
| 291 | const previous = this._previous | |
| 292 | ||
| 293 | this._next = this | |
| 294 | this._previous = this | |
| 295 | ||
| 296 | previous._next = next | |
| 297 | next._previous = previous | |
| 298 | ||
| 299 | return next | |
| 300 | } | |
| 301 | ||
| 302 | *[Symbol.iterator]() { | |
| 303 | let current = this | |
| 304 | ||
| 305 | while (true) { | |
| 306 | const next = current._next | |
| 307 | yield current | |
| 308 | if (next === this) break | |
| 309 | current = next | |
| 310 | } | |
| 311 | } | |
| 312 | ||
| 313 | toJSON() { | |
| 314 | return { | |
| 315 | type: this.type, | |
| 316 | capture: this.capture, | |
| 317 | passive: this.passive, | |
| 318 | once: this.once, | |
| 319 | removed: this.removed | |
| 320 | } | |
| 321 | } | |
| 322 | ||
| 323 | [Symbol.for('bare.inspect')]() { | |
| 324 | return { | |
| 325 | __proto__: { constructor: EventListener }, | |
| 326 | ||
| 327 | type: this.type, | |
| 328 | callback: this.callback, | |
| 329 | capture: this.capture, | |
| 330 | passive: this.passive, | |
| 331 | once: this.once, | |
| 332 | removed: this.removed | |
| 333 | } | |
| 334 | } | |
| 335 | } |
| … | … | @@ -0,0 +1,13 @@ |
| 1 | cmake_minimum_required(VERSION 4.0) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | ||
| 5 | project(bare_fs C) | |
| 6 | ||
| 7 | add_bare_module(bare_fs) | |
| 8 | ||
| 9 | target_sources( | |
| 10 | ${bare_fs} | |
| 11 | PRIVATE | |
| 12 | binding.c | |
| 13 | ) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,29 @@ |
| 1 | # bare-fs | |
| 2 | ||
| 3 | Native file system operations for Javascript. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i bare-fs | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const fs = require('bare-fs') | |
| 13 | ||
| 14 | const fd = await fs.open('hello.txt') | |
| 15 | ||
| 16 | const buffer = Buffer.alloc(1024) | |
| 17 | ||
| 18 | try { | |
| 19 | const length = await fs.read(fd, buffer) | |
| 20 | ||
| 21 | console.log('Read', length, 'bytes') | |
| 22 | } finally { | |
| 23 | await fs.close(fd) | |
| 24 | } | |
| 25 | ``` | |
| 26 | ||
| 27 | ## License | |
| 28 | ||
| 29 | Apache-2.0 |
| … | … | @@ -0,0 +1,2273 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <bare.h> | |
| 3 | #include <js.h> | |
| 4 | #include <stdbool.h> | |
| 5 | #include <stdint.h> | |
| 6 | #include <stdlib.h> | |
| 7 | #include <string.h> | |
| 8 | #include <utf.h> | |
| 9 | #include <uv.h> | |
| 10 | ||
| 11 | #ifndef _MSC_VER | |
| 12 | #include <unistd.h> | |
| 13 | #endif | |
| 14 | ||
| 15 | typedef struct { | |
| 16 | uv_fs_t handle; | |
| 17 | ||
| 18 | js_env_t *env; | |
| 19 | js_ref_t *ctx; | |
| 20 | js_ref_t *on_result; | |
| 21 | ||
| 22 | bool exiting; | |
| 23 | bool inflight; | |
| 24 | ||
| 25 | js_deferred_teardown_t *teardown; | |
| 26 | } bare_fs_req_t; | |
| 27 | ||
| 28 | typedef utf8_t bare_fs_path_t[4096 + 1 /* NULL */]; | |
| 29 | ||
| 30 | typedef struct { | |
| 31 | uv_dir_t *handle; | |
| 32 | } bare_fs_dir_t; | |
| 33 | ||
| 34 | typedef struct { | |
| 35 | uv_fs_event_t handle; | |
| 36 | ||
| 37 | js_env_t *env; | |
| 38 | js_ref_t *ctx; | |
| 39 | js_ref_t *on_event; | |
| 40 | js_ref_t *on_close; | |
| 41 | ||
| 42 | bool exiting; | |
| 43 | bool closing; | |
| 44 | ||
| 45 | js_deferred_teardown_t *teardown; | |
| 46 | } bare_fs_watcher_t; | |
| 47 | ||
| 48 | typedef uv_dirent_t bare_fs_dirent_t; | |
| 49 | ||
| 50 | enum { | |
| 51 | bare_fs_async = true, | |
| 52 | bare_fs_sync = false | |
| 53 | }; | |
| 54 | ||
| 55 | static inline void | |
| 56 | bare_fs__request_destroy(bare_fs_req_t *req) { | |
| 57 | int err; | |
| 58 | ||
| 59 | js_env_t *env = req->env; | |
| 60 | ||
| 61 | js_deferred_teardown_t *teardown = req->teardown; | |
| 62 | ||
| 63 | uv_fs_req_cleanup(&req->handle); | |
| 64 | ||
| 65 | err = js_delete_reference(env, req->on_result); | |
| 66 | assert(err == 0); | |
| 67 | ||
| 68 | err = js_delete_reference(env, req->ctx); | |
| 69 | assert(err == 0); | |
| 70 | ||
| 71 | err = js_finish_deferred_teardown_callback(teardown); | |
| 72 | assert(err == 0); | |
| 73 | } | |
| 74 | ||
| 75 | static void | |
| 76 | bare_fs__on_request_teardown(js_deferred_teardown_t *handle, void *data) { | |
| 77 | int err; | |
| 78 | ||
| 79 | bare_fs_req_t *req = (bare_fs_req_t *) data; | |
| 80 | ||
| 81 | req->exiting = true; | |
| 82 | ||
| 83 | if (req->inflight) { | |
| 84 | err = uv_cancel((uv_req_t *) &req->handle); | |
| 85 | if (err < 0) return; | |
| 86 | } | |
| 87 | ||
| 88 | bare_fs__request_destroy(req); | |
| 89 | } | |
| 90 | ||
| 91 | static inline void | |
| 92 | bare_fs__on_request_result(uv_fs_t *handle) { | |
| 93 | int err; | |
| 94 | ||
| 95 | bare_fs_req_t *req = (bare_fs_req_t *) handle; | |
| 96 | ||
| 97 | req->inflight = false; | |
| 98 | ||
| 99 | if (req->exiting) return bare_fs__request_destroy(req); | |
| 100 | ||
| 101 | js_env_t *env = req->env; | |
| 102 | ||
| 103 | js_handle_scope_t *scope; | |
| 104 | err = js_open_handle_scope(env, &scope); | |
| 105 | assert(err == 0); | |
| 106 | ||
| 107 | js_value_t *ctx; | |
| 108 | err = js_get_reference_value(env, req->ctx, &ctx); | |
| 109 | assert(err == 0); | |
| 110 | ||
| 111 | js_value_t *on_result; | |
| 112 | err = js_get_reference_value(env, req->on_result, &on_result); | |
| 113 | assert(err == 0); | |
| 114 | ||
| 115 | int status = handle->result; | |
| 116 | ||
| 117 | js_value_t *args[2]; | |
| 118 | ||
| 119 | if (status < 0) { | |
| 120 | js_value_t *code; | |
| 121 | err = js_create_string_utf8(env, (utf8_t *) uv_err_name(status), -1, &code); | |
| 122 | assert(err == 0); | |
| 123 | ||
| 124 | js_value_t *message; | |
| 125 | err = js_create_string_utf8(env, (utf8_t *) uv_strerror(status), -1, &message); | |
| 126 | assert(err == 0); | |
| 127 | ||
| 128 | err = js_create_error(env, code, message, &args[0]); | |
| 129 | assert(err == 0); | |
| 130 | } else { | |
| 131 | err = js_get_null(env, &args[0]); | |
| 132 | assert(err == 0); | |
| 133 | } | |
| 134 | ||
| 135 | err = js_create_int32(env, status, &args[1]); | |
| 136 | assert(err == 0); | |
| 137 | ||
| 138 | err = js_call_function(env, ctx, on_result, 2, args, NULL); | |
| 139 | (void) err; | |
| 140 | ||
| 141 | err = js_close_handle_scope(env, scope); | |
| 142 | assert(err == 0); | |
| 143 | } | |
| 144 | ||
| 145 | static inline int | |
| 146 | bare_fs__request_pending(js_env_t *env, bare_fs_req_t *req, bool async, int *result) { | |
| 147 | int err; | |
| 148 | ||
| 149 | if (async) { | |
| 150 | req->inflight = true; | |
| 151 | ||
| 152 | return 0; | |
| 153 | } | |
| 154 | ||
| 155 | int status = req->handle.result; | |
| 156 | ||
| 157 | if (status < 0) { | |
| 158 | err = js_throw_error(env, uv_err_name(status), uv_strerror(status)); | |
| 159 | assert(err == 0); | |
| 160 | ||
| 161 | return -1; | |
| 162 | } | |
| 163 | ||
| 164 | if (result) *result = status; | |
| 165 | ||
| 166 | return 1; | |
| 167 | } | |
| 168 | ||
| 169 | static js_value_t * | |
| 170 | bare_fs_request_init(js_env_t *env, js_callback_info_t *info) { | |
| 171 | int err; | |
| 172 | ||
| 173 | size_t argc = 2; | |
| 174 | js_value_t *argv[2]; | |
| 175 | ||
| 176 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 177 | assert(err == 0); | |
| 178 | ||
| 179 | assert(argc == 2); | |
| 180 | ||
| 181 | js_value_t *handle; | |
| 182 | ||
| 183 | bare_fs_req_t *req; | |
| 184 | err = js_create_arraybuffer(env, sizeof(bare_fs_req_t), (void **) &req, &handle); | |
| 185 | assert(err == 0); | |
| 186 | ||
| 187 | req->env = env; | |
| 188 | req->exiting = false; | |
| 189 | req->inflight = false; | |
| 190 | ||
| 191 | err = js_create_reference(env, argv[0], 1, &req->ctx); | |
| 192 | assert(err == 0); | |
| 193 | ||
| 194 | err = js_create_reference(env, argv[1], 1, &req->on_result); | |
| 195 | assert(err == 0); | |
| 196 | ||
| 197 | err = js_add_deferred_teardown_callback(env, bare_fs__on_request_teardown, (void *) req, &req->teardown); | |
| 198 | assert(err == 0); | |
| 199 | ||
| 200 | return handle; | |
| 201 | } | |
| 202 | ||
| 203 | static js_value_t * | |
| 204 | bare_fs_request_destroy(js_env_t *env, js_callback_info_t *info) { | |
| 205 | int err; | |
| 206 | ||
| 207 | size_t argc = 1; | |
| 208 | js_value_t *argv[1]; | |
| 209 | ||
| 210 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 211 | assert(err == 0); | |
| 212 | ||
| 213 | assert(argc == 1); | |
| 214 | ||
| 215 | bare_fs_req_t *req; | |
| 216 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 217 | assert(err == 0); | |
| 218 | ||
| 219 | bare_fs__request_destroy(req); | |
| 220 | ||
| 221 | return NULL; | |
| 222 | } | |
| 223 | ||
| 224 | static js_value_t * | |
| 225 | bare_fs_request_reset(js_env_t *env, js_callback_info_t *info) { | |
| 226 | int err; | |
| 227 | ||
| 228 | size_t argc = 1; | |
| 229 | js_value_t *argv[1]; | |
| 230 | ||
| 231 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 232 | assert(err == 0); | |
| 233 | ||
| 234 | assert(argc == 1); | |
| 235 | ||
| 236 | bare_fs_req_t *req; | |
| 237 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 238 | assert(err == 0); | |
| 239 | ||
| 240 | uv_fs_req_cleanup(&req->handle); | |
| 241 | ||
| 242 | return NULL; | |
| 243 | } | |
| 244 | ||
| 245 | static js_value_t * | |
| 246 | bare_fs_request_result_stat(js_env_t *env, js_callback_info_t *info) { | |
| 247 | int err; | |
| 248 | ||
| 249 | size_t argc = 1; | |
| 250 | js_value_t *argv[1]; | |
| 251 | ||
| 252 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 253 | assert(err == 0); | |
| 254 | ||
| 255 | assert(argc == 1); | |
| 256 | ||
| 257 | bare_fs_req_t *req; | |
| 258 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 259 | assert(err == 0); | |
| 260 | ||
| 261 | js_value_t *result; | |
| 262 | err = js_create_array_with_length(env, 14, &result); | |
| 263 | assert(err == 0); | |
| 264 | ||
| 265 | uint32_t i = 0; | |
| 266 | ||
| 267 | #define V(property) \ | |
| 268 | { \ | |
| 269 | js_value_t *value; \ | |
| 270 | err = js_create_int64(env, req->handle.statbuf.st_##property, &value); \ | |
| 271 | assert(err == 0); \ | |
| 272 | \ | |
| 273 | err = js_set_element(env, result, i++, value); \ | |
| 274 | assert(err == 0); \ | |
| 275 | } | |
| 276 | V(dev) | |
| 277 | V(mode) | |
| 278 | V(nlink) | |
| 279 | V(uid) | |
| 280 | V(gid) | |
| 281 | V(rdev) | |
| 282 | V(blksize) | |
| 283 | V(ino) | |
| 284 | V(size) | |
| 285 | V(blocks) | |
| 286 | #undef V | |
| 287 | ||
| 288 | #define V(property) \ | |
| 289 | { \ | |
| 290 | uv_timespec_t time = req->handle.statbuf.st_##property; \ | |
| 291 | \ | |
| 292 | js_value_t *value; \ | |
| 293 | err = js_create_int64(env, time.tv_sec * 1000 + time.tv_nsec / 1000000, &value); \ | |
| 294 | assert(err == 0); \ | |
| 295 | \ | |
| 296 | err = js_set_element(env, result, i++, value); \ | |
| 297 | assert(err == 0); \ | |
| 298 | } | |
| 299 | V(atim) | |
| 300 | V(mtim) | |
| 301 | V(ctim) | |
| 302 | V(birthtim) | |
| 303 | #undef V | |
| 304 | ||
| 305 | return result; | |
| 306 | } | |
| 307 | ||
| 308 | static js_value_t * | |
| 309 | bare_fs_request_result_string(js_env_t *env, js_callback_info_t *info) { | |
| 310 | int err; | |
| 311 | ||
| 312 | size_t argc = 1; | |
| 313 | js_value_t *argv[1]; | |
| 314 | ||
| 315 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 316 | assert(err == 0); | |
| 317 | ||
| 318 | assert(argc == 1); | |
| 319 | ||
| 320 | bare_fs_req_t *req; | |
| 321 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 322 | assert(err == 0); | |
| 323 | ||
| 324 | size_t len = strlen(req->handle.ptr); | |
| 325 | ||
| 326 | js_value_t *result; | |
| 327 | ||
| 328 | void *data; | |
| 329 | err = js_create_arraybuffer(env, len, (void *) &data, &result); | |
| 330 | assert(err == 0); | |
| 331 | ||
| 332 | strncpy(data, req->handle.ptr, len); | |
| 333 | ||
| 334 | return result; | |
| 335 | } | |
| 336 | ||
| 337 | static js_value_t * | |
| 338 | bare_fs_request_result_dir(js_env_t *env, js_callback_info_t *info) { | |
| 339 | int err; | |
| 340 | ||
| 341 | size_t argc = 1; | |
| 342 | js_value_t *argv[1]; | |
| 343 | ||
| 344 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 345 | assert(err == 0); | |
| 346 | ||
| 347 | assert(argc == 1); | |
| 348 | ||
| 349 | bare_fs_req_t *req; | |
| 350 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 351 | assert(err == 0); | |
| 352 | ||
| 353 | js_value_t *result; | |
| 354 | ||
| 355 | bare_fs_dir_t *dir; | |
| 356 | err = js_create_arraybuffer(env, sizeof(bare_fs_dir_t), (void **) &dir, &result); | |
| 357 | assert(err == 0); | |
| 358 | ||
| 359 | dir->handle = req->handle.ptr; | |
| 360 | ||
| 361 | return result; | |
| 362 | } | |
| 363 | ||
| 364 | static js_value_t * | |
| 365 | bare_fs_request_result_dirents(js_env_t *env, js_callback_info_t *info) { | |
| 366 | int err; | |
| 367 | ||
| 368 | size_t argc = 1; | |
| 369 | js_value_t *argv[1]; | |
| 370 | ||
| 371 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 372 | assert(err == 0); | |
| 373 | ||
| 374 | assert(argc == 1); | |
| 375 | ||
| 376 | bare_fs_req_t *req; | |
| 377 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 378 | assert(err == 0); | |
| 379 | ||
| 380 | size_t len = req->handle.result; | |
| 381 | ||
| 382 | js_value_t *result; | |
| 383 | err = js_create_array_with_length(env, len, &result); | |
| 384 | assert(err == 0); | |
| 385 | ||
| 386 | uv_dir_t *dir = req->handle.ptr; | |
| 387 | ||
| 388 | for (uint32_t i = 0; i < len; i++) { | |
| 389 | uv_dirent_t *dirent = &dir->dirents[i]; | |
| 390 | ||
| 391 | js_value_t *entry; | |
| 392 | err = js_create_object(env, &entry); | |
| 393 | assert(err == 0); | |
| 394 | ||
| 395 | err = js_set_element(env, result, i, entry); | |
| 396 | assert(err == 0); | |
| 397 | ||
| 398 | size_t name_len = strlen(dirent->name); | |
| 399 | ||
| 400 | js_value_t *name; | |
| 401 | ||
| 402 | void *data; | |
| 403 | err = js_create_arraybuffer(env, name_len, &data, &name); | |
| 404 | assert(err == 0); | |
| 405 | ||
| 406 | memcpy(data, dirent->name, name_len); | |
| 407 | ||
| 408 | err = js_set_named_property(env, entry, "name", name); | |
| 409 | assert(err == 0); | |
| 410 | ||
| 411 | js_value_t *type; | |
| 412 | err = js_create_uint32(env, dirent->type, &type); | |
| 413 | assert(err == 0); | |
| 414 | ||
| 415 | err = js_set_named_property(env, entry, "type", type); | |
| 416 | assert(err == 0); | |
| 417 | } | |
| 418 | ||
| 419 | return result; | |
| 420 | } | |
| 421 | ||
| 422 | static void | |
| 423 | bare_fs__on_open(uv_fs_t *handle) { | |
| 424 | int err; | |
| 425 | ||
| 426 | bare_fs_req_t *req = (bare_fs_req_t *) handle; | |
| 427 | ||
| 428 | int status = handle->result; | |
| 429 | ||
| 430 | if (req->exiting && status >= 0) { | |
| 431 | int fd = status; | |
| 432 | ||
| 433 | uv_fs_req_cleanup(handle); | |
| 434 | ||
| 435 | err = uv_fs_close(handle->loop, handle, fd, NULL); | |
| 436 | assert(err == 0); | |
| 437 | } | |
| 438 | ||
| 439 | bare_fs__on_request_result(handle); | |
| 440 | } | |
| 441 | ||
| 442 | static inline js_value_t * | |
| 443 | bare_fs__open(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 444 | int err; | |
| 445 | ||
| 446 | size_t argc = 4; | |
| 447 | js_value_t *argv[4]; | |
| 448 | ||
| 449 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 450 | assert(err == 0); | |
| 451 | ||
| 452 | assert(argc == 4); | |
| 453 | ||
| 454 | bare_fs_req_t *req; | |
| 455 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 456 | assert(err == 0); | |
| 457 | ||
| 458 | bare_fs_path_t path; | |
| 459 | err = js_get_value_string_utf8(env, argv[1], path, sizeof(bare_fs_path_t), NULL); | |
| 460 | assert(err == 0); | |
| 461 | ||
| 462 | int32_t flags; | |
| 463 | err = js_get_value_int32(env, argv[2], &flags); | |
| 464 | assert(err == 0); | |
| 465 | ||
| 466 | int32_t mode; | |
| 467 | err = js_get_value_int32(env, argv[3], &mode); | |
| 468 | assert(err == 0); | |
| 469 | ||
| 470 | uv_loop_t *loop; | |
| 471 | err = js_get_env_loop(env, &loop); | |
| 472 | assert(err == 0); | |
| 473 | ||
| 474 | err = uv_fs_open(loop, &req->handle, (char *) path, flags, mode, async ? bare_fs__on_open : NULL); | |
| 475 | (void) err; | |
| 476 | ||
| 477 | int status; | |
| 478 | err = bare_fs__request_pending(env, req, async, &status); | |
| 479 | if (err != 1) return NULL; | |
| 480 | ||
| 481 | js_value_t *result; | |
| 482 | err = js_create_int32(env, status, &result); | |
| 483 | assert(err == 0); | |
| 484 | ||
| 485 | return result; | |
| 486 | } | |
| 487 | ||
| 488 | static js_value_t * | |
| 489 | bare_fs_open(js_env_t *env, js_callback_info_t *info) { | |
| 490 | return bare_fs__open(env, info, bare_fs_async); | |
| 491 | } | |
| 492 | ||
| 493 | static js_value_t * | |
| 494 | bare_fs_open_sync(js_env_t *env, js_callback_info_t *info) { | |
| 495 | return bare_fs__open(env, info, bare_fs_sync); | |
| 496 | } | |
| 497 | ||
| 498 | static void | |
| 499 | bare_fs__on_close(uv_fs_t *handle) { | |
| 500 | bare_fs__on_request_result(handle); | |
| 501 | } | |
| 502 | ||
| 503 | static inline js_value_t * | |
| 504 | bare_fs__close(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 505 | int err; | |
| 506 | ||
| 507 | size_t argc = 2; | |
| 508 | js_value_t *argv[2]; | |
| 509 | ||
| 510 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 511 | assert(err == 0); | |
| 512 | ||
| 513 | assert(argc == 2); | |
| 514 | ||
| 515 | bare_fs_req_t *req; | |
| 516 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 517 | assert(err == 0); | |
| 518 | ||
| 519 | uint32_t fd; | |
| 520 | err = js_get_value_uint32(env, argv[1], &fd); | |
| 521 | assert(err == 0); | |
| 522 | ||
| 523 | uv_loop_t *loop; | |
| 524 | err = js_get_env_loop(env, &loop); | |
| 525 | assert(err == 0); | |
| 526 | ||
| 527 | err = uv_fs_close(loop, &req->handle, fd, async ? bare_fs__on_close : NULL); | |
| 528 | (void) err; | |
| 529 | ||
| 530 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 531 | (void) err; | |
| 532 | ||
| 533 | return NULL; | |
| 534 | } | |
| 535 | ||
| 536 | static js_value_t * | |
| 537 | bare_fs_close(js_env_t *env, js_callback_info_t *info) { | |
| 538 | return bare_fs__close(env, info, bare_fs_async); | |
| 539 | } | |
| 540 | ||
| 541 | static js_value_t * | |
| 542 | bare_fs_close_sync(js_env_t *env, js_callback_info_t *info) { | |
| 543 | return bare_fs__close(env, info, bare_fs_sync); | |
| 544 | } | |
| 545 | ||
| 546 | static void | |
| 547 | bare_fs__on_access(uv_fs_t *handle) { | |
| 548 | bare_fs__on_request_result(handle); | |
| 549 | } | |
| 550 | ||
| 551 | static inline js_value_t * | |
| 552 | bare_fs__access(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 553 | int err; | |
| 554 | ||
| 555 | size_t argc = 3; | |
| 556 | js_value_t *argv[3]; | |
| 557 | ||
| 558 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 559 | assert(err == 0); | |
| 560 | ||
| 561 | assert(argc == 3); | |
| 562 | ||
| 563 | bare_fs_req_t *req; | |
| 564 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 565 | assert(err == 0); | |
| 566 | ||
| 567 | bare_fs_path_t path; | |
| 568 | err = js_get_value_string_utf8(env, argv[1], path, sizeof(bare_fs_path_t), NULL); | |
| 569 | assert(err == 0); | |
| 570 | ||
| 571 | int32_t mode; | |
| 572 | err = js_get_value_int32(env, argv[2], &mode); | |
| 573 | assert(err == 0); | |
| 574 | ||
| 575 | uv_loop_t *loop; | |
| 576 | err = js_get_env_loop(env, &loop); | |
| 577 | assert(err == 0); | |
| 578 | ||
| 579 | err = uv_fs_access(loop, &req->handle, (char *) path, mode, async ? bare_fs__on_access : NULL); | |
| 580 | (void) err; | |
| 581 | ||
| 582 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 583 | (void) err; | |
| 584 | ||
| 585 | return NULL; | |
| 586 | } | |
| 587 | ||
| 588 | static js_value_t * | |
| 589 | bare_fs_access(js_env_t *env, js_callback_info_t *info) { | |
| 590 | return bare_fs__access(env, info, bare_fs_async); | |
| 591 | } | |
| 592 | ||
| 593 | static js_value_t * | |
| 594 | bare_fs_access_sync(js_env_t *env, js_callback_info_t *info) { | |
| 595 | return bare_fs__access(env, info, bare_fs_sync); | |
| 596 | } | |
| 597 | ||
| 598 | static void | |
| 599 | bare_fs__on_read(uv_fs_t *handle) { | |
| 600 | bare_fs__on_request_result(handle); | |
| 601 | } | |
| 602 | ||
| 603 | static inline js_value_t * | |
| 604 | bare_fs__read(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 605 | int err; | |
| 606 | ||
| 607 | size_t argc = 6; | |
| 608 | js_value_t *argv[6]; | |
| 609 | ||
| 610 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 611 | assert(err == 0); | |
| 612 | ||
| 613 | assert(argc == 6); | |
| 614 | ||
| 615 | bare_fs_req_t *req; | |
| 616 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 617 | assert(err == 0); | |
| 618 | ||
| 619 | uint32_t fd; | |
| 620 | err = js_get_value_uint32(env, argv[1], &fd); | |
| 621 | assert(err == 0); | |
| 622 | ||
| 623 | uint8_t *data; | |
| 624 | size_t data_len; | |
| 625 | err = js_get_typedarray_info(env, argv[2], NULL, (void **) &data, &data_len, NULL, NULL); | |
| 626 | assert(err == 0); | |
| 627 | ||
| 628 | uint32_t offset; | |
| 629 | err = js_get_value_uint32(env, argv[3], &offset); | |
| 630 | assert(err == 0); | |
| 631 | ||
| 632 | uint32_t len; | |
| 633 | err = js_get_value_uint32(env, argv[4], &len); | |
| 634 | assert(err == 0); | |
| 635 | ||
| 636 | if (offset >= data_len) len = 0; | |
| 637 | else if (offset + len >= data_len) len = data_len - offset; | |
| 638 | ||
| 639 | int64_t pos; | |
| 640 | err = js_get_value_int64(env, argv[5], &pos); | |
| 641 | assert(err == 0); | |
| 642 | ||
| 643 | uv_loop_t *loop; | |
| 644 | err = js_get_env_loop(env, &loop); | |
| 645 | assert(err == 0); | |
| 646 | ||
| 647 | uv_buf_t buf = uv_buf_init((void *) (data + offset), len); | |
| 648 | ||
| 649 | err = uv_fs_read(loop, &req->handle, fd, &buf, 1, pos, async ? bare_fs__on_read : NULL); | |
| 650 | (void) err; | |
| 651 | ||
| 652 | int status; | |
| 653 | err = bare_fs__request_pending(env, req, async, &status); | |
| 654 | if (err != 1) return NULL; | |
| 655 | ||
| 656 | js_value_t *result; | |
| 657 | err = js_create_int32(env, status, &result); | |
| 658 | assert(err == 0); | |
| 659 | ||
| 660 | return result; | |
| 661 | } | |
| 662 | ||
| 663 | static js_value_t * | |
| 664 | bare_fs_read(js_env_t *env, js_callback_info_t *info) { | |
| 665 | return bare_fs__read(env, info, bare_fs_async); | |
| 666 | } | |
| 667 | ||
| 668 | static js_value_t * | |
| 669 | bare_fs_read_sync(js_env_t *env, js_callback_info_t *info) { | |
| 670 | return bare_fs__read(env, info, bare_fs_sync); | |
| 671 | } | |
| 672 | ||
| 673 | static void | |
| 674 | bare_fs__on_readv(uv_fs_t *handle) { | |
| 675 | bare_fs__on_request_result(handle); | |
| 676 | } | |
| 677 | ||
| 678 | static inline js_value_t * | |
| 679 | bare_fs__readv(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 680 | int err; | |
| 681 | ||
| 682 | size_t argc = 4; | |
| 683 | js_value_t *argv[4]; | |
| 684 | ||
| 685 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 686 | assert(err == 0); | |
| 687 | ||
| 688 | assert(argc == 4); | |
| 689 | ||
| 690 | bare_fs_req_t *req; | |
| 691 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 692 | assert(err == 0); | |
| 693 | ||
| 694 | uint32_t fd; | |
| 695 | err = js_get_value_uint32(env, argv[1], &fd); | |
| 696 | assert(err == 0); | |
| 697 | ||
| 698 | int64_t pos; | |
| 699 | err = js_get_value_int64(env, argv[3], &pos); | |
| 700 | assert(err == 0); | |
| 701 | ||
| 702 | uv_loop_t *loop; | |
| 703 | err = js_get_env_loop(env, &loop); | |
| 704 | assert(err == 0); | |
| 705 | ||
| 706 | js_value_t *arr = argv[2]; | |
| 707 | ||
| 708 | uint32_t bufs_len; | |
| 709 | err = js_get_array_length(env, arr, &bufs_len); | |
| 710 | assert(err == 0); | |
| 711 | ||
| 712 | uv_buf_t *bufs = malloc(sizeof(uv_buf_t) * bufs_len); | |
| 713 | ||
| 714 | js_value_t **elements = malloc(bufs_len * sizeof(js_value_t *)); | |
| 715 | err = js_get_array_elements(env, arr, elements, bufs_len, 0, NULL); | |
| 716 | assert(err == 0); | |
| 717 | ||
| 718 | for (uint32_t i = 0; i < bufs_len; i++) { | |
| 719 | js_value_t *item = elements[i]; | |
| 720 | ||
| 721 | uv_buf_t *buf = &bufs[i]; | |
| 722 | err = js_get_typedarray_info(env, item, NULL, (void **) &buf->base, (size_t *) &buf->len, NULL, NULL); | |
| 723 | assert(err == 0); | |
| 724 | } | |
| 725 | ||
| 726 | err = uv_fs_read(loop, &req->handle, fd, bufs, bufs_len, pos, async ? bare_fs__on_readv : NULL); | |
| 727 | (void) err; | |
| 728 | ||
| 729 | free(elements); | |
| 730 | free(bufs); | |
| 731 | ||
| 732 | int status; | |
| 733 | err = bare_fs__request_pending(env, req, async, &status); | |
| 734 | if (err != 1) return NULL; | |
| 735 | ||
| 736 | js_value_t *result; | |
| 737 | err = js_create_int32(env, status, &result); | |
| 738 | assert(err == 0); | |
| 739 | ||
| 740 | return result; | |
| 741 | } | |
| 742 | ||
| 743 | static js_value_t * | |
| 744 | bare_fs_readv(js_env_t *env, js_callback_info_t *info) { | |
| 745 | return bare_fs__readv(env, info, bare_fs_async); | |
| 746 | } | |
| 747 | ||
| 748 | static js_value_t * | |
| 749 | bare_fs_readv_sync(js_env_t *env, js_callback_info_t *info) { | |
| 750 | return bare_fs__readv(env, info, bare_fs_sync); | |
| 751 | } | |
| 752 | ||
| 753 | static void | |
| 754 | bare_fs__on_write(uv_fs_t *handle) { | |
| 755 | bare_fs__on_request_result(handle); | |
| 756 | } | |
| 757 | ||
| 758 | static inline js_value_t * | |
| 759 | bare_fs__write(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 760 | int err; | |
| 761 | ||
| 762 | size_t argc = 6; | |
| 763 | js_value_t *argv[6]; | |
| 764 | ||
| 765 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 766 | assert(err == 0); | |
| 767 | ||
| 768 | assert(argc == 6); | |
| 769 | ||
| 770 | bare_fs_req_t *req; | |
| 771 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 772 | assert(err == 0); | |
| 773 | ||
| 774 | uint32_t fd; | |
| 775 | err = js_get_value_uint32(env, argv[1], &fd); | |
| 776 | assert(err == 0); | |
| 777 | ||
| 778 | uint8_t *data; | |
| 779 | size_t data_len; | |
| 780 | err = js_get_typedarray_info(env, argv[2], NULL, (void **) &data, &data_len, NULL, NULL); | |
| 781 | assert(err == 0); | |
| 782 | ||
| 783 | uint32_t offset; | |
| 784 | err = js_get_value_uint32(env, argv[3], &offset); | |
| 785 | assert(err == 0); | |
| 786 | ||
| 787 | uint32_t len; | |
| 788 | err = js_get_value_uint32(env, argv[4], &len); | |
| 789 | assert(err == 0); | |
| 790 | ||
| 791 | if (offset >= data_len) len = 0; | |
| 792 | else if (offset + len >= data_len) len = data_len - offset; | |
| 793 | ||
| 794 | int64_t pos; | |
| 795 | err = js_get_value_int64(env, argv[5], &pos); | |
| 796 | assert(err == 0); | |
| 797 | ||
| 798 | uv_loop_t *loop; | |
| 799 | err = js_get_env_loop(env, &loop); | |
| 800 | assert(err == 0); | |
| 801 | ||
| 802 | uv_buf_t buf = uv_buf_init((void *) (data + offset), len); | |
| 803 | ||
| 804 | err = uv_fs_write(loop, &req->handle, fd, &buf, 1, pos, async ? bare_fs__on_write : NULL); | |
| 805 | (void) err; | |
| 806 | ||
| 807 | int status; | |
| 808 | err = bare_fs__request_pending(env, req, async, &status); | |
| 809 | if (err != 1) return NULL; | |
| 810 | ||
| 811 | js_value_t *result; | |
| 812 | err = js_create_int32(env, status, &result); | |
| 813 | assert(err == 0); | |
| 814 | ||
| 815 | return result; | |
| 816 | } | |
| 817 | ||
| 818 | static js_value_t * | |
| 819 | bare_fs_write(js_env_t *env, js_callback_info_t *info) { | |
| 820 | return bare_fs__write(env, info, bare_fs_async); | |
| 821 | } | |
| 822 | ||
| 823 | static js_value_t * | |
| 824 | bare_fs_write_sync(js_env_t *env, js_callback_info_t *info) { | |
| 825 | return bare_fs__write(env, info, bare_fs_sync); | |
| 826 | } | |
| 827 | ||
| 828 | static void | |
| 829 | bare_fs__on_writev(uv_fs_t *handle) { | |
| 830 | bare_fs__on_request_result(handle); | |
| 831 | } | |
| 832 | ||
| 833 | static inline js_value_t * | |
| 834 | bare_fs__writev(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 835 | int err; | |
| 836 | ||
| 837 | size_t argc = 4; | |
| 838 | js_value_t *argv[4]; | |
| 839 | ||
| 840 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 841 | assert(err == 0); | |
| 842 | ||
| 843 | assert(argc == 4); | |
| 844 | ||
| 845 | bare_fs_req_t *req; | |
| 846 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 847 | assert(err == 0); | |
| 848 | ||
| 849 | uint32_t fd; | |
| 850 | err = js_get_value_uint32(env, argv[1], &fd); | |
| 851 | assert(err == 0); | |
| 852 | ||
| 853 | int64_t pos; | |
| 854 | err = js_get_value_int64(env, argv[3], &pos); | |
| 855 | assert(err == 0); | |
| 856 | ||
| 857 | uv_loop_t *loop; | |
| 858 | err = js_get_env_loop(env, &loop); | |
| 859 | assert(err == 0); | |
| 860 | ||
| 861 | js_value_t *arr = argv[2]; | |
| 862 | ||
| 863 | uint32_t bufs_len; | |
| 864 | err = js_get_array_length(env, arr, &bufs_len); | |
| 865 | assert(err == 0); | |
| 866 | ||
| 867 | uv_buf_t *bufs = malloc(sizeof(uv_buf_t) * bufs_len); | |
| 868 | ||
| 869 | js_value_t **elements = malloc(bufs_len * sizeof(js_value_t *)); | |
| 870 | ||
| 871 | err = js_get_array_elements(env, arr, elements, bufs_len, 0, NULL); | |
| 872 | assert(err == 0); | |
| 873 | ||
| 874 | for (uint32_t i = 0; i < bufs_len; i++) { | |
| 875 | js_value_t *item = elements[i]; | |
| 876 | ||
| 877 | uv_buf_t *buf = &bufs[i]; | |
| 878 | err = js_get_typedarray_info(env, item, NULL, (void **) &buf->base, (size_t *) &buf->len, NULL, NULL); | |
| 879 | assert(err == 0); | |
| 880 | } | |
| 881 | ||
| 882 | err = uv_fs_write(loop, &req->handle, fd, bufs, bufs_len, pos, async ? bare_fs__on_writev : NULL); | |
| 883 | (void) err; | |
| 884 | ||
| 885 | free(elements); | |
| 886 | free(bufs); | |
| 887 | ||
| 888 | int status; | |
| 889 | err = bare_fs__request_pending(env, req, async, &status); | |
| 890 | if (err != 1) return NULL; | |
| 891 | ||
| 892 | js_value_t *result; | |
| 893 | err = js_create_int32(env, status, &result); | |
| 894 | assert(err == 0); | |
| 895 | ||
| 896 | return result; | |
| 897 | } | |
| 898 | ||
| 899 | static js_value_t * | |
| 900 | bare_fs_writev(js_env_t *env, js_callback_info_t *info) { | |
| 901 | return bare_fs__writev(env, info, bare_fs_async); | |
| 902 | } | |
| 903 | ||
| 904 | static js_value_t * | |
| 905 | bare_fs_writev_sync(js_env_t *env, js_callback_info_t *info) { | |
| 906 | return bare_fs__writev(env, info, bare_fs_sync); | |
| 907 | } | |
| 908 | ||
| 909 | static void | |
| 910 | bare_fs__on_ftruncate(uv_fs_t *handle) { | |
| 911 | bare_fs__on_request_result(handle); | |
| 912 | } | |
| 913 | ||
| 914 | static inline js_value_t * | |
| 915 | bare_fs__ftruncate(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 916 | int err; | |
| 917 | ||
| 918 | size_t argc = 3; | |
| 919 | js_value_t *argv[3]; | |
| 920 | ||
| 921 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 922 | assert(err == 0); | |
| 923 | ||
| 924 | assert(argc == 3); | |
| 925 | ||
| 926 | bare_fs_req_t *req; | |
| 927 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 928 | assert(err == 0); | |
| 929 | ||
| 930 | uint32_t fd; | |
| 931 | err = js_get_value_uint32(env, argv[1], &fd); | |
| 932 | assert(err == 0); | |
| 933 | ||
| 934 | int64_t len; | |
| 935 | err = js_get_value_int64(env, argv[2], &len); | |
| 936 | assert(err == 0); | |
| 937 | ||
| 938 | uv_loop_t *loop; | |
| 939 | err = js_get_env_loop(env, &loop); | |
| 940 | assert(err == 0); | |
| 941 | ||
| 942 | err = uv_fs_ftruncate(loop, &req->handle, fd, len, async ? bare_fs__on_ftruncate : NULL); | |
| 943 | (void) err; | |
| 944 | ||
| 945 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 946 | (void) err; | |
| 947 | ||
| 948 | return NULL; | |
| 949 | } | |
| 950 | ||
| 951 | static js_value_t * | |
| 952 | bare_fs_ftruncate(js_env_t *env, js_callback_info_t *info) { | |
| 953 | return bare_fs__ftruncate(env, info, bare_fs_async); | |
| 954 | } | |
| 955 | ||
| 956 | static js_value_t * | |
| 957 | bare_fs_ftruncate_sync(js_env_t *env, js_callback_info_t *info) { | |
| 958 | return bare_fs__ftruncate(env, info, bare_fs_sync); | |
| 959 | } | |
| 960 | ||
| 961 | static void | |
| 962 | bare_fs__on_chmod(uv_fs_t *handle) { | |
| 963 | bare_fs__on_request_result(handle); | |
| 964 | } | |
| 965 | ||
| 966 | static inline js_value_t * | |
| 967 | bare_fs__chmod(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 968 | int err; | |
| 969 | ||
| 970 | size_t argc = 3; | |
| 971 | js_value_t *argv[3]; | |
| 972 | ||
| 973 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 974 | assert(err == 0); | |
| 975 | ||
| 976 | assert(argc == 3); | |
| 977 | ||
| 978 | bare_fs_req_t *req; | |
| 979 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 980 | assert(err == 0); | |
| 981 | ||
| 982 | bare_fs_path_t path; | |
| 983 | err = js_get_value_string_utf8(env, argv[1], path, sizeof(bare_fs_path_t), NULL); | |
| 984 | assert(err == 0); | |
| 985 | ||
| 986 | int32_t mode; | |
| 987 | err = js_get_value_int32(env, argv[2], &mode); | |
| 988 | assert(err == 0); | |
| 989 | ||
| 990 | uv_loop_t *loop; | |
| 991 | err = js_get_env_loop(env, &loop); | |
| 992 | assert(err == 0); | |
| 993 | ||
| 994 | err = uv_fs_chmod(loop, &req->handle, (char *) path, mode, async ? bare_fs__on_chmod : NULL); | |
| 995 | (void) err; | |
| 996 | ||
| 997 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 998 | (void) err; | |
| 999 | ||
| 1000 | return NULL; | |
| 1001 | } | |
| 1002 | ||
| 1003 | static js_value_t * | |
| 1004 | bare_fs_chmod(js_env_t *env, js_callback_info_t *info) { | |
| 1005 | return bare_fs__chmod(env, info, bare_fs_async); | |
| 1006 | } | |
| 1007 | ||
| 1008 | static js_value_t * | |
| 1009 | bare_fs_chmod_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1010 | return bare_fs__chmod(env, info, bare_fs_sync); | |
| 1011 | } | |
| 1012 | ||
| 1013 | static void | |
| 1014 | bare_fs__on_fchmod(uv_fs_t *handle) { | |
| 1015 | bare_fs__on_request_result(handle); | |
| 1016 | } | |
| 1017 | ||
| 1018 | static inline js_value_t * | |
| 1019 | bare_fs__fchmod(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1020 | int err; | |
| 1021 | ||
| 1022 | size_t argc = 3; | |
| 1023 | js_value_t *argv[3]; | |
| 1024 | ||
| 1025 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1026 | assert(err == 0); | |
| 1027 | ||
| 1028 | assert(argc == 3); | |
| 1029 | ||
| 1030 | bare_fs_req_t *req; | |
| 1031 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1032 | assert(err == 0); | |
| 1033 | ||
| 1034 | uint32_t fd; | |
| 1035 | err = js_get_value_uint32(env, argv[1], &fd); | |
| 1036 | assert(err == 0); | |
| 1037 | ||
| 1038 | int32_t mode; | |
| 1039 | err = js_get_value_int32(env, argv[2], &mode); | |
| 1040 | assert(err == 0); | |
| 1041 | ||
| 1042 | uv_loop_t *loop; | |
| 1043 | err = js_get_env_loop(env, &loop); | |
| 1044 | assert(err == 0); | |
| 1045 | ||
| 1046 | err = uv_fs_fchmod(loop, &req->handle, fd, mode, async ? bare_fs__on_fchmod : NULL); | |
| 1047 | (void) err; | |
| 1048 | ||
| 1049 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1050 | (void) err; | |
| 1051 | ||
| 1052 | return NULL; | |
| 1053 | } | |
| 1054 | ||
| 1055 | static js_value_t * | |
| 1056 | bare_fs_fchmod(js_env_t *env, js_callback_info_t *info) { | |
| 1057 | return bare_fs__fchmod(env, info, bare_fs_async); | |
| 1058 | } | |
| 1059 | ||
| 1060 | static js_value_t * | |
| 1061 | bare_fs_fchmod_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1062 | return bare_fs__fchmod(env, info, bare_fs_sync); | |
| 1063 | } | |
| 1064 | ||
| 1065 | static void | |
| 1066 | bare_fs__on_utimes(uv_fs_t *handle) { | |
| 1067 | bare_fs__on_request_result(handle); | |
| 1068 | } | |
| 1069 | ||
| 1070 | static inline js_value_t * | |
| 1071 | bare_fs__utimes(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1072 | int err; | |
| 1073 | ||
| 1074 | size_t argc = 4; | |
| 1075 | js_value_t *argv[4]; | |
| 1076 | ||
| 1077 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1078 | assert(err == 0); | |
| 1079 | ||
| 1080 | assert(argc == 4); | |
| 1081 | ||
| 1082 | bare_fs_req_t *req; | |
| 1083 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1084 | assert(err == 0); | |
| 1085 | ||
| 1086 | bare_fs_path_t path; | |
| 1087 | err = js_get_value_string_utf8(env, argv[1], path, sizeof(bare_fs_path_t), NULL); | |
| 1088 | assert(err == 0); | |
| 1089 | ||
| 1090 | double atime; | |
| 1091 | err = js_get_value_double(env, argv[2], &atime); | |
| 1092 | assert(err == 0); | |
| 1093 | ||
| 1094 | double mtime; | |
| 1095 | err = js_get_value_double(env, argv[3], &mtime); | |
| 1096 | assert(err == 0); | |
| 1097 | ||
| 1098 | uv_loop_t *loop; | |
| 1099 | err = js_get_env_loop(env, &loop); | |
| 1100 | assert(err == 0); | |
| 1101 | ||
| 1102 | err = uv_fs_utime(loop, &req->handle, (char *) path, atime, mtime, async ? bare_fs__on_utimes : NULL); | |
| 1103 | (void) err; | |
| 1104 | ||
| 1105 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1106 | (void) err; | |
| 1107 | ||
| 1108 | return NULL; | |
| 1109 | } | |
| 1110 | ||
| 1111 | static js_value_t * | |
| 1112 | bare_fs_utimes(js_env_t *env, js_callback_info_t *info) { | |
| 1113 | return bare_fs__utimes(env, info, bare_fs_async); | |
| 1114 | } | |
| 1115 | ||
| 1116 | static js_value_t * | |
| 1117 | bare_fs_utimes_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1118 | return bare_fs__utimes(env, info, bare_fs_sync); | |
| 1119 | } | |
| 1120 | ||
| 1121 | static void | |
| 1122 | bare_fs__on_rename(uv_fs_t *handle) { | |
| 1123 | bare_fs__on_request_result(handle); | |
| 1124 | } | |
| 1125 | ||
| 1126 | static inline js_value_t * | |
| 1127 | bare_fs__rename(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1128 | int err; | |
| 1129 | ||
| 1130 | size_t argc = 3; | |
| 1131 | js_value_t *argv[3]; | |
| 1132 | ||
| 1133 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1134 | assert(err == 0); | |
| 1135 | ||
| 1136 | assert(argc == 3); | |
| 1137 | ||
| 1138 | bare_fs_req_t *req; | |
| 1139 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1140 | assert(err == 0); | |
| 1141 | ||
| 1142 | bare_fs_path_t src; | |
| 1143 | err = js_get_value_string_utf8(env, argv[1], src, sizeof(bare_fs_path_t), NULL); | |
| 1144 | assert(err == 0); | |
| 1145 | ||
| 1146 | bare_fs_path_t dst; | |
| 1147 | err = js_get_value_string_utf8(env, argv[2], dst, sizeof(bare_fs_path_t), NULL); | |
| 1148 | assert(err == 0); | |
| 1149 | ||
| 1150 | uv_loop_t *loop; | |
| 1151 | err = js_get_env_loop(env, &loop); | |
| 1152 | assert(err == 0); | |
| 1153 | ||
| 1154 | err = uv_fs_rename(loop, &req->handle, (char *) src, (char *) dst, async ? bare_fs__on_rename : NULL); | |
| 1155 | (void) err; | |
| 1156 | ||
| 1157 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1158 | (void) err; | |
| 1159 | ||
| 1160 | return NULL; | |
| 1161 | } | |
| 1162 | ||
| 1163 | static js_value_t * | |
| 1164 | bare_fs_rename(js_env_t *env, js_callback_info_t *info) { | |
| 1165 | return bare_fs__rename(env, info, bare_fs_async); | |
| 1166 | } | |
| 1167 | ||
| 1168 | static js_value_t * | |
| 1169 | bare_fs_rename_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1170 | return bare_fs__rename(env, info, bare_fs_sync); | |
| 1171 | } | |
| 1172 | ||
| 1173 | static void | |
| 1174 | bare_fs__on_copyfile(uv_fs_t *handle) { | |
| 1175 | bare_fs__on_request_result(handle); | |
| 1176 | } | |
| 1177 | ||
| 1178 | static inline js_value_t * | |
| 1179 | bare_fs__copyfile(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1180 | int err; | |
| 1181 | ||
| 1182 | size_t argc = 4; | |
| 1183 | js_value_t *argv[4]; | |
| 1184 | ||
| 1185 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1186 | assert(err == 0); | |
| 1187 | ||
| 1188 | assert(argc == 4); | |
| 1189 | ||
| 1190 | bare_fs_req_t *req; | |
| 1191 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1192 | assert(err == 0); | |
| 1193 | ||
| 1194 | bare_fs_path_t src; | |
| 1195 | err = js_get_value_string_utf8(env, argv[1], src, sizeof(bare_fs_path_t), NULL); | |
| 1196 | assert(err == 0); | |
| 1197 | ||
| 1198 | bare_fs_path_t dst; | |
| 1199 | err = js_get_value_string_utf8(env, argv[2], dst, sizeof(bare_fs_path_t), NULL); | |
| 1200 | assert(err == 0); | |
| 1201 | ||
| 1202 | int32_t mode; | |
| 1203 | err = js_get_value_int32(env, argv[3], &mode); | |
| 1204 | assert(err == 0); | |
| 1205 | ||
| 1206 | uv_loop_t *loop; | |
| 1207 | err = js_get_env_loop(env, &loop); | |
| 1208 | assert(err == 0); | |
| 1209 | ||
| 1210 | err = uv_fs_copyfile(loop, &req->handle, (char *) src, (char *) dst, mode, async ? bare_fs__on_copyfile : NULL); | |
| 1211 | (void) err; | |
| 1212 | ||
| 1213 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1214 | (void) err; | |
| 1215 | ||
| 1216 | return NULL; | |
| 1217 | } | |
| 1218 | ||
| 1219 | static js_value_t * | |
| 1220 | bare_fs_copyfile(js_env_t *env, js_callback_info_t *info) { | |
| 1221 | return bare_fs__copyfile(env, info, bare_fs_async); | |
| 1222 | } | |
| 1223 | ||
| 1224 | static js_value_t * | |
| 1225 | bare_fs_copyfile_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1226 | return bare_fs__copyfile(env, info, bare_fs_sync); | |
| 1227 | } | |
| 1228 | ||
| 1229 | static void | |
| 1230 | bare_fs__on_mkdir(uv_fs_t *handle) { | |
| 1231 | bare_fs__on_request_result(handle); | |
| 1232 | } | |
| 1233 | ||
| 1234 | static inline js_value_t * | |
| 1235 | bare_fs__mkdir(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1236 | int err; | |
| 1237 | ||
| 1238 | size_t argc = 3; | |
| 1239 | js_value_t *argv[3]; | |
| 1240 | ||
| 1241 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1242 | assert(err == 0); | |
| 1243 | ||
| 1244 | assert(argc == 3); | |
| 1245 | ||
| 1246 | bare_fs_req_t *req; | |
| 1247 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1248 | assert(err == 0); | |
| 1249 | ||
| 1250 | bare_fs_path_t path; | |
| 1251 | err = js_get_value_string_utf8(env, argv[1], path, sizeof(bare_fs_path_t), NULL); | |
| 1252 | assert(err == 0); | |
| 1253 | ||
| 1254 | int32_t mode; | |
| 1255 | err = js_get_value_int32(env, argv[2], &mode); | |
| 1256 | assert(err == 0); | |
| 1257 | ||
| 1258 | uv_loop_t *loop; | |
| 1259 | err = js_get_env_loop(env, &loop); | |
| 1260 | assert(err == 0); | |
| 1261 | ||
| 1262 | err = uv_fs_mkdir(loop, &req->handle, (char *) path, mode, async ? bare_fs__on_mkdir : NULL); | |
| 1263 | (void) err; | |
| 1264 | ||
| 1265 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1266 | (void) err; | |
| 1267 | ||
| 1268 | return NULL; | |
| 1269 | } | |
| 1270 | ||
| 1271 | static js_value_t * | |
| 1272 | bare_fs_mkdir(js_env_t *env, js_callback_info_t *info) { | |
| 1273 | return bare_fs__mkdir(env, info, bare_fs_async); | |
| 1274 | } | |
| 1275 | ||
| 1276 | static js_value_t * | |
| 1277 | bare_fs_mkdir_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1278 | return bare_fs__mkdir(env, info, bare_fs_sync); | |
| 1279 | } | |
| 1280 | ||
| 1281 | static void | |
| 1282 | bare_fs__on_rmdir(uv_fs_t *handle) { | |
| 1283 | bare_fs__on_request_result(handle); | |
| 1284 | } | |
| 1285 | ||
| 1286 | static inline js_value_t * | |
| 1287 | bare_fs__rmdir(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1288 | int err; | |
| 1289 | ||
| 1290 | size_t argc = 2; | |
| 1291 | js_value_t *argv[2]; | |
| 1292 | ||
| 1293 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1294 | assert(err == 0); | |
| 1295 | ||
| 1296 | assert(argc == 2); | |
| 1297 | ||
| 1298 | bare_fs_req_t *req; | |
| 1299 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1300 | assert(err == 0); | |
| 1301 | ||
| 1302 | bare_fs_path_t path; | |
| 1303 | err = js_get_value_string_utf8(env, argv[1], path, sizeof(bare_fs_path_t), NULL); | |
| 1304 | assert(err == 0); | |
| 1305 | ||
| 1306 | uv_loop_t *loop; | |
| 1307 | err = js_get_env_loop(env, &loop); | |
| 1308 | assert(err == 0); | |
| 1309 | ||
| 1310 | err = uv_fs_rmdir(loop, &req->handle, (char *) path, async ? bare_fs__on_rmdir : NULL); | |
| 1311 | (void) err; | |
| 1312 | ||
| 1313 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1314 | (void) err; | |
| 1315 | ||
| 1316 | return NULL; | |
| 1317 | } | |
| 1318 | ||
| 1319 | static js_value_t * | |
| 1320 | bare_fs_rmdir(js_env_t *env, js_callback_info_t *info) { | |
| 1321 | return bare_fs__rmdir(env, info, bare_fs_async); | |
| 1322 | } | |
| 1323 | ||
| 1324 | static js_value_t * | |
| 1325 | bare_fs_rmdir_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1326 | return bare_fs__rmdir(env, info, bare_fs_sync); | |
| 1327 | } | |
| 1328 | ||
| 1329 | static void | |
| 1330 | bare_fs__on_stat(uv_fs_t *handle) { | |
| 1331 | bare_fs__on_request_result(handle); | |
| 1332 | } | |
| 1333 | ||
| 1334 | static inline js_value_t * | |
| 1335 | bare_fs__stat(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1336 | int err; | |
| 1337 | ||
| 1338 | size_t argc = 2; | |
| 1339 | js_value_t *argv[2]; | |
| 1340 | ||
| 1341 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1342 | assert(err == 0); | |
| 1343 | ||
| 1344 | assert(argc == 2); | |
| 1345 | ||
| 1346 | bare_fs_req_t *req; | |
| 1347 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1348 | assert(err == 0); | |
| 1349 | ||
| 1350 | bare_fs_path_t path; | |
| 1351 | err = js_get_value_string_utf8(env, argv[1], path, sizeof(bare_fs_path_t), NULL); | |
| 1352 | assert(err == 0); | |
| 1353 | ||
| 1354 | uv_loop_t *loop; | |
| 1355 | err = js_get_env_loop(env, &loop); | |
| 1356 | assert(err == 0); | |
| 1357 | ||
| 1358 | err = uv_fs_stat(loop, &req->handle, (char *) path, async ? bare_fs__on_stat : NULL); | |
| 1359 | (void) err; | |
| 1360 | ||
| 1361 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1362 | (void) err; | |
| 1363 | ||
| 1364 | return NULL; | |
| 1365 | } | |
| 1366 | ||
| 1367 | static js_value_t * | |
| 1368 | bare_fs_stat(js_env_t *env, js_callback_info_t *info) { | |
| 1369 | return bare_fs__stat(env, info, bare_fs_async); | |
| 1370 | } | |
| 1371 | ||
| 1372 | static js_value_t * | |
| 1373 | bare_fs_stat_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1374 | return bare_fs__stat(env, info, bare_fs_sync); | |
| 1375 | } | |
| 1376 | ||
| 1377 | static void | |
| 1378 | bare_fs__on_lstat(uv_fs_t *handle) { | |
| 1379 | bare_fs__on_request_result(handle); | |
| 1380 | } | |
| 1381 | ||
| 1382 | static inline js_value_t * | |
| 1383 | bare_fs__lstat(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1384 | int err; | |
| 1385 | ||
| 1386 | size_t argc = 2; | |
| 1387 | js_value_t *argv[2]; | |
| 1388 | ||
| 1389 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1390 | assert(err == 0); | |
| 1391 | ||
| 1392 | assert(argc == 2); | |
| 1393 | ||
| 1394 | bare_fs_req_t *req; | |
| 1395 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1396 | assert(err == 0); | |
| 1397 | ||
| 1398 | bare_fs_path_t path; | |
| 1399 | err = js_get_value_string_utf8(env, argv[1], path, sizeof(bare_fs_path_t), NULL); | |
| 1400 | assert(err == 0); | |
| 1401 | ||
| 1402 | uv_loop_t *loop; | |
| 1403 | err = js_get_env_loop(env, &loop); | |
| 1404 | assert(err == 0); | |
| 1405 | ||
| 1406 | err = uv_fs_lstat(loop, &req->handle, (char *) path, async ? bare_fs__on_lstat : NULL); | |
| 1407 | (void) err; | |
| 1408 | ||
| 1409 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1410 | (void) err; | |
| 1411 | ||
| 1412 | return NULL; | |
| 1413 | } | |
| 1414 | ||
| 1415 | static js_value_t * | |
| 1416 | bare_fs_lstat(js_env_t *env, js_callback_info_t *info) { | |
| 1417 | return bare_fs__lstat(env, info, bare_fs_async); | |
| 1418 | } | |
| 1419 | ||
| 1420 | static js_value_t * | |
| 1421 | bare_fs_lstat_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1422 | return bare_fs__lstat(env, info, bare_fs_sync); | |
| 1423 | } | |
| 1424 | ||
| 1425 | static void | |
| 1426 | bare_fs__on_fstat(uv_fs_t *handle) { | |
| 1427 | bare_fs__on_request_result(handle); | |
| 1428 | } | |
| 1429 | ||
| 1430 | static inline js_value_t * | |
| 1431 | bare_fs__fstat(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1432 | int err; | |
| 1433 | ||
| 1434 | size_t argc = 2; | |
| 1435 | js_value_t *argv[2]; | |
| 1436 | ||
| 1437 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1438 | assert(err == 0); | |
| 1439 | ||
| 1440 | assert(argc == 2); | |
| 1441 | ||
| 1442 | bare_fs_req_t *req; | |
| 1443 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1444 | assert(err == 0); | |
| 1445 | ||
| 1446 | uint32_t fd; | |
| 1447 | err = js_get_value_uint32(env, argv[1], &fd); | |
| 1448 | assert(err == 0); | |
| 1449 | ||
| 1450 | uv_loop_t *loop; | |
| 1451 | err = js_get_env_loop(env, &loop); | |
| 1452 | assert(err == 0); | |
| 1453 | ||
| 1454 | err = uv_fs_fstat(loop, &req->handle, fd, async ? bare_fs__on_fstat : NULL); | |
| 1455 | (void) err; | |
| 1456 | ||
| 1457 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1458 | (void) err; | |
| 1459 | ||
| 1460 | return NULL; | |
| 1461 | } | |
| 1462 | ||
| 1463 | static js_value_t * | |
| 1464 | bare_fs_fstat(js_env_t *env, js_callback_info_t *info) { | |
| 1465 | return bare_fs__fstat(env, info, bare_fs_async); | |
| 1466 | } | |
| 1467 | ||
| 1468 | static js_value_t * | |
| 1469 | bare_fs_fstat_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1470 | return bare_fs__fstat(env, info, bare_fs_sync); | |
| 1471 | } | |
| 1472 | ||
| 1473 | static void | |
| 1474 | bare_fs__on_unlink(uv_fs_t *handle) { | |
| 1475 | bare_fs__on_request_result(handle); | |
| 1476 | } | |
| 1477 | ||
| 1478 | static inline js_value_t * | |
| 1479 | bare_fs__unlink(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1480 | int err; | |
| 1481 | ||
| 1482 | size_t argc = 2; | |
| 1483 | js_value_t *argv[2]; | |
| 1484 | ||
| 1485 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1486 | assert(err == 0); | |
| 1487 | ||
| 1488 | assert(argc == 2); | |
| 1489 | ||
| 1490 | bare_fs_req_t *req; | |
| 1491 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1492 | assert(err == 0); | |
| 1493 | ||
| 1494 | bare_fs_path_t path; | |
| 1495 | err = js_get_value_string_utf8(env, argv[1], path, sizeof(bare_fs_path_t), NULL); | |
| 1496 | assert(err == 0); | |
| 1497 | ||
| 1498 | uv_loop_t *loop; | |
| 1499 | err = js_get_env_loop(env, &loop); | |
| 1500 | assert(err == 0); | |
| 1501 | ||
| 1502 | err = uv_fs_unlink(loop, &req->handle, (char *) path, async ? bare_fs__on_unlink : NULL); | |
| 1503 | (void) err; | |
| 1504 | ||
| 1505 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1506 | (void) err; | |
| 1507 | ||
| 1508 | return NULL; | |
| 1509 | } | |
| 1510 | ||
| 1511 | static js_value_t * | |
| 1512 | bare_fs_unlink(js_env_t *env, js_callback_info_t *info) { | |
| 1513 | return bare_fs__unlink(env, info, bare_fs_async); | |
| 1514 | } | |
| 1515 | ||
| 1516 | static js_value_t * | |
| 1517 | bare_fs_unlink_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1518 | return bare_fs__unlink(env, info, bare_fs_sync); | |
| 1519 | } | |
| 1520 | ||
| 1521 | static void | |
| 1522 | bare_fs__on_realpath(uv_fs_t *handle) { | |
| 1523 | bare_fs__on_request_result(handle); | |
| 1524 | } | |
| 1525 | ||
| 1526 | static inline js_value_t * | |
| 1527 | bare_fs__realpath(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1528 | int err; | |
| 1529 | ||
| 1530 | size_t argc = 2; | |
| 1531 | js_value_t *argv[2]; | |
| 1532 | ||
| 1533 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1534 | assert(err == 0); | |
| 1535 | ||
| 1536 | assert(argc == 2); | |
| 1537 | ||
| 1538 | bare_fs_req_t *req; | |
| 1539 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1540 | assert(err == 0); | |
| 1541 | ||
| 1542 | bare_fs_path_t path; | |
| 1543 | err = js_get_value_string_utf8(env, argv[1], path, sizeof(bare_fs_path_t), NULL); | |
| 1544 | assert(err == 0); | |
| 1545 | ||
| 1546 | uv_loop_t *loop; | |
| 1547 | err = js_get_env_loop(env, &loop); | |
| 1548 | assert(err == 0); | |
| 1549 | ||
| 1550 | err = uv_fs_realpath(loop, &req->handle, (char *) path, async ? bare_fs__on_realpath : NULL); | |
| 1551 | (void) err; | |
| 1552 | ||
| 1553 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1554 | (void) err; | |
| 1555 | ||
| 1556 | return NULL; | |
| 1557 | } | |
| 1558 | ||
| 1559 | static js_value_t * | |
| 1560 | bare_fs_realpath(js_env_t *env, js_callback_info_t *info) { | |
| 1561 | return bare_fs__realpath(env, info, bare_fs_async); | |
| 1562 | } | |
| 1563 | ||
| 1564 | static js_value_t * | |
| 1565 | bare_fs_realpath_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1566 | return bare_fs__realpath(env, info, bare_fs_sync); | |
| 1567 | } | |
| 1568 | ||
| 1569 | static void | |
| 1570 | bare_fs__on_readlink(uv_fs_t *handle) { | |
| 1571 | bare_fs__on_request_result(handle); | |
| 1572 | } | |
| 1573 | ||
| 1574 | static inline js_value_t * | |
| 1575 | bare_fs__readlink(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1576 | int err; | |
| 1577 | ||
| 1578 | size_t argc = 2; | |
| 1579 | js_value_t *argv[2]; | |
| 1580 | ||
| 1581 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1582 | assert(err == 0); | |
| 1583 | ||
| 1584 | assert(argc == 2); | |
| 1585 | ||
| 1586 | bare_fs_req_t *req; | |
| 1587 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1588 | assert(err == 0); | |
| 1589 | ||
| 1590 | bare_fs_path_t path; | |
| 1591 | err = js_get_value_string_utf8(env, argv[1], path, sizeof(bare_fs_path_t), NULL); | |
| 1592 | assert(err == 0); | |
| 1593 | ||
| 1594 | uv_loop_t *loop; | |
| 1595 | err = js_get_env_loop(env, &loop); | |
| 1596 | assert(err == 0); | |
| 1597 | ||
| 1598 | err = uv_fs_readlink(loop, &req->handle, (char *) path, async ? bare_fs__on_readlink : NULL); | |
| 1599 | (void) err; | |
| 1600 | ||
| 1601 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1602 | (void) err; | |
| 1603 | ||
| 1604 | return NULL; | |
| 1605 | } | |
| 1606 | ||
| 1607 | static js_value_t * | |
| 1608 | bare_fs_readlink(js_env_t *env, js_callback_info_t *info) { | |
| 1609 | return bare_fs__readlink(env, info, bare_fs_async); | |
| 1610 | } | |
| 1611 | ||
| 1612 | static js_value_t * | |
| 1613 | bare_fs_readlink_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1614 | return bare_fs__readlink(env, info, bare_fs_sync); | |
| 1615 | } | |
| 1616 | ||
| 1617 | static void | |
| 1618 | bare_fs__on_symlink(uv_fs_t *handle) { | |
| 1619 | bare_fs__on_request_result(handle); | |
| 1620 | } | |
| 1621 | ||
| 1622 | static inline js_value_t * | |
| 1623 | bare_fs__symlink(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1624 | int err; | |
| 1625 | ||
| 1626 | size_t argc = 4; | |
| 1627 | js_value_t *argv[4]; | |
| 1628 | ||
| 1629 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1630 | assert(err == 0); | |
| 1631 | ||
| 1632 | assert(argc == 4); | |
| 1633 | ||
| 1634 | bare_fs_req_t *req; | |
| 1635 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1636 | assert(err == 0); | |
| 1637 | ||
| 1638 | bare_fs_path_t target; | |
| 1639 | err = js_get_value_string_utf8(env, argv[1], target, sizeof(bare_fs_path_t), NULL); | |
| 1640 | assert(err == 0); | |
| 1641 | ||
| 1642 | bare_fs_path_t path; | |
| 1643 | err = js_get_value_string_utf8(env, argv[2], path, sizeof(bare_fs_path_t), NULL); | |
| 1644 | assert(err == 0); | |
| 1645 | ||
| 1646 | int32_t flags; | |
| 1647 | err = js_get_value_int32(env, argv[3], &flags); | |
| 1648 | assert(err == 0); | |
| 1649 | ||
| 1650 | uv_loop_t *loop; | |
| 1651 | err = js_get_env_loop(env, &loop); | |
| 1652 | assert(err == 0); | |
| 1653 | ||
| 1654 | err = uv_fs_symlink(loop, &req->handle, (char *) target, (char *) path, flags, async ? bare_fs__on_symlink : NULL); | |
| 1655 | (void) err; | |
| 1656 | ||
| 1657 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1658 | (void) err; | |
| 1659 | ||
| 1660 | return NULL; | |
| 1661 | } | |
| 1662 | ||
| 1663 | static js_value_t * | |
| 1664 | bare_fs_symlink(js_env_t *env, js_callback_info_t *info) { | |
| 1665 | return bare_fs__symlink(env, info, bare_fs_async); | |
| 1666 | } | |
| 1667 | ||
| 1668 | static js_value_t * | |
| 1669 | bare_fs_symlink_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1670 | return bare_fs__symlink(env, info, bare_fs_sync); | |
| 1671 | } | |
| 1672 | ||
| 1673 | static void | |
| 1674 | bare_fs__on_opendir(uv_fs_t *handle) { | |
| 1675 | int err; | |
| 1676 | ||
| 1677 | bare_fs_req_t *req = (bare_fs_req_t *) handle; | |
| 1678 | ||
| 1679 | int status = handle->result; | |
| 1680 | ||
| 1681 | if (req->exiting && status >= 0) { | |
| 1682 | uv_dir_t *dir = handle->ptr; | |
| 1683 | ||
| 1684 | uv_fs_req_cleanup(handle); | |
| 1685 | ||
| 1686 | err = uv_fs_closedir(handle->loop, handle, dir, NULL); | |
| 1687 | assert(err == 0); | |
| 1688 | } | |
| 1689 | ||
| 1690 | bare_fs__on_request_result(handle); | |
| 1691 | } | |
| 1692 | ||
| 1693 | static inline js_value_t * | |
| 1694 | bare_fs__opendir(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1695 | int err; | |
| 1696 | ||
| 1697 | size_t argc = 2; | |
| 1698 | js_value_t *argv[2]; | |
| 1699 | ||
| 1700 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1701 | assert(err == 0); | |
| 1702 | ||
| 1703 | assert(argc == 2); | |
| 1704 | ||
| 1705 | bare_fs_req_t *req; | |
| 1706 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1707 | assert(err == 0); | |
| 1708 | ||
| 1709 | bare_fs_path_t path; | |
| 1710 | err = js_get_value_string_utf8(env, argv[1], path, sizeof(bare_fs_path_t), NULL); | |
| 1711 | assert(err == 0); | |
| 1712 | ||
| 1713 | uv_loop_t *loop; | |
| 1714 | err = js_get_env_loop(env, &loop); | |
| 1715 | assert(err == 0); | |
| 1716 | ||
| 1717 | err = uv_fs_opendir(loop, &req->handle, (char *) path, async ? bare_fs__on_opendir : NULL); | |
| 1718 | (void) err; | |
| 1719 | ||
| 1720 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1721 | (void) err; | |
| 1722 | ||
| 1723 | return NULL; | |
| 1724 | } | |
| 1725 | ||
| 1726 | static js_value_t * | |
| 1727 | bare_fs_opendir(js_env_t *env, js_callback_info_t *info) { | |
| 1728 | return bare_fs__opendir(env, info, bare_fs_async); | |
| 1729 | } | |
| 1730 | ||
| 1731 | static js_value_t * | |
| 1732 | bare_fs_opendir_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1733 | return bare_fs__opendir(env, info, bare_fs_sync); | |
| 1734 | } | |
| 1735 | ||
| 1736 | static void | |
| 1737 | bare_fs__on_readdir(uv_fs_t *handle) { | |
| 1738 | bare_fs__on_request_result(handle); | |
| 1739 | } | |
| 1740 | ||
| 1741 | static inline js_value_t * | |
| 1742 | bare_fs__readdir(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1743 | int err; | |
| 1744 | ||
| 1745 | size_t argc = 3; | |
| 1746 | js_value_t *argv[3]; | |
| 1747 | ||
| 1748 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1749 | assert(err == 0); | |
| 1750 | ||
| 1751 | assert(argc == 3); | |
| 1752 | ||
| 1753 | bare_fs_req_t *req; | |
| 1754 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1755 | assert(err == 0); | |
| 1756 | ||
| 1757 | bare_fs_dir_t *dir; | |
| 1758 | err = js_get_arraybuffer_info(env, argv[1], (void **) &dir, NULL); | |
| 1759 | assert(err == 0); | |
| 1760 | ||
| 1761 | uint32_t capacity; | |
| 1762 | err = js_get_value_uint32(env, argv[2], &capacity); | |
| 1763 | assert(err == 0); | |
| 1764 | ||
| 1765 | js_value_t *result; | |
| 1766 | ||
| 1767 | bare_fs_dirent_t *dirents; | |
| 1768 | err = js_create_arraybuffer(env, capacity * sizeof(bare_fs_dirent_t), (void **) &dirents, &result); | |
| 1769 | assert(err == 0); | |
| 1770 | ||
| 1771 | uv_loop_t *loop; | |
| 1772 | err = js_get_env_loop(env, &loop); | |
| 1773 | assert(err == 0); | |
| 1774 | ||
| 1775 | dir->handle->dirents = dirents; | |
| 1776 | dir->handle->nentries = capacity; | |
| 1777 | ||
| 1778 | err = uv_fs_readdir(loop, &req->handle, dir->handle, async ? bare_fs__on_readdir : NULL); | |
| 1779 | (void) err; | |
| 1780 | ||
| 1781 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1782 | (void) err; | |
| 1783 | ||
| 1784 | return result; | |
| 1785 | } | |
| 1786 | ||
| 1787 | static js_value_t * | |
| 1788 | bare_fs_readdir(js_env_t *env, js_callback_info_t *info) { | |
| 1789 | return bare_fs__readdir(env, info, bare_fs_async); | |
| 1790 | } | |
| 1791 | ||
| 1792 | static js_value_t * | |
| 1793 | bare_fs_readdir_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1794 | return bare_fs__readdir(env, info, bare_fs_sync); | |
| 1795 | } | |
| 1796 | ||
| 1797 | static void | |
| 1798 | bare_fs__on_closedir(uv_fs_t *handle) { | |
| 1799 | bare_fs__on_request_result(handle); | |
| 1800 | } | |
| 1801 | ||
| 1802 | static inline js_value_t * | |
| 1803 | bare_fs__closedir(js_env_t *env, js_callback_info_t *info, bool async) { | |
| 1804 | int err; | |
| 1805 | ||
| 1806 | size_t argc = 2; | |
| 1807 | js_value_t *argv[2]; | |
| 1808 | ||
| 1809 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1810 | assert(err == 0); | |
| 1811 | ||
| 1812 | assert(argc == 2); | |
| 1813 | ||
| 1814 | bare_fs_req_t *req; | |
| 1815 | err = js_get_arraybuffer_info(env, argv[0], (void **) &req, NULL); | |
| 1816 | assert(err == 0); | |
| 1817 | ||
| 1818 | bare_fs_dir_t *dir; | |
| 1819 | err = js_get_arraybuffer_info(env, argv[1], (void **) &dir, NULL); | |
| 1820 | assert(err == 0); | |
| 1821 | ||
| 1822 | uv_loop_t *loop; | |
| 1823 | err = js_get_env_loop(env, &loop); | |
| 1824 | assert(err == 0); | |
| 1825 | ||
| 1826 | err = uv_fs_closedir(loop, &req->handle, dir->handle, async ? bare_fs__on_closedir : NULL); | |
| 1827 | (void) err; | |
| 1828 | ||
| 1829 | err = bare_fs__request_pending(env, req, async, NULL); | |
| 1830 | (void) err; | |
| 1831 | ||
| 1832 | return NULL; | |
| 1833 | } | |
| 1834 | ||
| 1835 | static js_value_t * | |
| 1836 | bare_fs_closedir(js_env_t *env, js_callback_info_t *info) { | |
| 1837 | return bare_fs__closedir(env, info, bare_fs_async); | |
| 1838 | } | |
| 1839 | ||
| 1840 | static js_value_t * | |
| 1841 | bare_fs_closedir_sync(js_env_t *env, js_callback_info_t *info) { | |
| 1842 | return bare_fs__closedir(env, info, bare_fs_sync); | |
| 1843 | } | |
| 1844 | ||
| 1845 | static void | |
| 1846 | bare_fs__on_watcher_event(uv_fs_event_t *handle, const char *filename, int events, int status) { | |
| 1847 | int err; | |
| 1848 | ||
| 1849 | bare_fs_watcher_t *watcher = (bare_fs_watcher_t *) handle; | |
| 1850 | ||
| 1851 | if (watcher->exiting) return; | |
| 1852 | ||
| 1853 | js_env_t *env = watcher->env; | |
| 1854 | ||
| 1855 | js_handle_scope_t *scope; | |
| 1856 | err = js_open_handle_scope(env, &scope); | |
| 1857 | assert(err == 0); | |
| 1858 | ||
| 1859 | js_value_t *ctx; | |
| 1860 | err = js_get_reference_value(env, watcher->ctx, &ctx); | |
| 1861 | assert(err == 0); | |
| 1862 | ||
| 1863 | js_value_t *on_event; | |
| 1864 | err = js_get_reference_value(env, watcher->on_event, &on_event); | |
| 1865 | assert(err == 0); | |
| 1866 | ||
| 1867 | js_value_t *args[3]; | |
| 1868 | ||
| 1869 | if (status < 0) { | |
| 1870 | js_value_t *code; | |
| 1871 | err = js_create_string_utf8(env, (utf8_t *) uv_err_name(status), -1, &code); | |
| 1872 | assert(err == 0); | |
| 1873 | ||
| 1874 | js_value_t *message; | |
| 1875 | err = js_create_string_utf8(env, (utf8_t *) uv_strerror(status), -1, &message); | |
| 1876 | assert(err == 0); | |
| 1877 | ||
| 1878 | err = js_create_error(env, code, message, &args[0]); | |
| 1879 | assert(err == 0); | |
| 1880 | ||
| 1881 | err = js_create_int32(env, 0, &args[1]); | |
| 1882 | assert(err == 0); | |
| 1883 | ||
| 1884 | err = js_get_null(env, &args[2]); | |
| 1885 | assert(err == 0); | |
| 1886 | } else { | |
| 1887 | err = js_get_null(env, &args[0]); | |
| 1888 | assert(err == 0); | |
| 1889 | ||
| 1890 | err = js_create_int32(env, events, &args[1]); | |
| 1891 | assert(err == 0); | |
| 1892 | ||
| 1893 | size_t len = strlen(filename); | |
| 1894 | ||
| 1895 | void *data; | |
| 1896 | err = js_create_arraybuffer(env, len, &data, &args[2]); | |
| 1897 | assert(err == 0); | |
| 1898 | ||
| 1899 | memcpy(data, (void *) filename, len); | |
| 1900 | } | |
| 1901 | ||
| 1902 | err = js_call_function(env, ctx, on_event, 3, args, NULL); | |
| 1903 | (void) err; | |
| 1904 | ||
| 1905 | err = js_close_handle_scope(env, scope); | |
| 1906 | assert(err == 0); | |
| 1907 | } | |
| 1908 | ||
| 1909 | static void | |
| 1910 | bare_fs__on_watcher_close(uv_handle_t *handle) { | |
| 1911 | int err; | |
| 1912 | ||
| 1913 | bare_fs_watcher_t *watcher = (bare_fs_watcher_t *) handle; | |
| 1914 | ||
| 1915 | js_env_t *env = watcher->env; | |
| 1916 | ||
| 1917 | js_deferred_teardown_t *teardown = watcher->teardown; | |
| 1918 | ||
| 1919 | if (watcher->exiting) { | |
| 1920 | err = js_delete_reference(env, watcher->on_event); | |
| 1921 | assert(err == 0); | |
| 1922 | ||
| 1923 | err = js_delete_reference(env, watcher->on_close); | |
| 1924 | assert(err == 0); | |
| 1925 | ||
| 1926 | err = js_delete_reference(env, watcher->ctx); | |
| 1927 | assert(err == 0); | |
| 1928 | } else { | |
| 1929 | js_handle_scope_t *scope; | |
| 1930 | err = js_open_handle_scope(env, &scope); | |
| 1931 | assert(err == 0); | |
| 1932 | ||
| 1933 | js_value_t *ctx; | |
| 1934 | err = js_get_reference_value(env, watcher->ctx, &ctx); | |
| 1935 | assert(err == 0); | |
| 1936 | ||
| 1937 | js_value_t *on_close; | |
| 1938 | err = js_get_reference_value(env, watcher->on_close, &on_close); | |
| 1939 | assert(err == 0); | |
| 1940 | ||
| 1941 | err = js_delete_reference(env, watcher->on_event); | |
| 1942 | assert(err == 0); | |
| 1943 | ||
| 1944 | err = js_delete_reference(env, watcher->on_close); | |
| 1945 | assert(err == 0); | |
| 1946 | ||
| 1947 | err = js_delete_reference(env, watcher->ctx); | |
| 1948 | assert(err == 0); | |
| 1949 | ||
| 1950 | err = js_call_function(env, ctx, on_close, 0, NULL, NULL); | |
| 1951 | (void) err; | |
| 1952 | ||
| 1953 | err = js_close_handle_scope(env, scope); | |
| 1954 | assert(err == 0); | |
| 1955 | } | |
| 1956 | ||
| 1957 | err = js_finish_deferred_teardown_callback(teardown); | |
| 1958 | assert(err == 0); | |
| 1959 | } | |
| 1960 | ||
| 1961 | static void | |
| 1962 | bare_fs__on_watcher_teardown(js_deferred_teardown_t *handle, void *data) { | |
| 1963 | bare_fs_watcher_t *watcher = (bare_fs_watcher_t *) data; | |
| 1964 | ||
| 1965 | watcher->exiting = true; | |
| 1966 | ||
| 1967 | if (watcher->closing) return; | |
| 1968 | ||
| 1969 | uv_close((uv_handle_t *) &watcher->handle, bare_fs__on_watcher_close); | |
| 1970 | } | |
| 1971 | ||
| 1972 | static js_value_t * | |
| 1973 | bare_fs_watcher_init(js_env_t *env, js_callback_info_t *info) { | |
| 1974 | int err; | |
| 1975 | ||
| 1976 | size_t argc = 5; | |
| 1977 | js_value_t *argv[5]; | |
| 1978 | ||
| 1979 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1980 | assert(err == 0); | |
| 1981 | ||
| 1982 | assert(argc == 5); | |
| 1983 | ||
| 1984 | bare_fs_path_t path; | |
| 1985 | err = js_get_value_string_utf8(env, argv[0], path, sizeof(bare_fs_path_t), NULL); | |
| 1986 | assert(err == 0); | |
| 1987 | ||
| 1988 | bool recursive; | |
| 1989 | err = js_get_value_bool(env, argv[1], &recursive); | |
| 1990 | assert(err == 0); | |
| 1991 | ||
| 1992 | js_value_t *result; | |
| 1993 | ||
| 1994 | bare_fs_watcher_t *watcher; | |
| 1995 | err = js_create_arraybuffer(env, sizeof(bare_fs_watcher_t), (void **) &watcher, &result); | |
| 1996 | assert(err == 0); | |
| 1997 | ||
| 1998 | uv_loop_t *loop; | |
| 1999 | err = js_get_env_loop(env, &loop); | |
| 2000 | assert(err == 0); | |
| 2001 | ||
| 2002 | err = uv_fs_event_init(loop, &watcher->handle); | |
| 2003 | ||
| 2004 | if (err < 0) { | |
| 2005 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 2006 | assert(err == 0); | |
| 2007 | ||
| 2008 | return NULL; | |
| 2009 | } | |
| 2010 | ||
| 2011 | err = uv_fs_event_start(&watcher->handle, bare_fs__on_watcher_event, (char *) path, recursive ? UV_FS_EVENT_RECURSIVE : 0); | |
| 2012 | assert(err == 0); | |
| 2013 | ||
| 2014 | watcher->env = env; | |
| 2015 | watcher->closing = false; | |
| 2016 | watcher->exiting = false; | |
| 2017 | ||
| 2018 | err = js_create_reference(env, argv[2], 1, &watcher->ctx); | |
| 2019 | assert(err == 0); | |
| 2020 | ||
| 2021 | err = js_create_reference(env, argv[3], 1, &watcher->on_event); | |
| 2022 | assert(err == 0); | |
| 2023 | ||
| 2024 | err = js_create_reference(env, argv[4], 1, &watcher->on_close); | |
| 2025 | assert(err == 0); | |
| 2026 | ||
| 2027 | err = js_add_deferred_teardown_callback(env, bare_fs__on_watcher_teardown, (void *) watcher, &watcher->teardown); | |
| 2028 | assert(err == 0); | |
| 2029 | ||
| 2030 | return result; | |
| 2031 | } | |
| 2032 | ||
| 2033 | static js_value_t * | |
| 2034 | bare_fs_watcher_close(js_env_t *env, js_callback_info_t *info) { | |
| 2035 | int err; | |
| 2036 | ||
| 2037 | size_t argc = 1; | |
| 2038 | js_value_t *argv[1]; | |
| 2039 | ||
| 2040 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 2041 | assert(err == 0); | |
| 2042 | ||
| 2043 | assert(argc == 1); | |
| 2044 | ||
| 2045 | bare_fs_watcher_t *watcher; | |
| 2046 | err = js_get_arraybuffer_info(env, argv[0], (void **) &watcher, NULL); | |
| 2047 | assert(err == 0); | |
| 2048 | ||
| 2049 | err = uv_fs_event_stop(&watcher->handle); | |
| 2050 | assert(err == 0); | |
| 2051 | ||
| 2052 | watcher->closing = true; | |
| 2053 | ||
| 2054 | uv_close((uv_handle_t *) &watcher->handle, bare_fs__on_watcher_close); | |
| 2055 | ||
| 2056 | return NULL; | |
| 2057 | } | |
| 2058 | ||
| 2059 | static js_value_t * | |
| 2060 | bare_fs_watcher_ref(js_env_t *env, js_callback_info_t *info) { | |
| 2061 | int err; | |
| 2062 | ||
| 2063 | size_t argc = 1; | |
| 2064 | js_value_t *argv[1]; | |
| 2065 | ||
| 2066 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 2067 | assert(err == 0); | |
| 2068 | ||
| 2069 | assert(argc == 1); | |
| 2070 | ||
| 2071 | bare_fs_watcher_t *watcher; | |
| 2072 | err = js_get_arraybuffer_info(env, argv[0], (void **) &watcher, NULL); | |
| 2073 | assert(err == 0); | |
| 2074 | ||
| 2075 | uv_ref((uv_handle_t *) &watcher->handle); | |
| 2076 | ||
| 2077 | return NULL; | |
| 2078 | } | |
| 2079 | ||
| 2080 | static js_value_t * | |
| 2081 | bare_fs_watcher_unref(js_env_t *env, js_callback_info_t *info) { | |
| 2082 | int err; | |
| 2083 | ||
| 2084 | size_t argc = 1; | |
| 2085 | js_value_t *argv[1]; | |
| 2086 | ||
| 2087 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 2088 | assert(err == 0); | |
| 2089 | ||
| 2090 | assert(argc == 1); | |
| 2091 | ||
| 2092 | bare_fs_watcher_t *watcher; | |
| 2093 | err = js_get_arraybuffer_info(env, argv[0], (void **) &watcher, NULL); | |
| 2094 | assert(err == 0); | |
| 2095 | ||
| 2096 | uv_unref((uv_handle_t *) &watcher->handle); | |
| 2097 | ||
| 2098 | return NULL; | |
| 2099 | } | |
| 2100 | ||
| 2101 | static js_value_t * | |
| 2102 | bare_fs_exports(js_env_t *env, js_value_t *exports) { | |
| 2103 | int err; | |
| 2104 | ||
| 2105 | #define V(name, fn) \ | |
| 2106 | { \ | |
| 2107 | js_value_t *val; \ | |
| 2108 | err = js_create_function(env, name, -1, fn, NULL, &val); \ | |
| 2109 | assert(err == 0); \ | |
| 2110 | err = js_set_named_property(env, exports, name, val); \ | |
| 2111 | assert(err == 0); \ | |
| 2112 | } | |
| 2113 | ||
| 2114 | V("requestInit", bare_fs_request_init) | |
| 2115 | V("requestDestroy", bare_fs_request_destroy) | |
| 2116 | V("requestReset", bare_fs_request_reset) | |
| 2117 | V("requestResultStat", bare_fs_request_result_stat) | |
| 2118 | V("requestResultString", bare_fs_request_result_string) | |
| 2119 | V("requestResultDir", bare_fs_request_result_dir) | |
| 2120 | V("requestResultDirents", bare_fs_request_result_dirents) | |
| 2121 | ||
| 2122 | V("open", bare_fs_open) | |
| 2123 | V("openSync", bare_fs_open_sync) | |
| 2124 | V("close", bare_fs_close) | |
| 2125 | V("closeSync", bare_fs_close_sync) | |
| 2126 | V("access", bare_fs_access) | |
| 2127 | V("accessSync", bare_fs_access_sync) | |
| 2128 | V("read", bare_fs_read) | |
| 2129 | V("readSync", bare_fs_read_sync) | |
| 2130 | V("readv", bare_fs_readv) | |
| 2131 | V("readvSync", bare_fs_readv_sync) | |
| 2132 | V("write", bare_fs_write) | |
| 2133 | V("writeSync", bare_fs_write_sync) | |
| 2134 | V("writev", bare_fs_writev) | |
| 2135 | V("writevSync", bare_fs_writev_sync) | |
| 2136 | V("ftruncate", bare_fs_ftruncate) | |
| 2137 | V("ftruncateSync", bare_fs_ftruncate_sync) | |
| 2138 | V("chmod", bare_fs_chmod) | |
| 2139 | V("chmodSync", bare_fs_chmod_sync) | |
| 2140 | V("fchmod", bare_fs_fchmod) | |
| 2141 | V("fchmodSync", bare_fs_fchmod_sync) | |
| 2142 | V("utimes", bare_fs_utimes) | |
| 2143 | V("utimesSync", bare_fs_utimes_sync) | |
| 2144 | V("rename", bare_fs_rename) | |
| 2145 | V("renameSync", bare_fs_rename_sync) | |
| 2146 | V("copyfile", bare_fs_copyfile) | |
| 2147 | V("copyfileSync", bare_fs_copyfile_sync) | |
| 2148 | V("mkdir", bare_fs_mkdir) | |
| 2149 | V("mkdirSync", bare_fs_mkdir_sync) | |
| 2150 | V("rmdir", bare_fs_rmdir) | |
| 2151 | V("rmdirSync", bare_fs_rmdir_sync) | |
| 2152 | V("stat", bare_fs_stat) | |
| 2153 | V("statSync", bare_fs_stat_sync) | |
| 2154 | V("lstat", bare_fs_lstat) | |
| 2155 | V("lstatSync", bare_fs_lstat_sync) | |
| 2156 | V("fstat", bare_fs_fstat) | |
| 2157 | V("fstatSync", bare_fs_fstat_sync) | |
| 2158 | V("unlink", bare_fs_unlink) | |
| 2159 | V("unlinkSync", bare_fs_unlink_sync) | |
| 2160 | V("realpath", bare_fs_realpath) | |
| 2161 | V("realpathSync", bare_fs_realpath_sync) | |
| 2162 | V("readlink", bare_fs_readlink) | |
| 2163 | V("readlinkSync", bare_fs_readlink_sync) | |
| 2164 | V("symlink", bare_fs_symlink) | |
| 2165 | V("symlinkSync", bare_fs_symlink_sync) | |
| 2166 | V("opendir", bare_fs_opendir) | |
| 2167 | V("opendirSync", bare_fs_opendir_sync) | |
| 2168 | V("readdir", bare_fs_readdir) | |
| 2169 | V("readdirSync", bare_fs_readdir_sync) | |
| 2170 | V("closedir", bare_fs_closedir) | |
| 2171 | V("closedirSync", bare_fs_closedir_sync) | |
| 2172 | ||
| 2173 | V("watcherInit", bare_fs_watcher_init) | |
| 2174 | V("watcherClose", bare_fs_watcher_close) | |
| 2175 | V("watcherRef", bare_fs_watcher_ref) | |
| 2176 | V("watcherUnref", bare_fs_watcher_unref) | |
| 2177 | #undef V | |
| 2178 | ||
| 2179 | #define V(name) \ | |
| 2180 | { \ | |
| 2181 | js_value_t *val; \ | |
| 2182 | err = js_create_uint32(env, name, &val); \ | |
| 2183 | assert(err == 0); \ | |
| 2184 | err = js_set_named_property(env, exports, #name, val); \ | |
| 2185 | assert(err == 0); \ | |
| 2186 | } | |
| 2187 | ||
| 2188 | V(O_RDWR) | |
| 2189 | V(O_RDONLY) | |
| 2190 | V(O_WRONLY) | |
| 2191 | V(O_CREAT) | |
| 2192 | V(O_TRUNC) | |
| 2193 | V(O_APPEND) | |
| 2194 | ||
| 2195 | #ifdef F_OK | |
| 2196 | V(F_OK) | |
| 2197 | #endif | |
| 2198 | #ifdef R_OK | |
| 2199 | V(R_OK) | |
| 2200 | #endif | |
| 2201 | #ifdef W_OK | |
| 2202 | V(W_OK) | |
| 2203 | #endif | |
| 2204 | #ifdef X_OK | |
| 2205 | V(X_OK) | |
| 2206 | #endif | |
| 2207 | ||
| 2208 | V(S_IFMT) | |
| 2209 | V(S_IFREG) | |
| 2210 | V(S_IFDIR) | |
| 2211 | V(S_IFCHR) | |
| 2212 | V(S_IFLNK) | |
| 2213 | #ifdef S_IFBLK | |
| 2214 | V(S_IFBLK) | |
| 2215 | #endif | |
| 2216 | #ifdef S_IFIFO | |
| 2217 | V(S_IFIFO) | |
| 2218 | #endif | |
| 2219 | #ifdef S_IFSOCK | |
| 2220 | V(S_IFSOCK) | |
| 2221 | #endif | |
| 2222 | ||
| 2223 | #ifdef S_IRUSR | |
| 2224 | V(S_IRUSR) | |
| 2225 | #endif | |
| 2226 | #ifdef S_IWUSR | |
| 2227 | V(S_IWUSR) | |
| 2228 | #endif | |
| 2229 | #ifdef S_IXUSR | |
| 2230 | V(S_IXUSR) | |
| 2231 | #endif | |
| 2232 | #ifdef S_IRGRP | |
| 2233 | V(S_IRGRP) | |
| 2234 | #endif | |
| 2235 | #ifdef S_IWGRP | |
| 2236 | V(S_IWGRP) | |
| 2237 | #endif | |
| 2238 | #ifdef S_IXGRP | |
| 2239 | V(S_IXGRP) | |
| 2240 | #endif | |
| 2241 | #ifdef S_IROTH | |
| 2242 | V(S_IROTH) | |
| 2243 | #endif | |
| 2244 | #ifdef S_IWOTH | |
| 2245 | V(S_IWOTH) | |
| 2246 | #endif | |
| 2247 | #ifdef S_IXOTH | |
| 2248 | V(S_IXOTH) | |
| 2249 | #endif | |
| 2250 | ||
| 2251 | V(UV_DIRENT_UNKNOWN) | |
| 2252 | V(UV_DIRENT_FILE) | |
| 2253 | V(UV_DIRENT_DIR) | |
| 2254 | V(UV_DIRENT_LINK) | |
| 2255 | V(UV_DIRENT_FIFO) | |
| 2256 | V(UV_DIRENT_SOCKET) | |
| 2257 | V(UV_DIRENT_CHAR) | |
| 2258 | V(UV_DIRENT_BLOCK) | |
| 2259 | ||
| 2260 | V(UV_FS_COPYFILE_EXCL) | |
| 2261 | V(UV_FS_COPYFILE_FICLONE) | |
| 2262 | V(UV_FS_COPYFILE_FICLONE_FORCE) | |
| 2263 | V(UV_FS_SYMLINK_DIR) | |
| 2264 | V(UV_FS_SYMLINK_JUNCTION) | |
| 2265 | ||
| 2266 | V(UV_RENAME) | |
| 2267 | V(UV_CHANGE) | |
| 2268 | #undef V | |
| 2269 | ||
| 2270 | return exports; | |
| 2271 | } | |
| 2272 | ||
| 2273 | BARE_MODULE(bare_fs, bare_fs_exports) |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require.addon() |
| … | … | @@ -0,0 +1,1115 @@ |
| 1 | import EventEmitter, { EventMap } from 'bare-events' | |
| 2 | import Buffer, { BufferEncoding } from 'bare-buffer' | |
| 3 | import URL from 'bare-url' | |
| 4 | import { Readable, Writable } from 'bare-stream' | |
| 5 | import promises from './promises' | |
| 6 | import constants from './lib/constants' | |
| 7 | ||
| 8 | export { promises, constants } | |
| 9 | ||
| 10 | type Path = string | Buffer | URL | |
| 11 | ||
| 12 | type Flag = | |
| 13 | | 'a' | |
| 14 | | 'a+' | |
| 15 | | 'as' | |
| 16 | | 'as+' | |
| 17 | | 'ax' | |
| 18 | | 'ax+' | |
| 19 | | 'r' | |
| 20 | | 'r+' | |
| 21 | | 'rs' | |
| 22 | | 'rs+' | |
| 23 | | 'sa' | |
| 24 | | 'sa+' | |
| 25 | | 'sr' | |
| 26 | | 'sr+' | |
| 27 | | 'w' | |
| 28 | | 'w+' | |
| 29 | | 'wx' | |
| 30 | | 'wx+' | |
| 31 | | 'xa' | |
| 32 | | 'xa+' | |
| 33 | | 'xw' | |
| 34 | | 'xw+' | |
| 35 | ||
| 36 | interface Callback<A extends unknown[] = []> { | |
| 37 | (err: Error | null, ...args: A): void | |
| 38 | } | |
| 39 | ||
| 40 | export interface Dir<T extends string | Buffer = string | Buffer> | |
| 41 | extends Iterable<Dirent>, AsyncIterable<Dirent> { | |
| 42 | readonly path: string | |
| 43 | ||
| 44 | read(): Promise<Dirent<T> | null> | |
| 45 | read(cb: Callback<[dirent: Dirent<T> | null]>): void | |
| 46 | readSync(): Dirent<T> | null | |
| 47 | ||
| 48 | close(): Promise<void> | |
| 49 | close(cb: Callback): void | |
| 50 | closeSync(): void | |
| 51 | } | |
| 52 | ||
| 53 | export class Dir { | |
| 54 | private constructor(path: string, handle: ArrayBuffer, opts?: OpendirOptions) | |
| 55 | } | |
| 56 | ||
| 57 | export interface Dirent<T extends string | Buffer = string | Buffer> { | |
| 58 | readonly path: string | |
| 59 | readonly name: T | |
| 60 | readonly type: number | |
| 61 | ||
| 62 | isFile(): boolean | |
| 63 | isDirectory(): boolean | |
| 64 | isSymbolicLink(): boolean | |
| 65 | isFIFO(): boolean | |
| 66 | isSocket(): boolean | |
| 67 | isCharacterDevice(): boolean | |
| 68 | isBlockDevice(): boolean | |
| 69 | } | |
| 70 | ||
| 71 | export class Dirent<T extends string | Buffer = string | Buffer> { | |
| 72 | private constructor(path: string, name: T, type: number) | |
| 73 | } | |
| 74 | ||
| 75 | export interface Stats { | |
| 76 | readonly dev: number | |
| 77 | readonly mode: number | |
| 78 | readonly nlink: number | |
| 79 | readonly uid: number | |
| 80 | readonly gid: number | |
| 81 | readonly rdev: number | |
| 82 | readonly blksize: number | |
| 83 | readonly ino: number | |
| 84 | readonly size: number | |
| 85 | readonly blocks: number | |
| 86 | readonly atimeMs: Date | |
| 87 | readonly mtimeMs: Date | |
| 88 | readonly ctimeMs: Date | |
| 89 | readonly birthtimeMs: Date | |
| 90 | ||
| 91 | isDirectory(): boolean | |
| 92 | isFile(): boolean | |
| 93 | isBlockDevice(): boolean | |
| 94 | isCharacterDevice(): boolean | |
| 95 | isFIFO(): boolean | |
| 96 | isSymbolicLink(): boolean | |
| 97 | isSocket(): boolean | |
| 98 | } | |
| 99 | ||
| 100 | export class Stats { | |
| 101 | private constructor( | |
| 102 | dev: number, | |
| 103 | mode: number, | |
| 104 | nlink: number, | |
| 105 | uid: number, | |
| 106 | gid: number, | |
| 107 | rdev: number, | |
| 108 | blksize: number, | |
| 109 | ino: number, | |
| 110 | size: number, | |
| 111 | blocks: number, | |
| 112 | atimeMs: number, | |
| 113 | mtimeMs: number, | |
| 114 | ctimeMs: number, | |
| 115 | birthtimeMs: number | |
| 116 | ) | |
| 117 | } | |
| 118 | ||
| 119 | export interface ReadStreamOptions { | |
| 120 | fd?: number | |
| 121 | flags?: Flag | |
| 122 | mode?: number | |
| 123 | start?: number | |
| 124 | end?: number | |
| 125 | } | |
| 126 | ||
| 127 | export interface ReadStream extends Readable { | |
| 128 | readonly path: string | null | |
| 129 | readonly fd: number | |
| 130 | readonly flags: Flag | |
| 131 | readonly mode: number | |
| 132 | } | |
| 133 | ||
| 134 | export class ReadStream { | |
| 135 | private constructor(path: Path | null, opts?: WriteStreamOptions) | |
| 136 | } | |
| 137 | ||
| 138 | export function createReadStream(path: Path | null, opts?: ReadStreamOptions): ReadStream | |
| 139 | ||
| 140 | export interface WriteStreamOptions { | |
| 141 | fd?: number | |
| 142 | flags?: Flag | |
| 143 | mode?: number | |
| 144 | } | |
| 145 | ||
| 146 | export interface WriteStream extends Writable { | |
| 147 | readonly path: string | null | |
| 148 | readonly fd: number | |
| 149 | readonly flags: Flag | |
| 150 | readonly mode: number | |
| 151 | } | |
| 152 | ||
| 153 | export class WriteStream { | |
| 154 | private constructor(path: Path | null, opts?: WriteStreamOptions) | |
| 155 | } | |
| 156 | ||
| 157 | export function createWriteStream(path: Path | null, opts?: WriteStreamOptions): WriteStream | |
| 158 | ||
| 159 | export interface WatcherOptions { | |
| 160 | persistent?: boolean | |
| 161 | recursive?: boolean | |
| 162 | encoding?: BufferEncoding | 'buffer' | |
| 163 | } | |
| 164 | ||
| 165 | export type WatcherEventType = 'rename' | 'change' | |
| 166 | ||
| 167 | export interface WatcherEvents<T extends string | Buffer = string | Buffer> extends EventMap { | |
| 168 | error: [err: Error] | |
| 169 | change: [eventType: WatcherEventType, filename: T] | |
| 170 | close: [] | |
| 171 | } | |
| 172 | ||
| 173 | export interface Watcher<T extends string | Buffer = string | Buffer> | |
| 174 | extends | |
| 175 | EventEmitter<WatcherEvents<T>>, | |
| 176 | AsyncIterable<{ eventType: WatcherEventType; filename: T }> { | |
| 177 | close(): void | |
| 178 | ref(): void | |
| 179 | unref(): void | |
| 180 | } | |
| 181 | ||
| 182 | export class Watcher { | |
| 183 | private constructor(path: Path, opts: WatcherOptions) | |
| 184 | } | |
| 185 | ||
| 186 | export function access(filepath: Path, mode?: number): Promise<void> | |
| 187 | ||
| 188 | export function access(filepath: Path, mode: number, cb: Callback): void | |
| 189 | ||
| 190 | export function access(filepath: Path, cb: Callback): void | |
| 191 | ||
| 192 | export function accessSync(filepath: Path, mode?: number): void | |
| 193 | ||
| 194 | export interface AppendFileOptions { | |
| 195 | encoding?: BufferEncoding | |
| 196 | flag?: string | |
| 197 | mode?: number | |
| 198 | } | |
| 199 | ||
| 200 | export function appendFile( | |
| 201 | filepath: Path, | |
| 202 | data: string | Buffer | ArrayBufferView, | |
| 203 | opts?: AppendFileOptions | |
| 204 | ): Promise<void> | |
| 205 | ||
| 206 | export function appendFile( | |
| 207 | filepath: Path, | |
| 208 | data: string | Buffer | ArrayBufferView, | |
| 209 | encoding: BufferEncoding | |
| 210 | ): Promise<void> | |
| 211 | ||
| 212 | export function appendFile( | |
| 213 | filepath: Path, | |
| 214 | data: string | Buffer | ArrayBufferView, | |
| 215 | opts: AppendFileOptions, | |
| 216 | cb: Callback | |
| 217 | ): void | |
| 218 | ||
| 219 | export function appendFile( | |
| 220 | filepath: Path, | |
| 221 | data: string | Buffer | ArrayBufferView, | |
| 222 | encoding: BufferEncoding, | |
| 223 | cb: Callback | |
| 224 | ): void | |
| 225 | ||
| 226 | export function appendFile( | |
| 227 | filepath: Path, | |
| 228 | data: string | Buffer | ArrayBufferView, | |
| 229 | cb: Callback | |
| 230 | ): void | |
| 231 | ||
| 232 | export function appendFileSync( | |
| 233 | filepath: Path, | |
| 234 | data: string | Buffer | ArrayBufferView, | |
| 235 | opts?: AppendFileOptions | |
| 236 | ): void | |
| 237 | ||
| 238 | export function appendFileSync( | |
| 239 | filepath: Path, | |
| 240 | data: string | Buffer | ArrayBufferView, | |
| 241 | encoding: BufferEncoding | |
| 242 | ): void | |
| 243 | ||
| 244 | export function chmod(filepath: Path, mode: string | number): Promise<void> | |
| 245 | ||
| 246 | export function chmod(filepath: Path, mode: string | number, cb: Callback): void | |
| 247 | ||
| 248 | export function chmodSync(filepath: Path, mode: string | number): void | |
| 249 | ||
| 250 | export function close(fd: number): Promise<void> | |
| 251 | ||
| 252 | export function close(fd: number, cb: Callback): void | |
| 253 | ||
| 254 | export function closeSync(fd: number): void | |
| 255 | ||
| 256 | export function copyFile(src: Path, dst: Path, mode?: number): Promise<void> | |
| 257 | ||
| 258 | export function copyFile(src: Path, dst: Path, mode: number, cb: Callback): void | |
| 259 | ||
| 260 | export function copyFile(src: Path, dst: Path, cb: Callback): void | |
| 261 | ||
| 262 | export function copyFileSync(src: Path, dst: Path, mode?: number): void | |
| 263 | ||
| 264 | export interface CpOptions { | |
| 265 | recursive?: boolean | |
| 266 | } | |
| 267 | ||
| 268 | export function cp(src: Path, dst: Path, opts?: CpOptions): Promise<void> | |
| 269 | ||
| 270 | export function cp(src: Path, dst: Path, opts: CpOptions, cb: Callback): void | |
| 271 | ||
| 272 | export function cp(src: Path, dst: Path, cb: Callback): void | |
| 273 | ||
| 274 | export function exists(filepath: Path): Promise<boolean> | |
| 275 | ||
| 276 | export function exists(filepath: Path, cb: (exists: boolean) => void): void | |
| 277 | ||
| 278 | export function existsSync(filepath: Path): boolean | |
| 279 | ||
| 280 | export function fchmod(fd: number, mode: string | number): Promise<void> | |
| 281 | ||
| 282 | export function fchmod(fd: number, mode: string | number, cb: Callback): void | |
| 283 | ||
| 284 | export function fchmodSync(fd: number, mode: string | number): void | |
| 285 | ||
| 286 | export function fstat(fd: number): Promise<Stats> | |
| 287 | ||
| 288 | export function fstat(fd: number, cb: Callback<[stats: Stats | null]>): void | |
| 289 | ||
| 290 | export function fstatSync(fd: number): Stats | |
| 291 | ||
| 292 | export function ftruncate(fd: number, len?: number): Promise<void> | |
| 293 | ||
| 294 | export function ftruncate(fd: number, len: number, cb: Callback): void | |
| 295 | ||
| 296 | export function ftruncate(fd: number, cb: Callback): void | |
| 297 | ||
| 298 | export function ftruncateSync(fd: number, len?: number): void | |
| 299 | ||
| 300 | export function lstat(filepath: Path): Promise<Stats> | |
| 301 | ||
| 302 | export function lstat(filepath: Path, cb: Callback<[stats: Stats | null]>): void | |
| 303 | ||
| 304 | export function lstatSync(filepath: Path): Stats | |
| 305 | ||
| 306 | export interface MkdirOptions { | |
| 307 | mode?: number | |
| 308 | recursive?: boolean | |
| 309 | } | |
| 310 | ||
| 311 | export function mkdir(filepath: Path, opts?: MkdirOptions): Promise<void> | |
| 312 | ||
| 313 | export function mkdir(filepath: Path, mode: number): Promise<void> | |
| 314 | ||
| 315 | export function mkdir(filepath: Path, opts: MkdirOptions, cb: Callback): void | |
| 316 | ||
| 317 | export function mkdir(filepath: Path, mode: number, cb: Callback): void | |
| 318 | ||
| 319 | export function mkdir(filepath: Path, cb: Callback): void | |
| 320 | ||
| 321 | export function mkdirSync(filepath: Path, opts?: MkdirOptions): void | |
| 322 | ||
| 323 | export function mkdirSync(filepath: Path, mode: number): void | |
| 324 | ||
| 325 | export function open(filepath: Path, flags?: Flag | number, mode?: string | number): Promise<number> | |
| 326 | ||
| 327 | export function open( | |
| 328 | filepath: Path, | |
| 329 | flags: Flag | number, | |
| 330 | mode: string | number, | |
| 331 | cb: Callback<[fd: number]> | |
| 332 | ): void | |
| 333 | ||
| 334 | export function open(filepath: Path, flags: Flag | number, cb: Callback<[fd: number]>): void | |
| 335 | ||
| 336 | export function open(filepath: Path, cb: Callback<[fd: number]>): void | |
| 337 | ||
| 338 | export function openSync(filepath: Path, flags?: Flag | number, mode?: string | number): number | |
| 339 | ||
| 340 | export interface OpendirOptions { | |
| 341 | encoding?: BufferEncoding | 'buffer' | |
| 342 | bufferSize?: number | |
| 343 | } | |
| 344 | ||
| 345 | export function opendir( | |
| 346 | filepath: Path, | |
| 347 | opts: OpendirOptions & { encoding?: BufferEncoding } | |
| 348 | ): Promise<Dir<string>> | |
| 349 | ||
| 350 | export function opendir( | |
| 351 | filepath: Path, | |
| 352 | opts: OpendirOptions & { encoding: 'buffer' } | |
| 353 | ): Promise<Dir<Buffer>> | |
| 354 | ||
| 355 | export function opendir(filepath: Path, opts: OpendirOptions): Promise<Dir> | |
| 356 | ||
| 357 | export function opendir(filepath: Path, encoding: BufferEncoding): Promise<Dir<string>> | |
| 358 | ||
| 359 | export function opendir(filepath: Path, encoding: 'buffer'): Promise<Dir<Buffer>> | |
| 360 | ||
| 361 | export function opendir(filepath: Path, encoding: BufferEncoding | 'buffer'): Promise<Dir> | |
| 362 | ||
| 363 | export function opendir(filepath: Path): Promise<Dir<string>> | |
| 364 | ||
| 365 | export function opendir( | |
| 366 | filepath: Path, | |
| 367 | opts: OpendirOptions & { encoding?: BufferEncoding }, | |
| 368 | cb: Callback<[dir: Dir<string> | null]> | |
| 369 | ): void | |
| 370 | ||
| 371 | export function opendir( | |
| 372 | filepath: Path, | |
| 373 | opts: OpendirOptions & { encoding: 'buffer' }, | |
| 374 | cb: Callback<[dir: Dir<Buffer> | null]> | |
| 375 | ): void | |
| 376 | ||
| 377 | export function opendir(filepath: Path, opts: OpendirOptions, cb: Callback<[dir: Dir | null]>): void | |
| 378 | ||
| 379 | export function opendir( | |
| 380 | filepath: Path, | |
| 381 | encoding: BufferEncoding, | |
| 382 | cb: Callback<[dir: Dir<string> | null]> | |
| 383 | ): void | |
| 384 | ||
| 385 | export function opendir( | |
| 386 | filepath: Path, | |
| 387 | encoding: 'buffer', | |
| 388 | cb: Callback<[dir: Dir<Buffer> | null]> | |
| 389 | ): void | |
| 390 | ||
| 391 | export function opendir( | |
| 392 | filepath: Path, | |
| 393 | encoding: BufferEncoding | 'buffer', | |
| 394 | cb: Callback<[dir: Dir | null]> | |
| 395 | ): void | |
| 396 | ||
| 397 | export function opendir(filepath: Path, cb: Callback<[dir: Dir<string> | null]>): void | |
| 398 | ||
| 399 | export function opendirSync( | |
| 400 | filepath: Path, | |
| 401 | opts: OpendirOptions & { encoding?: BufferEncoding } | |
| 402 | ): Dir<string> | |
| 403 | ||
| 404 | export function opendirSync( | |
| 405 | filepath: Path, | |
| 406 | opts: OpendirOptions & { encoding: 'buffer' } | |
| 407 | ): Dir<Buffer> | |
| 408 | ||
| 409 | export function opendirSync(filepath: Path, opts: OpendirOptions): Dir | |
| 410 | ||
| 411 | export function opendirSync(filepath: Path, encoding: BufferEncoding): Dir<string> | |
| 412 | ||
| 413 | export function opendirSync(filepath: Path, encoding: 'buffer'): Dir<Buffer> | |
| 414 | ||
| 415 | export function opendirSync(filepath: Path, encoding: BufferEncoding | 'buffer'): Dir | |
| 416 | ||
| 417 | export function opendirSync(filepath: Path): Dir<string> | |
| 418 | ||
| 419 | export function read( | |
| 420 | fd: number, | |
| 421 | buffer: Buffer | ArrayBufferView, | |
| 422 | offset?: number, | |
| 423 | len?: number, | |
| 424 | pos?: number | |
| 425 | ): Promise<number> | |
| 426 | ||
| 427 | export function read( | |
| 428 | fd: number, | |
| 429 | buffer: Buffer | ArrayBufferView, | |
| 430 | offset: number, | |
| 431 | len: number, | |
| 432 | pos: number, | |
| 433 | cb: Callback<[len: number]> | |
| 434 | ): void | |
| 435 | ||
| 436 | export function read( | |
| 437 | fd: number, | |
| 438 | buffer: Buffer | ArrayBufferView, | |
| 439 | offset: number, | |
| 440 | len: number, | |
| 441 | cb: Callback<[len: number]> | |
| 442 | ): void | |
| 443 | ||
| 444 | export function read( | |
| 445 | fd: number, | |
| 446 | buffer: Buffer | ArrayBufferView, | |
| 447 | offset: number, | |
| 448 | cb: Callback<[len: number]> | |
| 449 | ): void | |
| 450 | ||
| 451 | export function read( | |
| 452 | fd: number, | |
| 453 | buffer: Buffer | ArrayBufferView, | |
| 454 | cb: Callback<[len: number]> | |
| 455 | ): void | |
| 456 | ||
| 457 | export function readSync( | |
| 458 | fd: number, | |
| 459 | buffer: Buffer | ArrayBufferView, | |
| 460 | offset?: number, | |
| 461 | len?: number, | |
| 462 | pos?: number | |
| 463 | ): number | |
| 464 | ||
| 465 | export interface ReadFileOptions { | |
| 466 | encoding?: BufferEncoding | 'buffer' | |
| 467 | flag?: Flag | |
| 468 | } | |
| 469 | ||
| 470 | export function readFile( | |
| 471 | filepath: Path, | |
| 472 | opts: ReadFileOptions & { encoding: BufferEncoding } | |
| 473 | ): Promise<string> | |
| 474 | ||
| 475 | export function readFile( | |
| 476 | filepath: Path, | |
| 477 | opts: ReadFileOptions & { encoding?: 'buffer' } | |
| 478 | ): Promise<Buffer> | |
| 479 | ||
| 480 | export function readFile(filepath: Path, opts: ReadFileOptions): Promise<string | Buffer> | |
| 481 | ||
| 482 | export function readFile(filepath: Path, encoding: BufferEncoding): Promise<string> | |
| 483 | ||
| 484 | export function readFile(filepath: Path, encoding: 'buffer'): Promise<Buffer> | |
| 485 | ||
| 486 | export function readFile( | |
| 487 | filepath: Path, | |
| 488 | encoding?: BufferEncoding | 'buffer' | |
| 489 | ): Promise<string | Buffer> | |
| 490 | ||
| 491 | export function readFile(filepath: Path): Promise<Buffer> | |
| 492 | ||
| 493 | export function readFile( | |
| 494 | filepath: Path, | |
| 495 | opts: ReadFileOptions & { encoding: BufferEncoding }, | |
| 496 | cb: Callback<[buffer?: string]> | |
| 497 | ): void | |
| 498 | ||
| 499 | export function readFile( | |
| 500 | filepath: Path, | |
| 501 | opts: ReadFileOptions & { encoding?: 'buffer' }, | |
| 502 | cb: Callback<[buffer?: Buffer]> | |
| 503 | ): void | |
| 504 | ||
| 505 | export function readFile( | |
| 506 | filepath: Path, | |
| 507 | opts: ReadFileOptions, | |
| 508 | cb: Callback<[buffer?: string | Buffer]> | |
| 509 | ): void | |
| 510 | ||
| 511 | export function readFile( | |
| 512 | filepath: Path, | |
| 513 | encoding: BufferEncoding, | |
| 514 | cb: Callback<[buffer?: string]> | |
| 515 | ): void | |
| 516 | ||
| 517 | export function readFile(filepath: Path, encoding: 'buffer', cb: Callback<[buffer?: Buffer]>): void | |
| 518 | ||
| 519 | export function readFile( | |
| 520 | filepath: Path, | |
| 521 | encoding: BufferEncoding | 'buffer', | |
| 522 | cb: Callback<[buffer?: string | Buffer]> | |
| 523 | ): void | |
| 524 | ||
| 525 | export function readFile(filepath: Path, cb: Callback<[buffer?: Buffer]>): void | |
| 526 | ||
| 527 | export function readFileSync( | |
| 528 | filepath: Path, | |
| 529 | opts: ReadFileOptions & { encoding: BufferEncoding } | |
| 530 | ): string | |
| 531 | ||
| 532 | export function readFileSync( | |
| 533 | filepath: Path, | |
| 534 | opts: ReadFileOptions & { encoding?: 'buffer' } | |
| 535 | ): Buffer | |
| 536 | ||
| 537 | export function readFileSync(filepath: Path, opts: ReadFileOptions): string | Buffer | |
| 538 | ||
| 539 | export function readFileSync(filepath: Path, encoding: BufferEncoding): string | |
| 540 | ||
| 541 | export function readFileSync(filepath: Path, encoding: 'buffer'): Buffer | |
| 542 | ||
| 543 | export function readFileSync(filepath: Path, encoding?: BufferEncoding | 'buffer'): string | Buffer | |
| 544 | ||
| 545 | export function readFileSync(filepath: Path): Buffer | |
| 546 | ||
| 547 | export interface ReaddirOptions extends OpendirOptions { | |
| 548 | withFileTypes?: boolean | |
| 549 | } | |
| 550 | export function readdir( | |
| 551 | filepath: Path, | |
| 552 | opts: ReaddirOptions & { encoding?: BufferEncoding } | |
| 553 | ): Promise<Dir<string>[] | string[]> | |
| 554 | ||
| 555 | export function readdir( | |
| 556 | filepath: Path, | |
| 557 | opts: ReaddirOptions & { encoding?: BufferEncoding; withFileTypes: true } | |
| 558 | ): Promise<Dir<string>[]> | |
| 559 | ||
| 560 | export function readdir( | |
| 561 | filepath: Path, | |
| 562 | opts: ReaddirOptions & { encoding?: BufferEncoding; withFileTypes?: false } | |
| 563 | ): Promise<string[]> | |
| 564 | ||
| 565 | export function readdir( | |
| 566 | filepath: Path, | |
| 567 | opts: ReaddirOptions & { encoding: 'buffer' } | |
| 568 | ): Promise<Dir<Buffer>[] | Buffer[]> | |
| 569 | ||
| 570 | export function readdir( | |
| 571 | filepath: Path, | |
| 572 | opts: ReaddirOptions & { encoding: 'buffer'; withFileTypes: true } | |
| 573 | ): Promise<Dir<Buffer>[]> | |
| 574 | ||
| 575 | export function readdir( | |
| 576 | filepath: Path, | |
| 577 | opts: ReaddirOptions & { encoding: 'buffer'; withFileTypes?: false } | |
| 578 | ): Promise<Buffer[]> | |
| 579 | ||
| 580 | export function readdir( | |
| 581 | filepath: Path, | |
| 582 | opts: ReaddirOptions & { withFileTypes: true } | |
| 583 | ): Promise<Dir<string | Buffer>[]> | |
| 584 | ||
| 585 | export function readdir( | |
| 586 | filepath: Path, | |
| 587 | opts: ReaddirOptions & { withFileTypes?: false } | |
| 588 | ): Promise<string[] | Buffer[]> | |
| 589 | ||
| 590 | export function readdir(filepath: Path, opts: ReaddirOptions): Promise<Dir[] | string[] | Buffer[]> | |
| 591 | ||
| 592 | export function readdir(filepath: Path, encoding: BufferEncoding): Promise<string[]> | |
| 593 | ||
| 594 | export function readdir(filepath: Path, encoding: 'buffer'): Promise<Buffer[]> | |
| 595 | ||
| 596 | export function readdir( | |
| 597 | filepath: Path, | |
| 598 | encoding: BufferEncoding | 'buffer' | |
| 599 | ): Promise<string[] | Buffer[]> | |
| 600 | ||
| 601 | export function readdir(filepath: Path): Promise<string[]> | |
| 602 | ||
| 603 | export function readdir( | |
| 604 | filepath: Path, | |
| 605 | opts: ReaddirOptions & { encoding?: BufferEncoding }, | |
| 606 | cb: Callback<[entries: Dir<string>[] | string[] | null]> | |
| 607 | ): void | |
| 608 | ||
| 609 | export function readdir( | |
| 610 | filepath: Path, | |
| 611 | opts: ReaddirOptions & { encoding?: BufferEncoding; withFileTypes: true }, | |
| 612 | cb: Callback<[entries: Dir<string>[] | null]> | |
| 613 | ): void | |
| 614 | ||
| 615 | export function readdir( | |
| 616 | filepath: Path, | |
| 617 | opts: ReaddirOptions & { encoding?: BufferEncoding; withFileTypes?: false }, | |
| 618 | cb: Callback<[entries: string[] | null]> | |
| 619 | ): void | |
| 620 | ||
| 621 | export function readdir( | |
| 622 | filepath: Path, | |
| 623 | opts: ReaddirOptions & { encoding: 'buffer' }, | |
| 624 | cb: Callback<[entries: Dir<Buffer>[] | Buffer[] | null]> | |
| 625 | ): void | |
| 626 | ||
| 627 | export function readdir( | |
| 628 | filepath: Path, | |
| 629 | opts: ReaddirOptions & { encoding: 'buffer'; withFileTypes: true }, | |
| 630 | cb: Callback<[entries: Dir<Buffer>[] | null]> | |
| 631 | ): void | |
| 632 | ||
| 633 | export function readdir( | |
| 634 | filepath: Path, | |
| 635 | opts: ReaddirOptions & { encoding: 'buffer'; withFileTypes?: false }, | |
| 636 | cb: Callback<[entries: Buffer[] | null]> | |
| 637 | ): void | |
| 638 | ||
| 639 | export function readdir( | |
| 640 | filepath: Path, | |
| 641 | opts: ReaddirOptions & { withFileTypes: true }, | |
| 642 | cb: Callback<[entries: Dir<string | Buffer>[] | null]> | |
| 643 | ): void | |
| 644 | ||
| 645 | export function readdir( | |
| 646 | filepath: Path, | |
| 647 | opts: ReaddirOptions & { withFileTypes?: false }, | |
| 648 | cb: Callback<[entries: string[] | Buffer[] | null]> | |
| 649 | ): void | |
| 650 | ||
| 651 | export function readdir( | |
| 652 | filepath: Path, | |
| 653 | opts: ReaddirOptions, | |
| 654 | cb: Callback<[entries: Dir[] | string[] | Buffer[] | null]> | |
| 655 | ): void | |
| 656 | ||
| 657 | export function readdir( | |
| 658 | filepath: Path, | |
| 659 | encoding: BufferEncoding, | |
| 660 | cb: Callback<[entries: string[] | null]> | |
| 661 | ): void | |
| 662 | ||
| 663 | export function readdir( | |
| 664 | filepath: Path, | |
| 665 | encoding: 'buffer', | |
| 666 | cb: Callback<[entries: Buffer[] | null]> | |
| 667 | ): void | |
| 668 | ||
| 669 | export function readdir( | |
| 670 | filepath: Path, | |
| 671 | encoding: BufferEncoding | 'buffer', | |
| 672 | cb: Callback<[entries: string[] | Buffer[] | null]> | |
| 673 | ): void | |
| 674 | ||
| 675 | export function readdir(filepath: Path, cb: Callback<[entries: string[] | null]>): void | |
| 676 | ||
| 677 | export function readdirSync( | |
| 678 | filepath: Path, | |
| 679 | opts: ReaddirOptions & { encoding?: BufferEncoding } | |
| 680 | ): Dir<string>[] | string[] | |
| 681 | ||
| 682 | export function readdirSync( | |
| 683 | filepath: Path, | |
| 684 | opts: ReaddirOptions & { encoding?: BufferEncoding; withFileTypes: true } | |
| 685 | ): Dir<string>[] | |
| 686 | ||
| 687 | export function readdirSync( | |
| 688 | filepath: Path, | |
| 689 | opts: ReaddirOptions & { encoding?: BufferEncoding; withFileTypes?: false } | |
| 690 | ): string[] | |
| 691 | ||
| 692 | export function readdirSync( | |
| 693 | filepath: Path, | |
| 694 | opts: ReaddirOptions & { encoding: 'buffer' } | |
| 695 | ): Dir<Buffer>[] | Buffer[] | |
| 696 | ||
| 697 | export function readdirSync( | |
| 698 | filepath: Path, | |
| 699 | opts: ReaddirOptions & { encoding: 'buffer'; withFileTypes: true } | |
| 700 | ): Dir<Buffer>[] | |
| 701 | ||
| 702 | export function readdirSync( | |
| 703 | filepath: Path, | |
| 704 | opts: ReaddirOptions & { encoding: 'buffer'; withFileTypes?: false } | |
| 705 | ): Buffer[] | |
| 706 | ||
| 707 | export function readdirSync( | |
| 708 | filepath: Path, | |
| 709 | opts: ReaddirOptions & { withFileTypes: true } | |
| 710 | ): Dir<string | Buffer>[] | |
| 711 | ||
| 712 | export function readdirSync( | |
| 713 | filepath: Path, | |
| 714 | opts: ReaddirOptions & { withFileTypes?: false } | |
| 715 | ): string[] | Buffer[] | |
| 716 | ||
| 717 | export function readdirSync(filepath: Path, opts: ReaddirOptions): Dir[] | string[] | Buffer[] | |
| 718 | ||
| 719 | export function readdirSync(filepath: Path, encoding: BufferEncoding): string[] | |
| 720 | ||
| 721 | export function readdirSync(filepath: Path, encoding: 'buffer'): Buffer[] | |
| 722 | ||
| 723 | export function readdirSync( | |
| 724 | filepath: Path, | |
| 725 | encoding: BufferEncoding | 'buffer' | |
| 726 | ): string[] | Buffer[] | |
| 727 | ||
| 728 | export function readdirSync(filepath: Path): string[] | |
| 729 | ||
| 730 | export interface ReadlinkOptions { | |
| 731 | encoding?: BufferEncoding | 'buffer' | |
| 732 | } | |
| 733 | ||
| 734 | export function readlink( | |
| 735 | filepath: Path, | |
| 736 | opts: ReadlinkOptions & { encoding?: BufferEncoding } | |
| 737 | ): Promise<string> | |
| 738 | ||
| 739 | export function readlink( | |
| 740 | filepath: Path, | |
| 741 | opts: ReadlinkOptions & { encoding: 'buffer' } | |
| 742 | ): Promise<Buffer> | |
| 743 | ||
| 744 | export function readlink(filepath: Path, opts: ReadlinkOptions): Promise<string | Buffer> | |
| 745 | ||
| 746 | export function readlink(filepath: Path, encoding: BufferEncoding): Promise<string> | |
| 747 | ||
| 748 | export function readlink(filepath: Path, encoding: 'buffer'): Promise<Buffer> | |
| 749 | ||
| 750 | export function readlink( | |
| 751 | filepath: Path, | |
| 752 | encoding: BufferEncoding | 'buffer' | |
| 753 | ): Promise<string | Buffer> | |
| 754 | ||
| 755 | export function readlink(filepath: Path): Promise<string> | |
| 756 | ||
| 757 | export function readlink( | |
| 758 | filepath: Path, | |
| 759 | opts: ReadlinkOptions & { encoding?: BufferEncoding }, | |
| 760 | cb: Callback<[link: string | null]> | |
| 761 | ): void | |
| 762 | ||
| 763 | export function readlink( | |
| 764 | filepath: Path, | |
| 765 | opts: ReadlinkOptions & { encoding: 'buffer' }, | |
| 766 | cb: Callback<[link: Buffer | null]> | |
| 767 | ): void | |
| 768 | ||
| 769 | export function readlink( | |
| 770 | filepath: Path, | |
| 771 | opts: ReadlinkOptions, | |
| 772 | cb: Callback<[link: string | Buffer | null]> | |
| 773 | ): void | |
| 774 | ||
| 775 | export function readlink( | |
| 776 | filepath: Path, | |
| 777 | encoding: BufferEncoding, | |
| 778 | cb: Callback<[link: string | null]> | |
| 779 | ): void | |
| 780 | ||
| 781 | export function readlink( | |
| 782 | filepath: Path, | |
| 783 | encoding: 'buffer', | |
| 784 | cb: Callback<[link: Buffer | null]> | |
| 785 | ): void | |
| 786 | ||
| 787 | export function readlink( | |
| 788 | filepath: Path, | |
| 789 | encoding: BufferEncoding | 'buffer', | |
| 790 | cb: Callback<[link: string | Buffer | null]> | |
| 791 | ): void | |
| 792 | ||
| 793 | export function readlink(filepath: Path, cb: Callback<[link: string | null]>): void | |
| 794 | ||
| 795 | export function readlinkSync( | |
| 796 | filepath: Path, | |
| 797 | opts: ReadlinkOptions & { encoding?: BufferEncoding } | |
| 798 | ): string | |
| 799 | ||
| 800 | export function readlinkSync(filepath: Path, opts: ReadlinkOptions & { encoding: 'buffer' }): Buffer | |
| 801 | ||
| 802 | export function readlinkSync(filepath: Path, opts: ReadlinkOptions): string | Buffer | |
| 803 | ||
| 804 | export function readlinkSync(filepath: Path, encoding: BufferEncoding): string | |
| 805 | ||
| 806 | export function readlinkSync(filepath: Path, encoding: 'buffer'): Buffer | |
| 807 | ||
| 808 | export function readlinkSync(filepath: Path, encoding: BufferEncoding | 'buffer'): string | Buffer | |
| 809 | ||
| 810 | export function readlinkSync(filepath: Path): string | |
| 811 | ||
| 812 | export function readv(fd: number, buffers: ArrayBufferView[], position?: number): Promise<number> | |
| 813 | ||
| 814 | export function readv( | |
| 815 | fd: number, | |
| 816 | buffers: ArrayBufferView[], | |
| 817 | position: number, | |
| 818 | cb: Callback<[len: number]> | |
| 819 | ): void | |
| 820 | ||
| 821 | export function readv(fd: number, buffers: ArrayBufferView[], cb: Callback<[len: number]>): void | |
| 822 | ||
| 823 | export function readvSync(fd: number, buffers: ArrayBufferView[], position?: number): number | |
| 824 | ||
| 825 | export interface RealpathOptions { | |
| 826 | encoding?: BufferEncoding | 'buffer' | |
| 827 | } | |
| 828 | ||
| 829 | export function realpath( | |
| 830 | filepath: Path, | |
| 831 | opts: RealpathOptions & { encoding?: BufferEncoding } | |
| 832 | ): Promise<string> | |
| 833 | ||
| 834 | export function realpath( | |
| 835 | filepath: Path, | |
| 836 | opts: RealpathOptions & { encoding: 'buffer' } | |
| 837 | ): Promise<Buffer> | |
| 838 | ||
| 839 | export function realpath(filepath: Path, opts: RealpathOptions): Promise<string | Buffer> | |
| 840 | ||
| 841 | export function realpath(filepath: Path, encoding: BufferEncoding): Promise<string> | |
| 842 | ||
| 843 | export function realpath(filepath: Path, encoding: 'buffer'): Promise<Buffer> | |
| 844 | ||
| 845 | export function realpath( | |
| 846 | filepath: Path, | |
| 847 | encoding: BufferEncoding | 'buffer' | |
| 848 | ): Promise<string | Buffer> | |
| 849 | ||
| 850 | export function realpath(filepath: Path): Promise<string> | |
| 851 | ||
| 852 | export function realpath( | |
| 853 | filepath: Path, | |
| 854 | opts: RealpathOptions & { encoding?: BufferEncoding }, | |
| 855 | cb: Callback<[path: string | null]> | |
| 856 | ): void | |
| 857 | ||
| 858 | export function realpath( | |
| 859 | filepath: Path, | |
| 860 | opts: RealpathOptions & { encoding: 'buffer' }, | |
| 861 | cb: Callback<[path: Buffer | null]> | |
| 862 | ): void | |
| 863 | ||
| 864 | export function realpath( | |
| 865 | filepath: Path, | |
| 866 | opts: RealpathOptions, | |
| 867 | cb: Callback<[path: string | Buffer | null]> | |
| 868 | ): void | |
| 869 | ||
| 870 | export function realpath( | |
| 871 | filepath: Path, | |
| 872 | encoding: BufferEncoding, | |
| 873 | cb: Callback<[path: string | null]> | |
| 874 | ): void | |
| 875 | ||
| 876 | export function realpath( | |
| 877 | filepath: Path, | |
| 878 | encoding: 'buffer', | |
| 879 | cb: Callback<[path: Buffer | null]> | |
| 880 | ): void | |
| 881 | ||
| 882 | export function realpath( | |
| 883 | filepath: Path, | |
| 884 | encoding: BufferEncoding | 'buffer', | |
| 885 | cb: Callback<[path: string | Buffer | null]> | |
| 886 | ): void | |
| 887 | ||
| 888 | export function realpath(filepath: Path, cb: Callback<[path: string | null]>): void | |
| 889 | ||
| 890 | export function realpathSync( | |
| 891 | filepath: Path, | |
| 892 | opts: RealpathOptions & { encoding?: BufferEncoding } | |
| 893 | ): string | |
| 894 | ||
| 895 | export function realpathSync(filepath: Path, opts: RealpathOptions & { encoding: 'buffer' }): Buffer | |
| 896 | ||
| 897 | export function realpathSync(filepath: Path, opts: RealpathOptions): string | Buffer | |
| 898 | ||
| 899 | export function realpathSync(filepath: Path, encoding: BufferEncoding): string | |
| 900 | ||
| 901 | export function realpathSync(filepath: Path, encoding: 'buffer'): Buffer | |
| 902 | ||
| 903 | export function realpathSync(filepath: Path, encoding: BufferEncoding | 'buffer'): string | Buffer | |
| 904 | ||
| 905 | export function realpathSync(filepath: Path): string | |
| 906 | ||
| 907 | export function rename(src: string, dst: string): Promise<void> | |
| 908 | ||
| 909 | export function rename(src: string, dst: string, cb: Callback): void | |
| 910 | ||
| 911 | export function renameSync(src: string, dst: string): void | |
| 912 | ||
| 913 | export interface RmOptions { | |
| 914 | force?: boolean | |
| 915 | recursive?: boolean | |
| 916 | } | |
| 917 | ||
| 918 | export function rm(filepath: Path, opts?: RmOptions): Promise<void> | |
| 919 | ||
| 920 | export function rm(filepath: Path, opts: RmOptions, cb: Callback): void | |
| 921 | ||
| 922 | export function rm(filepath: Path, cb: Callback): void | |
| 923 | ||
| 924 | export function rmSync(filepath: Path, opts?: RmOptions): void | |
| 925 | ||
| 926 | export function rmdir(filepath: Path, cb: Callback): void | |
| 927 | ||
| 928 | export function rmdirSync(filepath: Path): void | |
| 929 | ||
| 930 | export function stat(filepath: Path): Promise<Stats> | |
| 931 | ||
| 932 | export function stat(filepath: Path, cb: Callback<[stats: Stats | null]>): void | |
| 933 | ||
| 934 | export function statSync(filepath: Path): Stats | |
| 935 | ||
| 936 | export function symlink(target: Path, filepath: Path, type?: string | number): Promise<void> | |
| 937 | ||
| 938 | export function symlink(target: Path, filepath: Path, type: string | number, cb: Callback): void | |
| 939 | ||
| 940 | export function symlink(target: string, filepath: Path, cb: Callback): void | |
| 941 | ||
| 942 | export function symlinkSync(target: string, filepath: Path, type?: string | number): void | |
| 943 | ||
| 944 | export function unlink(filepath: Path): Promise<void> | |
| 945 | ||
| 946 | export function unlink(filepath: Path, cb: Callback): void | |
| 947 | ||
| 948 | export function unlinkSync(filepath: Path): void | |
| 949 | ||
| 950 | export function watch( | |
| 951 | filepath: Path, | |
| 952 | opts: WatcherOptions & { encoding?: BufferEncoding }, | |
| 953 | cb: (eventType: WatcherEventType, filename: string) => void | |
| 954 | ): Watcher<string> | |
| 955 | ||
| 956 | export function watch( | |
| 957 | filepath: Path, | |
| 958 | opts: WatcherOptions & { encoding: 'buffer' }, | |
| 959 | cb: (eventType: WatcherEventType, filename: Buffer) => void | |
| 960 | ): Watcher<Buffer> | |
| 961 | ||
| 962 | export function watch( | |
| 963 | filepath: Path, | |
| 964 | opts: WatcherOptions, | |
| 965 | cb: (eventType: WatcherEventType, filename: string | Buffer) => void | |
| 966 | ): Watcher | |
| 967 | ||
| 968 | export function watch( | |
| 969 | filepath: Path, | |
| 970 | encoding: BufferEncoding, | |
| 971 | cb: (evenType: WatcherEventType, filename: string) => void | |
| 972 | ): Watcher<string> | |
| 973 | ||
| 974 | export function watch( | |
| 975 | filepath: Path, | |
| 976 | encoding: 'buffer', | |
| 977 | cb: (evenType: WatcherEventType, filename: Buffer) => void | |
| 978 | ): Watcher<Buffer> | |
| 979 | ||
| 980 | export function watch( | |
| 981 | filepath: Path, | |
| 982 | encoding: BufferEncoding | 'buffer', | |
| 983 | cb: (evenType: WatcherEventType, filename: string | Buffer) => void | |
| 984 | ): Watcher | |
| 985 | ||
| 986 | export function watch( | |
| 987 | filepath: Path, | |
| 988 | cb: (eventType: WatcherEventType, filename: string) => void | |
| 989 | ): Watcher<string> | |
| 990 | ||
| 991 | export function write( | |
| 992 | fd: number, | |
| 993 | data: Buffer | ArrayBufferView, | |
| 994 | offset?: number, | |
| 995 | len?: number, | |
| 996 | pos?: number | |
| 997 | ): Promise<number> | |
| 998 | ||
| 999 | export function write( | |
| 1000 | fd: number, | |
| 1001 | data: string, | |
| 1002 | pos?: number, | |
| 1003 | encoding?: BufferEncoding | |
| 1004 | ): Promise<number> | |
| 1005 | ||
| 1006 | export function write( | |
| 1007 | fd: number, | |
| 1008 | data: Buffer | ArrayBufferView, | |
| 1009 | offset: number, | |
| 1010 | len: number, | |
| 1011 | pos: number, | |
| 1012 | cb: Callback<[len: number]> | |
| 1013 | ): void | |
| 1014 | ||
| 1015 | export function write( | |
| 1016 | fd: number, | |
| 1017 | data: Buffer | ArrayBufferView, | |
| 1018 | offset: number, | |
| 1019 | len: number, | |
| 1020 | cb: Callback<[len: number]> | |
| 1021 | ): void | |
| 1022 | ||
| 1023 | export function write( | |
| 1024 | fd: number, | |
| 1025 | data: string, | |
| 1026 | pos: number, | |
| 1027 | encoding: BufferEncoding, | |
| 1028 | cb: Callback<[len: number]> | |
| 1029 | ): void | |
| 1030 | ||
| 1031 | export function write( | |
| 1032 | fd: number, | |
| 1033 | data: Buffer | ArrayBufferView, | |
| 1034 | offset: number, | |
| 1035 | cb: Callback<[len: number]> | |
| 1036 | ): void | |
| 1037 | ||
| 1038 | export function write(fd: number, data: string, pos: number, cb: Callback<[len: number]>): void | |
| 1039 | ||
| 1040 | export function write(fd: number, data: Buffer | ArrayBufferView, cb: Callback<[len: number]>): void | |
| 1041 | ||
| 1042 | export function write(fd: number, data: string, cb: Callback<[len: number]>): void | |
| 1043 | ||
| 1044 | export function writeSync( | |
| 1045 | fd: number, | |
| 1046 | data: Buffer | ArrayBufferView, | |
| 1047 | offset?: number, | |
| 1048 | len?: number, | |
| 1049 | pos?: number | |
| 1050 | ): number | |
| 1051 | ||
| 1052 | export function writeSync(fd: number, data: string, pos?: number, encoding?: BufferEncoding): number | |
| 1053 | ||
| 1054 | export interface WriteFileOptions { | |
| 1055 | encoding?: BufferEncoding | |
| 1056 | flag?: Flag | |
| 1057 | mode?: number | |
| 1058 | } | |
| 1059 | ||
| 1060 | export function writeFile( | |
| 1061 | filepath: Path, | |
| 1062 | data: string | Buffer | ArrayBufferView, | |
| 1063 | opts?: WriteFileOptions | |
| 1064 | ): Promise<void> | |
| 1065 | ||
| 1066 | export function writeFile( | |
| 1067 | filepath: Path, | |
| 1068 | data: string | Buffer | ArrayBufferView, | |
| 1069 | encoding: BufferEncoding | |
| 1070 | ): Promise<void> | |
| 1071 | ||
| 1072 | export function writeFile( | |
| 1073 | filepath: Path, | |
| 1074 | data: string | Buffer | ArrayBufferView, | |
| 1075 | opts: WriteFileOptions, | |
| 1076 | cb: Callback | |
| 1077 | ): void | |
| 1078 | ||
| 1079 | export function writeFile( | |
| 1080 | filepath: Path, | |
| 1081 | data: string | Buffer | ArrayBufferView, | |
| 1082 | encoding: BufferEncoding, | |
| 1083 | cb: Callback | |
| 1084 | ): void | |
| 1085 | ||
| 1086 | export function writeFile( | |
| 1087 | filepath: Path, | |
| 1088 | data: string | Buffer | ArrayBufferView, | |
| 1089 | cb: Callback | |
| 1090 | ): void | |
| 1091 | ||
| 1092 | export function writeFileSync( | |
| 1093 | filepath: Path, | |
| 1094 | data: string | Buffer | ArrayBufferView, | |
| 1095 | opts?: WriteFileOptions | |
| 1096 | ): void | |
| 1097 | ||
| 1098 | export function writeFileSync( | |
| 1099 | filepath: Path, | |
| 1100 | data: string | Buffer | ArrayBufferView, | |
| 1101 | encoding: BufferEncoding | |
| 1102 | ): void | |
| 1103 | ||
| 1104 | export function writev(fd: number, buffers: ArrayBufferView[], pos?: number): Promise<number> | |
| 1105 | ||
| 1106 | export function writev( | |
| 1107 | fd: number, | |
| 1108 | buffers: ArrayBufferView[], | |
| 1109 | pos: number, | |
| 1110 | cb: Callback<[len: number]> | |
| 1111 | ): void | |
| 1112 | ||
| 1113 | export function writev(fd: number, buffers: ArrayBufferView[], cb: Callback<[len: number]>): void | |
| 1114 | ||
| 1115 | export function writevSync(fd: number, buffers: ArrayBufferView[], pos?: number): number |
| … | … | @@ -0,0 +1,2425 @@ |
| 1 | const FIFO = require('fast-fifo') | |
| 2 | const EventEmitter = require('bare-events') | |
| 3 | const path = require('bare-path') | |
| 4 | const { isURL, fileURLToPath } = require('bare-url') | |
| 5 | const { Readable, Writable } = require('bare-stream') | |
| 6 | const binding = require('./binding') | |
| 7 | const constants = require('./lib/constants') | |
| 8 | const FileError = require('./lib/errors') | |
| 9 | ||
| 10 | const isWindows = Bare.platform === 'win32' | |
| 11 | ||
| 12 | exports.constants = constants | |
| 13 | ||
| 14 | class FileRequest { | |
| 15 | static borrow() { | |
| 16 | if (this._free.length > 0) return this._free.pop() | |
| 17 | return new FileRequest() | |
| 18 | } | |
| 19 | ||
| 20 | static return(req) { | |
| 21 | if (this._free.length < 32) this._free.push(req.reset()) | |
| 22 | else req.destroy() | |
| 23 | } | |
| 24 | ||
| 25 | constructor() { | |
| 26 | this._reset() | |
| 27 | this._handle = binding.requestInit(this, this._onresult) | |
| 28 | } | |
| 29 | ||
| 30 | get handle() { | |
| 31 | return this._handle | |
| 32 | } | |
| 33 | ||
| 34 | retain(value) { | |
| 35 | this._retain = value // Tie the lifetime of `value` to the lifetime of `this` | |
| 36 | } | |
| 37 | ||
| 38 | reset() { | |
| 39 | if (this._handle === null) return this | |
| 40 | ||
| 41 | binding.requestReset(this._handle) | |
| 42 | ||
| 43 | this._reset() | |
| 44 | ||
| 45 | return this | |
| 46 | } | |
| 47 | ||
| 48 | destroy() { | |
| 49 | if (this._handle === null) return this | |
| 50 | ||
| 51 | binding.requestDestroy(this._handle) | |
| 52 | ||
| 53 | this._reset() | |
| 54 | this._handle = null | |
| 55 | ||
| 56 | return this | |
| 57 | } | |
| 58 | ||
| 59 | then(resolve, reject) { | |
| 60 | return this._promise.then(resolve, reject) | |
| 61 | } | |
| 62 | ||
| 63 | return() { | |
| 64 | if (this._handle === null) return this | |
| 65 | ||
| 66 | FileRequest.return(this) | |
| 67 | ||
| 68 | return this | |
| 69 | } | |
| 70 | ||
| 71 | _reset() { | |
| 72 | this._promise = new Promise((resolve, reject) => { | |
| 73 | this._resolve = resolve | |
| 74 | this._reject = reject | |
| 75 | }) | |
| 76 | this._retain = null | |
| 77 | } | |
| 78 | ||
| 79 | _onresult(err, status) { | |
| 80 | if (err) this._reject(err) | |
| 81 | else this._resolve(status) | |
| 82 | } | |
| 83 | } | |
| 84 | ||
| 85 | FileRequest._free = [] | |
| 86 | ||
| 87 | function ok(result, cb) { | |
| 88 | if (typeof result === 'function') { | |
| 89 | cb = result | |
| 90 | result = undefined | |
| 91 | } | |
| 92 | ||
| 93 | if (cb) cb(null, result) | |
| 94 | else return result | |
| 95 | } | |
| 96 | ||
| 97 | function fail(err, cb) { | |
| 98 | if (cb) cb(err) | |
| 99 | else throw err | |
| 100 | } | |
| 101 | ||
| 102 | function done(err, result, cb) { | |
| 103 | if (typeof result === 'function') { | |
| 104 | cb = result | |
| 105 | result = undefined | |
| 106 | } | |
| 107 | ||
| 108 | if (err) fail(err, cb) | |
| 109 | else return ok(result, cb) | |
| 110 | } | |
| 111 | ||
| 112 | async function open(filepath, flags = 'r', mode = 0o666, cb) { | |
| 113 | if (typeof flags === 'function') { | |
| 114 | cb = flags | |
| 115 | flags = 'r' | |
| 116 | mode = 0o666 | |
| 117 | } else if (typeof mode === 'function') { | |
| 118 | cb = mode | |
| 119 | mode = 0o666 | |
| 120 | } | |
| 121 | ||
| 122 | if (typeof flags === 'string') flags = toFlags(flags) | |
| 123 | if (typeof mode === 'string') mode = toMode(mode) | |
| 124 | ||
| 125 | filepath = toNamespacedPath(filepath) | |
| 126 | ||
| 127 | const req = FileRequest.borrow() | |
| 128 | ||
| 129 | let fd | |
| 130 | let err = null | |
| 131 | try { | |
| 132 | binding.open(req.handle, filepath, flags, mode) | |
| 133 | ||
| 134 | fd = await req | |
| 135 | } catch (e) { | |
| 136 | err = new FileError(e.message, { | |
| 137 | operation: 'open', | |
| 138 | code: e.code, | |
| 139 | path: filepath | |
| 140 | }) | |
| 141 | } finally { | |
| 142 | req.return() | |
| 143 | } | |
| 144 | ||
| 145 | return done(err, fd, cb) | |
| 146 | } | |
| 147 | ||
| 148 | function openSync(filepath, flags = 'r', mode = 0o666) { | |
| 149 | if (typeof flags === 'string') flags = toFlags(flags) | |
| 150 | if (typeof mode === 'string') mode = toMode(mode) | |
| 151 | ||
| 152 | filepath = toNamespacedPath(filepath) | |
| 153 | ||
| 154 | const req = FileRequest.borrow() | |
| 155 | ||
| 156 | try { | |
| 157 | return binding.openSync(req.handle, filepath, flags, mode) | |
| 158 | } catch (e) { | |
| 159 | throw new FileError(e.message, { | |
| 160 | operation: 'open', | |
| 161 | code: e.code, | |
| 162 | path: filepath | |
| 163 | }) | |
| 164 | } finally { | |
| 165 | req.return() | |
| 166 | } | |
| 167 | } | |
| 168 | ||
| 169 | async function close(fd, cb) { | |
| 170 | const req = FileRequest.borrow() | |
| 171 | ||
| 172 | let err = null | |
| 173 | try { | |
| 174 | binding.close(req.handle, fd) | |
| 175 | ||
| 176 | await req | |
| 177 | } catch (e) { | |
| 178 | err = new FileError(e.message, { operation: 'close', code: e.code, fd }) | |
| 179 | } finally { | |
| 180 | req.return() | |
| 181 | } | |
| 182 | ||
| 183 | return done(err, cb) | |
| 184 | } | |
| 185 | ||
| 186 | function closeSync(fd) { | |
| 187 | const req = FileRequest.borrow() | |
| 188 | ||
| 189 | try { | |
| 190 | binding.closeSync(req.handle, fd) | |
| 191 | } catch (e) { | |
| 192 | throw new FileError(e.message, { operation: 'close', code: e.code, fd }) | |
| 193 | } finally { | |
| 194 | req.return() | |
| 195 | } | |
| 196 | } | |
| 197 | ||
| 198 | async function access(filepath, mode = constants.F_OK, cb) { | |
| 199 | if (typeof mode === 'function') { | |
| 200 | cb = mode | |
| 201 | mode = constants.F_OK | |
| 202 | } | |
| 203 | ||
| 204 | filepath = toNamespacedPath(filepath) | |
| 205 | ||
| 206 | const req = FileRequest.borrow() | |
| 207 | ||
| 208 | let err = null | |
| 209 | try { | |
| 210 | binding.access(req.handle, filepath, mode) | |
| 211 | ||
| 212 | await req | |
| 213 | } catch (e) { | |
| 214 | err = new FileError(e.message, { | |
| 215 | operation: 'access', | |
| 216 | code: e.code, | |
| 217 | path: filepath | |
| 218 | }) | |
| 219 | } finally { | |
| 220 | req.return() | |
| 221 | } | |
| 222 | ||
| 223 | return done(err, cb) | |
| 224 | } | |
| 225 | ||
| 226 | function accessSync(filepath, mode = constants.F_OK) { | |
| 227 | filepath = toNamespacedPath(filepath) | |
| 228 | ||
| 229 | const req = FileRequest.borrow() | |
| 230 | ||
| 231 | try { | |
| 232 | binding.accessSync(req.handle, filepath, mode) | |
| 233 | } catch (e) { | |
| 234 | throw new FileError(e.message, { | |
| 235 | operation: 'access', | |
| 236 | code: e.code, | |
| 237 | path: filepath | |
| 238 | }) | |
| 239 | } finally { | |
| 240 | req.return() | |
| 241 | } | |
| 242 | } | |
| 243 | ||
| 244 | async function exists(filepath, cb) { | |
| 245 | let ok = true | |
| 246 | try { | |
| 247 | await access(filepath) | |
| 248 | } catch { | |
| 249 | ok = false | |
| 250 | } | |
| 251 | ||
| 252 | return done(null, ok, cb) | |
| 253 | } | |
| 254 | ||
| 255 | function existsSync(filepath) { | |
| 256 | try { | |
| 257 | accessSync(filepath) | |
| 258 | } catch { | |
| 259 | return false | |
| 260 | } | |
| 261 | ||
| 262 | return true | |
| 263 | } | |
| 264 | ||
| 265 | async function read(fd, buffer, offset = 0, len = buffer.byteLength - offset, pos = -1, cb) { | |
| 266 | if (typeof offset === 'function') { | |
| 267 | cb = offset | |
| 268 | offset = 0 | |
| 269 | len = buffer.byteLength | |
| 270 | pos = -1 | |
| 271 | } else if (typeof len === 'function') { | |
| 272 | cb = len | |
| 273 | len = buffer.byteLength - offset | |
| 274 | pos = -1 | |
| 275 | } else if (typeof pos === 'function') { | |
| 276 | cb = pos | |
| 277 | pos = -1 | |
| 278 | } | |
| 279 | ||
| 280 | if (typeof pos !== 'number') pos = -1 | |
| 281 | ||
| 282 | const req = FileRequest.borrow() | |
| 283 | ||
| 284 | let bytes | |
| 285 | let err = null | |
| 286 | try { | |
| 287 | binding.read(req.handle, fd, buffer, offset, len, pos) | |
| 288 | ||
| 289 | bytes = await req | |
| 290 | } catch (e) { | |
| 291 | err = new FileError(e.message, { operation: 'read', code: e.code, fd }) | |
| 292 | } finally { | |
| 293 | req.return() | |
| 294 | } | |
| 295 | ||
| 296 | return done(err, bytes, cb) | |
| 297 | } | |
| 298 | ||
| 299 | function readSync(fd, buffer, offset = 0, len = buffer.byteLength - offset, pos = -1) { | |
| 300 | const req = FileRequest.borrow() | |
| 301 | ||
| 302 | try { | |
| 303 | return binding.readSync(req.handle, fd, buffer, offset, len, pos) | |
| 304 | } catch (e) { | |
| 305 | throw new FileError(e.message, { operation: 'read', code: e.code, fd }) | |
| 306 | } finally { | |
| 307 | req.return() | |
| 308 | } | |
| 309 | } | |
| 310 | ||
| 311 | async function readv(fd, buffers, pos = -1, cb) { | |
| 312 | if (typeof pos === 'function') { | |
| 313 | cb = pos | |
| 314 | pos = -1 | |
| 315 | } | |
| 316 | ||
| 317 | if (typeof pos !== 'number') pos = -1 | |
| 318 | ||
| 319 | const req = FileRequest.borrow() | |
| 320 | ||
| 321 | let bytes | |
| 322 | let err = null | |
| 323 | try { | |
| 324 | binding.readv(req.handle, fd, buffers, pos) | |
| 325 | ||
| 326 | bytes = await req | |
| 327 | } catch (e) { | |
| 328 | err = new FileError(e.message, { operation: 'readv', code: e.code, fd }) | |
| 329 | } finally { | |
| 330 | req.return() | |
| 331 | } | |
| 332 | ||
| 333 | return done(err, bytes, cb) | |
| 334 | } | |
| 335 | ||
| 336 | function readvSync(fd, buffers, pos = -1) { | |
| 337 | if (typeof pos !== 'number') pos = -1 | |
| 338 | ||
| 339 | const req = FileRequest.borrow() | |
| 340 | ||
| 341 | try { | |
| 342 | return binding.readvSync(req.handle, fd, buffers, pos) | |
| 343 | } catch (e) { | |
| 344 | throw new FileError(e.message, { operation: 'readv', code: e.code, fd }) | |
| 345 | } finally { | |
| 346 | req.return() | |
| 347 | } | |
| 348 | } | |
| 349 | ||
| 350 | async function write(fd, data, offset, len, pos = -1, cb) { | |
| 351 | if (typeof data === 'string') { | |
| 352 | let encoding = len | |
| 353 | cb = pos | |
| 354 | pos = offset | |
| 355 | ||
| 356 | if (typeof pos === 'function') { | |
| 357 | cb = pos | |
| 358 | pos = -1 | |
| 359 | encoding = 'utf8' | |
| 360 | } else if (typeof encoding === 'function') { | |
| 361 | cb = encoding | |
| 362 | encoding = 'utf8' | |
| 363 | } | |
| 364 | ||
| 365 | if (typeof pos === 'string') { | |
| 366 | encoding = pos | |
| 367 | pos = -1 | |
| 368 | } | |
| 369 | ||
| 370 | data = Buffer.from(data, encoding) | |
| 371 | offset = 0 | |
| 372 | len = data.byteLength | |
| 373 | } else if (typeof offset === 'function') { | |
| 374 | cb = offset | |
| 375 | offset = 0 | |
| 376 | len = data.byteLength | |
| 377 | pos = -1 | |
| 378 | } else if (typeof len === 'function') { | |
| 379 | cb = len | |
| 380 | len = data.byteLength - offset | |
| 381 | pos = -1 | |
| 382 | } else if (typeof pos === 'function') { | |
| 383 | cb = pos | |
| 384 | pos = -1 | |
| 385 | } | |
| 386 | ||
| 387 | if (typeof offset !== 'number') offset = 0 | |
| 388 | if (typeof len !== 'number') len = data.byteLength - offset | |
| 389 | if (typeof pos !== 'number') pos = -1 | |
| 390 | ||
| 391 | const req = FileRequest.borrow() | |
| 392 | ||
| 393 | let bytes | |
| 394 | let err = null | |
| 395 | try { | |
| 396 | binding.write(req.handle, fd, data, offset, len, pos) | |
| 397 | ||
| 398 | bytes = await req | |
| 399 | } catch (e) { | |
| 400 | err = new FileError(e.message, { operation: 'write', code: e.code, fd }) | |
| 401 | } finally { | |
| 402 | req.return() | |
| 403 | } | |
| 404 | ||
| 405 | return done(err, bytes, cb) | |
| 406 | } | |
| 407 | ||
| 408 | function writeSync(fd, data, offset, len, pos = -1) { | |
| 409 | if (typeof data === 'string') { | |
| 410 | let encoding = len | |
| 411 | pos = offset | |
| 412 | ||
| 413 | if (typeof pos === 'string') { | |
| 414 | encoding = pos | |
| 415 | pos = -1 | |
| 416 | } | |
| 417 | ||
| 418 | data = Buffer.from(data, encoding) | |
| 419 | offset = 0 | |
| 420 | len = data.byteLength | |
| 421 | } | |
| 422 | ||
| 423 | if (typeof offset !== 'number') offset = 0 | |
| 424 | if (typeof len !== 'number') len = data.byteLength - offset | |
| 425 | if (typeof pos !== 'number') pos = -1 | |
| 426 | ||
| 427 | const req = FileRequest.borrow() | |
| 428 | ||
| 429 | try { | |
| 430 | return binding.writeSync(req.handle, fd, data, offset, len, pos) | |
| 431 | } catch (e) { | |
| 432 | throw new FileError(e.message, { operation: 'write', code: e.code, fd }) | |
| 433 | } finally { | |
| 434 | req.return() | |
| 435 | } | |
| 436 | } | |
| 437 | ||
| 438 | async function writev(fd, buffers, pos = -1, cb) { | |
| 439 | if (typeof pos === 'function') { | |
| 440 | cb = pos | |
| 441 | pos = -1 | |
| 442 | } | |
| 443 | ||
| 444 | if (typeof pos !== 'number') pos = -1 | |
| 445 | ||
| 446 | const req = FileRequest.borrow() | |
| 447 | ||
| 448 | let bytes | |
| 449 | let err = null | |
| 450 | try { | |
| 451 | binding.writev(req.handle, fd, buffers, pos) | |
| 452 | ||
| 453 | bytes = await req | |
| 454 | } catch (e) { | |
| 455 | err = new FileError(e.message, { operation: 'writev', code: e.code, fd }) | |
| 456 | } finally { | |
| 457 | req.return() | |
| 458 | } | |
| 459 | ||
| 460 | return done(err, bytes, cb) | |
| 461 | } | |
| 462 | ||
| 463 | function writevSync(fd, buffers, pos = -1) { | |
| 464 | if (typeof pos !== 'number') pos = -1 | |
| 465 | ||
| 466 | const req = FileRequest.borrow() | |
| 467 | ||
| 468 | try { | |
| 469 | return binding.writevSync(req.handle, fd, buffers, pos) | |
| 470 | } catch (e) { | |
| 471 | throw new FileError(e.message, { operation: 'writev', code: e.code, fd }) | |
| 472 | } finally { | |
| 473 | req.return() | |
| 474 | } | |
| 475 | } | |
| 476 | ||
| 477 | async function stat(filepath, cb) { | |
| 478 | filepath = toNamespacedPath(filepath) | |
| 479 | ||
| 480 | const req = FileRequest.borrow() | |
| 481 | ||
| 482 | let st | |
| 483 | let err = null | |
| 484 | try { | |
| 485 | binding.stat(req.handle, filepath) | |
| 486 | ||
| 487 | await req | |
| 488 | ||
| 489 | st = new Stats(...binding.requestResultStat(req.handle)) | |
| 490 | } catch (e) { | |
| 491 | err = new FileError(e.message, { | |
| 492 | operation: 'stat', | |
| 493 | code: e.code, | |
| 494 | path: filepath | |
| 495 | }) | |
| 496 | } finally { | |
| 497 | req.return() | |
| 498 | } | |
| 499 | ||
| 500 | return done(err, st, cb) | |
| 501 | } | |
| 502 | ||
| 503 | function statSync(filepath) { | |
| 504 | filepath = toNamespacedPath(filepath) | |
| 505 | ||
| 506 | const req = FileRequest.borrow() | |
| 507 | ||
| 508 | try { | |
| 509 | binding.statSync(req.handle, filepath) | |
| 510 | ||
| 511 | return new Stats(...binding.requestResultStat(req.handle)) | |
| 512 | } catch (e) { | |
| 513 | throw new FileError(e.message, { | |
| 514 | operation: 'stat', | |
| 515 | code: e.code, | |
| 516 | path: filepath | |
| 517 | }) | |
| 518 | } finally { | |
| 519 | req.return() | |
| 520 | } | |
| 521 | } | |
| 522 | ||
| 523 | async function lstat(filepath, cb) { | |
| 524 | filepath = toNamespacedPath(filepath) | |
| 525 | ||
| 526 | const req = FileRequest.borrow() | |
| 527 | ||
| 528 | let st | |
| 529 | let err = null | |
| 530 | try { | |
| 531 | binding.lstat(req.handle, filepath) | |
| 532 | ||
| 533 | await req | |
| 534 | ||
| 535 | st = new Stats(...binding.requestResultStat(req.handle)) | |
| 536 | } catch (e) { | |
| 537 | err = new FileError(e.message, { | |
| 538 | operation: 'lstat', | |
| 539 | code: e.code, | |
| 540 | path: filepath | |
| 541 | }) | |
| 542 | } finally { | |
| 543 | req.return() | |
| 544 | } | |
| 545 | ||
| 546 | return done(err, st, cb) | |
| 547 | } | |
| 548 | ||
| 549 | function lstatSync(filepath) { | |
| 550 | filepath = toNamespacedPath(filepath) | |
| 551 | ||
| 552 | const req = FileRequest.borrow() | |
| 553 | ||
| 554 | try { | |
| 555 | binding.lstatSync(req.handle, filepath) | |
| 556 | ||
| 557 | return new Stats(...binding.requestResultStat(req.handle)) | |
| 558 | } catch (e) { | |
| 559 | throw new FileError(e.message, { | |
| 560 | operation: 'lstat', | |
| 561 | code: e.code, | |
| 562 | path: filepath | |
| 563 | }) | |
| 564 | } finally { | |
| 565 | req.return() | |
| 566 | } | |
| 567 | } | |
| 568 | ||
| 569 | async function fstat(fd, cb) { | |
| 570 | const req = FileRequest.borrow() | |
| 571 | ||
| 572 | let st | |
| 573 | let err = null | |
| 574 | try { | |
| 575 | binding.fstat(req.handle, fd) | |
| 576 | ||
| 577 | await req | |
| 578 | ||
| 579 | st = new Stats(...binding.requestResultStat(req.handle)) | |
| 580 | } catch (e) { | |
| 581 | err = new FileError(e.message, { operation: 'fstat', code: e.code, fd }) | |
| 582 | } finally { | |
| 583 | req.return() | |
| 584 | } | |
| 585 | ||
| 586 | return done(err, st, cb) | |
| 587 | } | |
| 588 | ||
| 589 | function fstatSync(fd) { | |
| 590 | const req = FileRequest.borrow() | |
| 591 | ||
| 592 | try { | |
| 593 | binding.fstatSync(req.handle, fd) | |
| 594 | ||
| 595 | return new Stats(...binding.requestResultStat(req.handle)) | |
| 596 | } catch (e) { | |
| 597 | throw new FileError(e.message, { operation: 'fstat', code: e.code, fd }) | |
| 598 | } finally { | |
| 599 | req.return() | |
| 600 | } | |
| 601 | } | |
| 602 | ||
| 603 | async function ftruncate(fd, len = 0, cb) { | |
| 604 | if (typeof len === 'function') { | |
| 605 | cb = len | |
| 606 | len = 0 | |
| 607 | } | |
| 608 | ||
| 609 | if (typeof len !== 'number') len = 0 | |
| 610 | ||
| 611 | const req = FileRequest.borrow() | |
| 612 | ||
| 613 | let err = null | |
| 614 | try { | |
| 615 | binding.ftruncate(req.handle, fd, len) | |
| 616 | ||
| 617 | await req | |
| 618 | } catch (e) { | |
| 619 | err = new FileError(e.message, { operation: 'ftruncate', code: e.code, fd }) | |
| 620 | } finally { | |
| 621 | req.return() | |
| 622 | } | |
| 623 | ||
| 624 | return done(err, cb) | |
| 625 | } | |
| 626 | ||
| 627 | function ftruncateSync(fd, len = 0) { | |
| 628 | if (typeof len !== 'number') len = 0 | |
| 629 | ||
| 630 | const req = FileRequest.borrow() | |
| 631 | ||
| 632 | try { | |
| 633 | binding.ftruncateSync(req.handle, fd, len) | |
| 634 | } catch (e) { | |
| 635 | throw new FileError(e.message, { operation: 'ftruncate', code: e.code, fd }) | |
| 636 | } finally { | |
| 637 | req.return() | |
| 638 | } | |
| 639 | } | |
| 640 | ||
| 641 | async function chmod(filepath, mode, cb) { | |
| 642 | if (typeof mode === 'string') mode = toMode(mode) | |
| 643 | ||
| 644 | filepath = toNamespacedPath(filepath) | |
| 645 | ||
| 646 | const req = FileRequest.borrow() | |
| 647 | ||
| 648 | let err = null | |
| 649 | try { | |
| 650 | binding.chmod(req.handle, filepath, mode) | |
| 651 | ||
| 652 | await req | |
| 653 | } catch (e) { | |
| 654 | err = new FileError(e.message, { | |
| 655 | operation: 'chmod', | |
| 656 | code: e.code, | |
| 657 | path: filepath | |
| 658 | }) | |
| 659 | } finally { | |
| 660 | req.return() | |
| 661 | } | |
| 662 | ||
| 663 | return done(err, cb) | |
| 664 | } | |
| 665 | ||
| 666 | function chmodSync(filepath, mode) { | |
| 667 | if (typeof mode === 'string') mode = toMode(mode) | |
| 668 | ||
| 669 | filepath = toNamespacedPath(filepath) | |
| 670 | ||
| 671 | const req = FileRequest.borrow() | |
| 672 | ||
| 673 | try { | |
| 674 | binding.chmodSync(req.handle, filepath, mode) | |
| 675 | } catch (e) { | |
| 676 | throw new FileError(e.message, { | |
| 677 | operation: 'chmod', | |
| 678 | code: e.code, | |
| 679 | path: filepath | |
| 680 | }) | |
| 681 | } finally { | |
| 682 | req.return() | |
| 683 | } | |
| 684 | } | |
| 685 | ||
| 686 | async function fchmod(fd, mode, cb) { | |
| 687 | if (typeof mode === 'string') mode = toMode(mode) | |
| 688 | ||
| 689 | const req = FileRequest.borrow() | |
| 690 | ||
| 691 | let err = null | |
| 692 | try { | |
| 693 | binding.fchmod(req.handle, fd, mode) | |
| 694 | ||
| 695 | await req | |
| 696 | } catch (e) { | |
| 697 | err = new FileError(e.message, { operation: 'fchmod', code: e.code, fd }) | |
| 698 | } finally { | |
| 699 | req.return() | |
| 700 | } | |
| 701 | ||
| 702 | return done(err, cb) | |
| 703 | } | |
| 704 | ||
| 705 | function fchmodSync(fd, mode) { | |
| 706 | if (typeof mode === 'string') mode = toMode(mode) | |
| 707 | ||
| 708 | const req = FileRequest.borrow() | |
| 709 | ||
| 710 | try { | |
| 711 | binding.fchmodSync(req.handle, fd, mode) | |
| 712 | } catch (e) { | |
| 713 | throw new FileError(e.message, { operation: 'fchmod', code: e.code, fd }) | |
| 714 | } finally { | |
| 715 | req.return() | |
| 716 | } | |
| 717 | } | |
| 718 | ||
| 719 | async function utimes(filepath, atime, mtime, cb) { | |
| 720 | if (typeof atime !== 'number') atime = atime.getTime() / 1000 | |
| 721 | if (typeof mtime !== 'number') mtime = mtime.getTime() / 1000 | |
| 722 | ||
| 723 | filepath = toNamespacedPath(filepath) | |
| 724 | ||
| 725 | const req = FileRequest.borrow() | |
| 726 | ||
| 727 | let err = null | |
| 728 | try { | |
| 729 | binding.utimes(req.handle, filepath, atime, mtime) | |
| 730 | ||
| 731 | await req | |
| 732 | } catch (e) { | |
| 733 | err = new FileError(e.message, { | |
| 734 | operation: 'utimes', | |
| 735 | code: e.code, | |
| 736 | path: filepath | |
| 737 | }) | |
| 738 | } finally { | |
| 739 | req.return() | |
| 740 | } | |
| 741 | ||
| 742 | return done(err, cb) | |
| 743 | } | |
| 744 | ||
| 745 | function utimesSync(filepath, atime, mtime) { | |
| 746 | if (typeof atime !== 'number') atime = atime.getTime() / 1000 | |
| 747 | if (typeof mtime !== 'number') mtime = mtime.getTime() / 1000 | |
| 748 | ||
| 749 | filepath = toNamespacedPath(filepath) | |
| 750 | ||
| 751 | const req = FileRequest.borrow() | |
| 752 | ||
| 753 | try { | |
| 754 | binding.utimesSync(req.handle, filepath, atime, mtime) | |
| 755 | } catch (e) { | |
| 756 | throw new FileError(e.message, { | |
| 757 | operation: 'utimes', | |
| 758 | code: e.code, | |
| 759 | path: filepath | |
| 760 | }) | |
| 761 | } finally { | |
| 762 | req.return() | |
| 763 | } | |
| 764 | } | |
| 765 | ||
| 766 | async function mkdir(filepath, opts, cb) { | |
| 767 | if (typeof opts === 'function') { | |
| 768 | cb = opts | |
| 769 | opts = { mode: 0o777 } | |
| 770 | } | |
| 771 | ||
| 772 | if (typeof opts === 'number') opts = { mode: opts } | |
| 773 | else if (!opts) opts = {} | |
| 774 | ||
| 775 | const mode = typeof opts.mode === 'number' ? opts.mode : 0o777 | |
| 776 | ||
| 777 | filepath = toNamespacedPath(filepath) | |
| 778 | ||
| 779 | if (opts.recursive) { | |
| 780 | let err = null | |
| 781 | try { | |
| 782 | try { | |
| 783 | await mkdir(filepath, { mode }) | |
| 784 | } catch (err) { | |
| 785 | if (err.code !== 'ENOENT') { | |
| 786 | if (!(await stat(filepath)).isDirectory()) throw err | |
| 787 | } else { | |
| 788 | while (filepath.endsWith(path.sep)) filepath = filepath.slice(0, -1) | |
| 789 | const i = filepath.lastIndexOf(path.sep) | |
| 790 | if (i <= 0) throw err | |
| 791 | ||
| 792 | await mkdir(filepath.slice(0, i), { mode, recursive: true }) | |
| 793 | ||
| 794 | try { | |
| 795 | await mkdir(filepath, { mode }) | |
| 796 | } catch (err) { | |
| 797 | if (!(await stat(filepath)).isDirectory()) throw err | |
| 798 | } | |
| 799 | } | |
| 800 | } | |
| 801 | } catch (e) { | |
| 802 | err = e | |
| 803 | } | |
| 804 | ||
| 805 | return done(err, cb) | |
| 806 | } | |
| 807 | ||
| 808 | const req = FileRequest.borrow() | |
| 809 | ||
| 810 | let err = null | |
| 811 | try { | |
| 812 | binding.mkdir(req.handle, filepath, mode) | |
| 813 | ||
| 814 | await req | |
| 815 | } catch (e) { | |
| 816 | err = new FileError(e.message, { | |
| 817 | operation: 'mkdir', | |
| 818 | code: e.code, | |
| 819 | path: filepath | |
| 820 | }) | |
| 821 | } finally { | |
| 822 | req.return() | |
| 823 | } | |
| 824 | ||
| 825 | return done(err, cb) | |
| 826 | } | |
| 827 | ||
| 828 | function mkdirSync(filepath, opts) { | |
| 829 | if (typeof opts === 'number') opts = { mode: opts } | |
| 830 | else if (!opts) opts = {} | |
| 831 | ||
| 832 | const mode = typeof opts.mode === 'number' ? opts.mode : 0o777 | |
| 833 | ||
| 834 | filepath = toNamespacedPath(filepath) | |
| 835 | ||
| 836 | if (opts.recursive) { | |
| 837 | try { | |
| 838 | mkdirSync(filepath, { mode }) | |
| 839 | } catch (err) { | |
| 840 | if (err.code !== 'ENOENT') { | |
| 841 | if (!statSync(filepath).isDirectory()) throw err | |
| 842 | } else { | |
| 843 | while (filepath.endsWith(path.sep)) filepath = filepath.slice(0, -1) | |
| 844 | const i = filepath.lastIndexOf(path.sep) | |
| 845 | if (i <= 0) throw err | |
| 846 | ||
| 847 | mkdirSync(filepath.slice(0, i), { mode, recursive: true }) | |
| 848 | ||
| 849 | try { | |
| 850 | mkdirSync(filepath, { mode }) | |
| 851 | } catch (err) { | |
| 852 | if (!statSync(filepath).isDirectory()) throw err | |
| 853 | } | |
| 854 | } | |
| 855 | } | |
| 856 | ||
| 857 | return | |
| 858 | } | |
| 859 | ||
| 860 | const req = FileRequest.borrow() | |
| 861 | ||
| 862 | try { | |
| 863 | binding.mkdirSync(req.handle, filepath, mode) | |
| 864 | } catch (e) { | |
| 865 | throw new FileError(e.message, { | |
| 866 | operation: 'mkdir', | |
| 867 | code: e.code, | |
| 868 | path: filepath | |
| 869 | }) | |
| 870 | } finally { | |
| 871 | req.return() | |
| 872 | } | |
| 873 | } | |
| 874 | ||
| 875 | async function rmdir(filepath, cb) { | |
| 876 | filepath = toNamespacedPath(filepath) | |
| 877 | ||
| 878 | const req = FileRequest.borrow() | |
| 879 | ||
| 880 | let err = null | |
| 881 | try { | |
| 882 | binding.rmdir(req.handle, filepath) | |
| 883 | ||
| 884 | await req | |
| 885 | } catch (e) { | |
| 886 | err = new FileError(e.message, { | |
| 887 | operation: 'rmdir', | |
| 888 | code: e.code, | |
| 889 | path: filepath | |
| 890 | }) | |
| 891 | } finally { | |
| 892 | req.return() | |
| 893 | } | |
| 894 | ||
| 895 | return done(err, cb) | |
| 896 | } | |
| 897 | ||
| 898 | function rmdirSync(filepath) { | |
| 899 | filepath = toNamespacedPath(filepath) | |
| 900 | ||
| 901 | const req = FileRequest.borrow() | |
| 902 | ||
| 903 | try { | |
| 904 | binding.rmdirSync(req.handle, filepath) | |
| 905 | } catch (e) { | |
| 906 | throw new FileError(e.message, { | |
| 907 | operation: 'rmdir', | |
| 908 | code: e.code, | |
| 909 | path: filepath | |
| 910 | }) | |
| 911 | } finally { | |
| 912 | req.return() | |
| 913 | } | |
| 914 | } | |
| 915 | ||
| 916 | async function rm(filepath, opts, cb) { | |
| 917 | if (typeof opts === 'function') { | |
| 918 | cb = opts | |
| 919 | opts = {} | |
| 920 | } | |
| 921 | ||
| 922 | if (!opts) opts = {} | |
| 923 | ||
| 924 | filepath = toNamespacedPath(filepath) | |
| 925 | ||
| 926 | let err = null | |
| 927 | try { | |
| 928 | const st = await lstat(filepath) | |
| 929 | ||
| 930 | if (st.isDirectory()) { | |
| 931 | if (opts.recursive) { | |
| 932 | try { | |
| 933 | await rmdir(filepath) | |
| 934 | } catch (err) { | |
| 935 | if (err.code !== 'ENOTEMPTY') throw err | |
| 936 | ||
| 937 | const files = await readdir(filepath) | |
| 938 | ||
| 939 | for (const file of files) { | |
| 940 | await rm(filepath + path.sep + file, opts) | |
| 941 | } | |
| 942 | ||
| 943 | await rmdir(filepath) | |
| 944 | } | |
| 945 | } else { | |
| 946 | throw new FileError('is a directory', { | |
| 947 | operation: 'rm', | |
| 948 | code: 'EISDIR', | |
| 949 | path: filepath | |
| 950 | }) | |
| 951 | } | |
| 952 | } else { | |
| 953 | await unlink(filepath) | |
| 954 | } | |
| 955 | } catch (e) { | |
| 956 | if (e.code !== 'ENOENT' || !opts.force) err = e | |
| 957 | } | |
| 958 | ||
| 959 | return done(err, cb) | |
| 960 | } | |
| 961 | ||
| 962 | function rmSync(filepath, opts) { | |
| 963 | if (!opts) opts = {} | |
| 964 | ||
| 965 | filepath = toNamespacedPath(filepath) | |
| 966 | ||
| 967 | try { | |
| 968 | const st = lstatSync(filepath) | |
| 969 | ||
| 970 | if (st.isDirectory()) { | |
| 971 | if (opts.recursive) { | |
| 972 | try { | |
| 973 | rmdirSync(filepath) | |
| 974 | } catch (err) { | |
| 975 | if (err.code !== 'ENOTEMPTY') throw err | |
| 976 | ||
| 977 | const files = readdirSync(filepath) | |
| 978 | ||
| 979 | for (const file of files) { | |
| 980 | rmSync(filepath + path.sep + file, opts) | |
| 981 | } | |
| 982 | ||
| 983 | rmdirSync(filepath) | |
| 984 | } | |
| 985 | } else { | |
| 986 | throw new FileError('is a directory', { | |
| 987 | operation: 'rm', | |
| 988 | code: 'EISDIR', | |
| 989 | path: filepath | |
| 990 | }) | |
| 991 | } | |
| 992 | } else { | |
| 993 | unlinkSync(filepath) | |
| 994 | } | |
| 995 | } catch (err) { | |
| 996 | if (err.code !== 'ENOENT' || !opts.force) throw err | |
| 997 | } | |
| 998 | } | |
| 999 | ||
| 1000 | async function unlink(filepath, cb) { | |
| 1001 | filepath = toNamespacedPath(filepath) | |
| 1002 | ||
| 1003 | const req = FileRequest.borrow() | |
| 1004 | ||
| 1005 | let err = null | |
| 1006 | try { | |
| 1007 | binding.unlink(req.handle, filepath) | |
| 1008 | ||
| 1009 | await req | |
| 1010 | } catch (e) { | |
| 1011 | err = new FileError(e.message, { | |
| 1012 | operation: 'unlink', | |
| 1013 | code: e.code, | |
| 1014 | path: filepath | |
| 1015 | }) | |
| 1016 | } finally { | |
| 1017 | req.return() | |
| 1018 | } | |
| 1019 | ||
| 1020 | return done(err, cb) | |
| 1021 | } | |
| 1022 | ||
| 1023 | function unlinkSync(filepath) { | |
| 1024 | filepath = toNamespacedPath(filepath) | |
| 1025 | ||
| 1026 | const req = FileRequest.borrow() | |
| 1027 | ||
| 1028 | try { | |
| 1029 | binding.unlinkSync(req.handle, filepath) | |
| 1030 | } catch (e) { | |
| 1031 | throw new FileError(e.message, { | |
| 1032 | operation: 'unlink', | |
| 1033 | code: e.code, | |
| 1034 | path: filepath | |
| 1035 | }) | |
| 1036 | } finally { | |
| 1037 | req.return() | |
| 1038 | } | |
| 1039 | } | |
| 1040 | ||
| 1041 | async function rename(src, dst, cb) { | |
| 1042 | src = toNamespacedPath(src) | |
| 1043 | dst = toNamespacedPath(dst) | |
| 1044 | ||
| 1045 | const req = FileRequest.borrow() | |
| 1046 | ||
| 1047 | let err = null | |
| 1048 | try { | |
| 1049 | binding.rename(req.handle, src, dst) | |
| 1050 | ||
| 1051 | await req | |
| 1052 | } catch (e) { | |
| 1053 | err = new FileError(e.message, { | |
| 1054 | operation: 'rename', | |
| 1055 | code: e.code, | |
| 1056 | path: src, | |
| 1057 | destination: dst | |
| 1058 | }) | |
| 1059 | } finally { | |
| 1060 | req.return() | |
| 1061 | } | |
| 1062 | ||
| 1063 | return done(err, cb) | |
| 1064 | } | |
| 1065 | ||
| 1066 | function renameSync(src, dst) { | |
| 1067 | src = toNamespacedPath(src) | |
| 1068 | dst = toNamespacedPath(dst) | |
| 1069 | ||
| 1070 | const req = FileRequest.borrow() | |
| 1071 | ||
| 1072 | try { | |
| 1073 | binding.renameSync(req.handle, src, dst) | |
| 1074 | } catch (e) { | |
| 1075 | throw new FileError(e.message, { | |
| 1076 | operation: 'rename', | |
| 1077 | code: e.code, | |
| 1078 | path: src, | |
| 1079 | destination: dst | |
| 1080 | }) | |
| 1081 | } finally { | |
| 1082 | req.return() | |
| 1083 | } | |
| 1084 | } | |
| 1085 | ||
| 1086 | async function copyFile(src, dst, mode = 0, cb) { | |
| 1087 | if (typeof mode === 'function') { | |
| 1088 | cb = mode | |
| 1089 | mode = 0 | |
| 1090 | } | |
| 1091 | ||
| 1092 | src = toNamespacedPath(src) | |
| 1093 | dst = toNamespacedPath(dst) | |
| 1094 | ||
| 1095 | const req = FileRequest.borrow() | |
| 1096 | ||
| 1097 | let err = null | |
| 1098 | try { | |
| 1099 | binding.copyfile(req.handle, src, dst, mode) | |
| 1100 | ||
| 1101 | await req | |
| 1102 | } catch (e) { | |
| 1103 | err = new FileError(e.message, { | |
| 1104 | operation: 'copyfile', | |
| 1105 | code: e.code, | |
| 1106 | path: src, | |
| 1107 | destination: dst | |
| 1108 | }) | |
| 1109 | } finally { | |
| 1110 | req.return() | |
| 1111 | } | |
| 1112 | ||
| 1113 | return done(err, cb) | |
| 1114 | } | |
| 1115 | ||
| 1116 | function copyFileSync(src, dst, mode = 0) { | |
| 1117 | src = toNamespacedPath(src) | |
| 1118 | dst = toNamespacedPath(dst) | |
| 1119 | ||
| 1120 | const req = FileRequest.borrow() | |
| 1121 | ||
| 1122 | try { | |
| 1123 | binding.copyfileSync(req.handle, src, dst, mode) | |
| 1124 | } catch (e) { | |
| 1125 | throw new FileError(e.message, { | |
| 1126 | operation: 'copyfile', | |
| 1127 | code: e.code, | |
| 1128 | path: src, | |
| 1129 | destination: dst | |
| 1130 | }) | |
| 1131 | } finally { | |
| 1132 | req.return() | |
| 1133 | } | |
| 1134 | } | |
| 1135 | ||
| 1136 | async function cp(src, dst, opts, cb) { | |
| 1137 | if (typeof opts === 'function') { | |
| 1138 | cb = opts | |
| 1139 | opts = {} | |
| 1140 | } | |
| 1141 | ||
| 1142 | if (!opts) opts = {} | |
| 1143 | ||
| 1144 | src = toNamespacedPath(src) | |
| 1145 | dst = toNamespacedPath(dst) | |
| 1146 | ||
| 1147 | let err = null | |
| 1148 | try { | |
| 1149 | const st = await lstat(src) | |
| 1150 | ||
| 1151 | if (st.isDirectory()) { | |
| 1152 | if (opts.recursive !== true) { | |
| 1153 | throw new FileError('is a directory', { | |
| 1154 | operation: 'cp', | |
| 1155 | code: 'EISDIR', | |
| 1156 | path: src | |
| 1157 | }) | |
| 1158 | } | |
| 1159 | ||
| 1160 | try { | |
| 1161 | await lstat(dst) | |
| 1162 | } catch (e) { | |
| 1163 | if (e.code === 'ENOENT') { | |
| 1164 | await mkdir(dst, { mode: st.mode, recursive: true }) | |
| 1165 | } else { | |
| 1166 | throw e | |
| 1167 | } | |
| 1168 | } | |
| 1169 | ||
| 1170 | const dir = await opendir(src) | |
| 1171 | for await (const { name } of dir) { | |
| 1172 | await cp(path.join(src, name), path.join(dst, name), opts) | |
| 1173 | } | |
| 1174 | } else if (st.isFile()) { | |
| 1175 | await copyFile(src, dst) | |
| 1176 | await chmod(dst, st.mode) | |
| 1177 | } | |
| 1178 | } catch (e) { | |
| 1179 | err = e | |
| 1180 | } | |
| 1181 | ||
| 1182 | return done(err, cb) | |
| 1183 | } | |
| 1184 | ||
| 1185 | async function realpath(filepath, opts, cb) { | |
| 1186 | if (typeof opts === 'function') { | |
| 1187 | cb = opts | |
| 1188 | opts = {} | |
| 1189 | } | |
| 1190 | ||
| 1191 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1192 | else if (!opts) opts = {} | |
| 1193 | ||
| 1194 | const { encoding = 'utf8' } = opts | |
| 1195 | ||
| 1196 | filepath = toNamespacedPath(filepath) | |
| 1197 | ||
| 1198 | const req = FileRequest.borrow() | |
| 1199 | ||
| 1200 | let res | |
| 1201 | let err = null | |
| 1202 | try { | |
| 1203 | binding.realpath(req.handle, filepath) | |
| 1204 | ||
| 1205 | await req | |
| 1206 | ||
| 1207 | res = Buffer.from(binding.requestResultString(req.handle)) | |
| 1208 | ||
| 1209 | if (encoding !== 'buffer') res = res.toString(encoding) | |
| 1210 | } catch (e) { | |
| 1211 | err = new FileError(e.message, { | |
| 1212 | operation: 'realpath', | |
| 1213 | code: e.code, | |
| 1214 | path: filepath | |
| 1215 | }) | |
| 1216 | } finally { | |
| 1217 | req.return() | |
| 1218 | } | |
| 1219 | ||
| 1220 | return done(err, res, cb) | |
| 1221 | } | |
| 1222 | ||
| 1223 | function realpathSync(filepath, opts) { | |
| 1224 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1225 | else if (!opts) opts = {} | |
| 1226 | ||
| 1227 | const { encoding = 'utf8' } = opts | |
| 1228 | ||
| 1229 | filepath = toNamespacedPath(filepath) | |
| 1230 | ||
| 1231 | const req = FileRequest.borrow() | |
| 1232 | ||
| 1233 | try { | |
| 1234 | binding.realpathSync(req.handle, filepath) | |
| 1235 | ||
| 1236 | let res = Buffer.from(binding.requestResultString(req.handle)) | |
| 1237 | ||
| 1238 | if (encoding !== 'buffer') res = res.toString(encoding) | |
| 1239 | ||
| 1240 | return res | |
| 1241 | } catch (e) { | |
| 1242 | throw new FileError(e.message, { | |
| 1243 | operation: 'realpath', | |
| 1244 | code: e.code, | |
| 1245 | path: filepath | |
| 1246 | }) | |
| 1247 | } finally { | |
| 1248 | req.return() | |
| 1249 | } | |
| 1250 | } | |
| 1251 | ||
| 1252 | async function readlink(filepath, opts, cb) { | |
| 1253 | if (typeof opts === 'function') { | |
| 1254 | cb = opts | |
| 1255 | opts = {} | |
| 1256 | } | |
| 1257 | ||
| 1258 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1259 | else if (!opts) opts = {} | |
| 1260 | ||
| 1261 | const { encoding = 'utf8' } = opts | |
| 1262 | ||
| 1263 | filepath = toNamespacedPath(filepath) | |
| 1264 | ||
| 1265 | const req = FileRequest.borrow() | |
| 1266 | ||
| 1267 | let res | |
| 1268 | let err = null | |
| 1269 | try { | |
| 1270 | binding.readlink(req.handle, filepath) | |
| 1271 | ||
| 1272 | await req | |
| 1273 | ||
| 1274 | res = Buffer.from(binding.requestResultString(req.handle)) | |
| 1275 | ||
| 1276 | if (encoding !== 'buffer') res = res.toString(encoding) | |
| 1277 | } catch (e) { | |
| 1278 | err = new FileError(e.message, { | |
| 1279 | operation: 'readlink', | |
| 1280 | code: e.code, | |
| 1281 | path: filepath | |
| 1282 | }) | |
| 1283 | } finally { | |
| 1284 | req.return() | |
| 1285 | } | |
| 1286 | ||
| 1287 | return done(err, res, cb) | |
| 1288 | } | |
| 1289 | ||
| 1290 | function readlinkSync(filepath, opts) { | |
| 1291 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1292 | else if (!opts) opts = {} | |
| 1293 | ||
| 1294 | const { encoding = 'utf8' } = opts | |
| 1295 | ||
| 1296 | filepath = toNamespacedPath(filepath) | |
| 1297 | ||
| 1298 | const req = FileRequest.borrow() | |
| 1299 | ||
| 1300 | try { | |
| 1301 | binding.readlinkSync(req.handle, filepath) | |
| 1302 | ||
| 1303 | let res = Buffer.from(binding.requestResultString(req.handle)) | |
| 1304 | ||
| 1305 | if (encoding !== 'buffer') res = res.toString(encoding) | |
| 1306 | ||
| 1307 | return res | |
| 1308 | } catch (e) { | |
| 1309 | throw new FileError(e.message, { | |
| 1310 | operation: 'readlink', | |
| 1311 | code: e.code, | |
| 1312 | path: filepath | |
| 1313 | }) | |
| 1314 | } finally { | |
| 1315 | req.return() | |
| 1316 | } | |
| 1317 | } | |
| 1318 | ||
| 1319 | function normalizeSymlinkTarget(target, type, filepath) { | |
| 1320 | if (isWindows) { | |
| 1321 | if (type === constants.UV_FS_SYMLINK_JUNCTION) target = path.resolve(filepath, '..', target) | |
| 1322 | ||
| 1323 | if (path.isAbsolute(target)) return path.toNamespacedPath(target) | |
| 1324 | ||
| 1325 | return target.replace(/\//g, path.sep) | |
| 1326 | } | |
| 1327 | ||
| 1328 | return target | |
| 1329 | } | |
| 1330 | ||
| 1331 | async function symlink(target, filepath, type, cb) { | |
| 1332 | if (typeof type === 'function') { | |
| 1333 | cb = type | |
| 1334 | type = null | |
| 1335 | } | |
| 1336 | ||
| 1337 | filepath = toNamespacedPath(filepath) | |
| 1338 | ||
| 1339 | if (typeof type === 'string') { | |
| 1340 | switch (type) { | |
| 1341 | case 'file': | |
| 1342 | default: | |
| 1343 | type = 0 | |
| 1344 | break | |
| 1345 | case 'dir': | |
| 1346 | type = constants.UV_FS_SYMLINK_DIR | |
| 1347 | break | |
| 1348 | case 'junction': | |
| 1349 | type = constants.UV_FS_SYMLINK_JUNCTION | |
| 1350 | break | |
| 1351 | } | |
| 1352 | } else if (typeof type !== 'number') { | |
| 1353 | if (isWindows) { | |
| 1354 | target = path.resolve(filepath, '..', target) | |
| 1355 | ||
| 1356 | try { | |
| 1357 | type = (await stat(target)).isDirectory() | |
| 1358 | ? constants.UV_FS_SYMLINK_DIR | |
| 1359 | : constants.UV_FS_SYMLINK_JUNCTION | |
| 1360 | } catch { | |
| 1361 | type = 0 | |
| 1362 | } | |
| 1363 | } else { | |
| 1364 | type = 0 | |
| 1365 | } | |
| 1366 | } | |
| 1367 | ||
| 1368 | target = normalizeSymlinkTarget(target, type, filepath) | |
| 1369 | ||
| 1370 | const req = FileRequest.borrow() | |
| 1371 | ||
| 1372 | let err = null | |
| 1373 | try { | |
| 1374 | binding.symlink(req.handle, target, filepath, type) | |
| 1375 | ||
| 1376 | await req | |
| 1377 | } catch (e) { | |
| 1378 | err = new FileError(e.message, { | |
| 1379 | operation: 'symlink', | |
| 1380 | code: e.code, | |
| 1381 | path: target, | |
| 1382 | destination: filepath | |
| 1383 | }) | |
| 1384 | } finally { | |
| 1385 | req.return() | |
| 1386 | } | |
| 1387 | ||
| 1388 | return done(err, cb) | |
| 1389 | } | |
| 1390 | ||
| 1391 | function symlinkSync(target, filepath, type) { | |
| 1392 | filepath = toNamespacedPath(filepath) | |
| 1393 | ||
| 1394 | if (typeof type === 'string') { | |
| 1395 | switch (type) { | |
| 1396 | case 'file': | |
| 1397 | default: | |
| 1398 | type = 0 | |
| 1399 | break | |
| 1400 | case 'dir': | |
| 1401 | type = constants.UV_FS_SYMLINK_DIR | |
| 1402 | break | |
| 1403 | case 'junction': | |
| 1404 | type = constants.UV_FS_SYMLINK_JUNCTION | |
| 1405 | break | |
| 1406 | } | |
| 1407 | } else if (typeof type !== 'number') { | |
| 1408 | if (isWindows) { | |
| 1409 | target = path.resolve(filepath, '..', target) | |
| 1410 | ||
| 1411 | try { | |
| 1412 | type = statSync(target).isDirectory() | |
| 1413 | ? constants.UV_FS_SYMLINK_DIR | |
| 1414 | : constants.UV_FS_SYMLINK_JUNCTION | |
| 1415 | } catch { | |
| 1416 | type = 0 | |
| 1417 | } | |
| 1418 | } else { | |
| 1419 | type = 0 | |
| 1420 | } | |
| 1421 | } | |
| 1422 | ||
| 1423 | target = normalizeSymlinkTarget(target, type, filepath) | |
| 1424 | ||
| 1425 | const req = FileRequest.borrow() | |
| 1426 | ||
| 1427 | try { | |
| 1428 | binding.symlinkSync(req.handle, target, filepath, type) | |
| 1429 | } catch (e) { | |
| 1430 | throw new FileError(e.message, { | |
| 1431 | operation: 'symlink', | |
| 1432 | code: e.code, | |
| 1433 | path: target, | |
| 1434 | destination: filepath | |
| 1435 | }) | |
| 1436 | } finally { | |
| 1437 | req.return() | |
| 1438 | } | |
| 1439 | } | |
| 1440 | ||
| 1441 | async function opendir(filepath, opts, cb) { | |
| 1442 | if (typeof opts === 'function') { | |
| 1443 | cb = opts | |
| 1444 | opts = {} | |
| 1445 | } | |
| 1446 | ||
| 1447 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1448 | else if (!opts) opts = {} | |
| 1449 | ||
| 1450 | filepath = toNamespacedPath(filepath) | |
| 1451 | ||
| 1452 | const req = FileRequest.borrow() | |
| 1453 | ||
| 1454 | let dir | |
| 1455 | let err = null | |
| 1456 | try { | |
| 1457 | binding.opendir(req.handle, filepath) | |
| 1458 | ||
| 1459 | await req | |
| 1460 | ||
| 1461 | dir = new Dir(filepath, binding.requestResultDir(req.handle), opts) | |
| 1462 | } catch (e) { | |
| 1463 | err = new FileError(e.message, { | |
| 1464 | operation: 'opendir', | |
| 1465 | code: e.code, | |
| 1466 | path: filepath | |
| 1467 | }) | |
| 1468 | } finally { | |
| 1469 | req.return() | |
| 1470 | } | |
| 1471 | ||
| 1472 | return done(err, dir, cb) | |
| 1473 | } | |
| 1474 | ||
| 1475 | function opendirSync(filepath, opts) { | |
| 1476 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1477 | else if (!opts) opts = {} | |
| 1478 | ||
| 1479 | filepath = toNamespacedPath(filepath) | |
| 1480 | ||
| 1481 | const req = FileRequest.borrow() | |
| 1482 | ||
| 1483 | try { | |
| 1484 | binding.opendirSync(req.handle, filepath) | |
| 1485 | ||
| 1486 | return new Dir(filepath, binding.requestResultDir(req.handle), opts) | |
| 1487 | } catch (e) { | |
| 1488 | throw new FileError(e.message, { | |
| 1489 | operation: 'opendir', | |
| 1490 | code: e.code, | |
| 1491 | path: filepath | |
| 1492 | }) | |
| 1493 | } finally { | |
| 1494 | req.return() | |
| 1495 | } | |
| 1496 | } | |
| 1497 | ||
| 1498 | async function readdir(filepath, opts, cb) { | |
| 1499 | if (typeof opts === 'function') { | |
| 1500 | cb = opts | |
| 1501 | opts = {} | |
| 1502 | } | |
| 1503 | ||
| 1504 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1505 | else if (!opts) opts = {} | |
| 1506 | ||
| 1507 | const { withFileTypes = false } = opts | |
| 1508 | ||
| 1509 | filepath = toNamespacedPath(filepath) | |
| 1510 | ||
| 1511 | let result = [] | |
| 1512 | let err = null | |
| 1513 | try { | |
| 1514 | const dir = await opendir(filepath) | |
| 1515 | ||
| 1516 | for await (const entry of dir) { | |
| 1517 | result.push(withFileTypes ? entry : entry.name) | |
| 1518 | } | |
| 1519 | } catch (e) { | |
| 1520 | result = [] | |
| 1521 | err = e | |
| 1522 | } | |
| 1523 | ||
| 1524 | return done(err, result, cb) | |
| 1525 | } | |
| 1526 | ||
| 1527 | function readdirSync(filepath, opts) { | |
| 1528 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1529 | else if (!opts) opts = {} | |
| 1530 | ||
| 1531 | const { withFileTypes = false } = opts | |
| 1532 | ||
| 1533 | filepath = toNamespacedPath(filepath) | |
| 1534 | ||
| 1535 | const dir = opendirSync(filepath, opts) | |
| 1536 | const result = [] | |
| 1537 | ||
| 1538 | for (const entry of dir) { | |
| 1539 | result.push(withFileTypes ? entry : entry.name) | |
| 1540 | } | |
| 1541 | ||
| 1542 | return result | |
| 1543 | } | |
| 1544 | ||
| 1545 | async function readFile(filepath, opts, cb) { | |
| 1546 | if (typeof opts === 'function') { | |
| 1547 | cb = opts | |
| 1548 | opts = {} | |
| 1549 | } | |
| 1550 | ||
| 1551 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1552 | else if (!opts) opts = {} | |
| 1553 | ||
| 1554 | const { encoding = 'buffer' } = opts | |
| 1555 | ||
| 1556 | let fd = -1 | |
| 1557 | let buffer = null | |
| 1558 | let err = null | |
| 1559 | try { | |
| 1560 | fd = await open(filepath, opts.flag || 'r') | |
| 1561 | ||
| 1562 | const st = await fstat(fd) | |
| 1563 | ||
| 1564 | let len = 0 | |
| 1565 | ||
| 1566 | if (st.size === 0) { | |
| 1567 | const buffers = [] | |
| 1568 | ||
| 1569 | while (true) { | |
| 1570 | buffer = Buffer.allocUnsafe(8192) | |
| 1571 | const r = await read(fd, buffer) | |
| 1572 | len += r | |
| 1573 | if (r === 0) break | |
| 1574 | buffers.push(buffer.subarray(0, r)) | |
| 1575 | } | |
| 1576 | ||
| 1577 | buffer = Buffer.concat(buffers) | |
| 1578 | } else { | |
| 1579 | buffer = Buffer.allocUnsafe(st.size) | |
| 1580 | ||
| 1581 | while (true) { | |
| 1582 | const r = await read(fd, len ? buffer.subarray(len) : buffer) | |
| 1583 | len += r | |
| 1584 | if (r === 0 || len === buffer.byteLength) break | |
| 1585 | } | |
| 1586 | ||
| 1587 | if (len !== buffer.byteLength) buffer = buffer.subarray(0, len) | |
| 1588 | } | |
| 1589 | ||
| 1590 | if (encoding !== 'buffer') buffer = buffer.toString(encoding) | |
| 1591 | } catch (e) { | |
| 1592 | err = e | |
| 1593 | } finally { | |
| 1594 | if (fd !== -1) await close(fd) | |
| 1595 | } | |
| 1596 | ||
| 1597 | return done(err, buffer, cb) | |
| 1598 | } | |
| 1599 | ||
| 1600 | function readFileSync(filepath, opts) { | |
| 1601 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1602 | else if (!opts) opts = {} | |
| 1603 | ||
| 1604 | const { encoding = 'buffer' } = opts | |
| 1605 | ||
| 1606 | let fd = -1 | |
| 1607 | try { | |
| 1608 | fd = openSync(filepath, opts.flag || 'r') | |
| 1609 | ||
| 1610 | const st = fstatSync(fd) | |
| 1611 | ||
| 1612 | let buffer | |
| 1613 | let len = 0 | |
| 1614 | ||
| 1615 | if (st.size === 0) { | |
| 1616 | const buffers = [] | |
| 1617 | ||
| 1618 | while (true) { | |
| 1619 | buffer = Buffer.allocUnsafe(8192) | |
| 1620 | const r = readSync(fd, buffer) | |
| 1621 | len += r | |
| 1622 | if (r === 0) break | |
| 1623 | buffers.push(buffer.subarray(0, r)) | |
| 1624 | } | |
| 1625 | ||
| 1626 | buffer = Buffer.concat(buffers) | |
| 1627 | } else { | |
| 1628 | buffer = Buffer.allocUnsafe(st.size) | |
| 1629 | ||
| 1630 | while (true) { | |
| 1631 | const r = readSync(fd, len ? buffer.subarray(len) : buffer) | |
| 1632 | len += r | |
| 1633 | if (r === 0 || len === buffer.byteLength) break | |
| 1634 | } | |
| 1635 | ||
| 1636 | if (len !== buffer.byteLength) buffer = buffer.subarray(0, len) | |
| 1637 | } | |
| 1638 | ||
| 1639 | if (encoding !== 'buffer') buffer = buffer.toString(encoding) | |
| 1640 | ||
| 1641 | return buffer | |
| 1642 | } finally { | |
| 1643 | if (fd !== -1) closeSync(fd) | |
| 1644 | } | |
| 1645 | } | |
| 1646 | ||
| 1647 | async function writeFile(filepath, data, opts, cb) { | |
| 1648 | if (typeof opts === 'function') { | |
| 1649 | cb = opts | |
| 1650 | opts = {} | |
| 1651 | } | |
| 1652 | ||
| 1653 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1654 | else if (!opts) opts = {} | |
| 1655 | ||
| 1656 | if (typeof data === 'string') data = Buffer.from(data, opts.encoding) | |
| 1657 | ||
| 1658 | let fd = -1 | |
| 1659 | let len = 0 | |
| 1660 | let err = null | |
| 1661 | try { | |
| 1662 | fd = await open(filepath, opts.flag || 'w', opts.mode || 0o666) | |
| 1663 | ||
| 1664 | while (true) { | |
| 1665 | len += await write(fd, len ? data.subarray(len) : data) | |
| 1666 | if (len === data.byteLength) break | |
| 1667 | } | |
| 1668 | } catch (e) { | |
| 1669 | err = e | |
| 1670 | } finally { | |
| 1671 | if (fd !== -1) await close(fd) | |
| 1672 | } | |
| 1673 | ||
| 1674 | return done(err, len, cb) | |
| 1675 | } | |
| 1676 | ||
| 1677 | function writeFileSync(filepath, data, opts) { | |
| 1678 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1679 | else if (!opts) opts = {} | |
| 1680 | ||
| 1681 | if (typeof data === 'string') data = Buffer.from(data, opts.encoding) | |
| 1682 | ||
| 1683 | let fd = -1 | |
| 1684 | try { | |
| 1685 | fd = openSync(filepath, opts.flag || 'w', opts.mode || 0o666) | |
| 1686 | ||
| 1687 | let len = 0 | |
| 1688 | ||
| 1689 | while (true) { | |
| 1690 | len += writeSync(fd, len ? data.subarray(len) : data) | |
| 1691 | if (len === data.byteLength) break | |
| 1692 | } | |
| 1693 | } finally { | |
| 1694 | if (fd !== -1) closeSync(fd) | |
| 1695 | } | |
| 1696 | } | |
| 1697 | ||
| 1698 | function appendFile(filepath, data, opts, cb) { | |
| 1699 | if (typeof opts === 'function') { | |
| 1700 | cb = opts | |
| 1701 | opts = {} | |
| 1702 | } | |
| 1703 | ||
| 1704 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1705 | else if (!opts) opts = {} | |
| 1706 | ||
| 1707 | if (!opts.flag) opts = { ...opts, flag: 'a' } | |
| 1708 | ||
| 1709 | return writeFile(filepath, data, opts, cb) | |
| 1710 | } | |
| 1711 | ||
| 1712 | function appendFileSync(filepath, data, opts) { | |
| 1713 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1714 | else if (!opts) opts = {} | |
| 1715 | ||
| 1716 | if (!opts.flag) opts = { ...opts, flag: 'a' } | |
| 1717 | ||
| 1718 | return writeFileSync(filepath, data, opts) | |
| 1719 | } | |
| 1720 | ||
| 1721 | function watch(filepath, opts, cb) { | |
| 1722 | if (typeof opts === 'function') { | |
| 1723 | cb = opts | |
| 1724 | opts = {} | |
| 1725 | } | |
| 1726 | ||
| 1727 | if (typeof opts === 'string') opts = { encoding: opts } | |
| 1728 | else if (!opts) opts = {} | |
| 1729 | ||
| 1730 | filepath = toNamespacedPath(filepath) | |
| 1731 | ||
| 1732 | return new Watcher(filepath, opts, cb) | |
| 1733 | } | |
| 1734 | ||
| 1735 | class Stats { | |
| 1736 | constructor( | |
| 1737 | dev, | |
| 1738 | mode, | |
| 1739 | nlink, | |
| 1740 | uid, | |
| 1741 | gid, | |
| 1742 | rdev, | |
| 1743 | blksize, | |
| 1744 | ino, | |
| 1745 | size, | |
| 1746 | blocks, | |
| 1747 | atimeMs, | |
| 1748 | mtimeMs, | |
| 1749 | ctimeMs, | |
| 1750 | birthtimeMs | |
| 1751 | ) { | |
| 1752 | this.dev = dev | |
| 1753 | this.mode = mode | |
| 1754 | this.nlink = nlink | |
| 1755 | this.uid = uid | |
| 1756 | this.gid = gid | |
| 1757 | this.rdev = rdev | |
| 1758 | this.blksize = blksize | |
| 1759 | this.ino = ino | |
| 1760 | this.size = size | |
| 1761 | this.blocks = blocks | |
| 1762 | this.atimeMs = atimeMs | |
| 1763 | this.mtimeMs = mtimeMs | |
| 1764 | this.ctimeMs = ctimeMs | |
| 1765 | this.birthtimeMs = birthtimeMs | |
| 1766 | this.atime = new Date(atimeMs) | |
| 1767 | this.mtime = new Date(mtimeMs) | |
| 1768 | this.ctime = new Date(ctimeMs) | |
| 1769 | this.birthtime = new Date(birthtimeMs) | |
| 1770 | } | |
| 1771 | ||
| 1772 | isDirectory() { | |
| 1773 | return (this.mode & constants.S_IFMT) === constants.S_IFDIR | |
| 1774 | } | |
| 1775 | ||
| 1776 | isFile() { | |
| 1777 | return (this.mode & constants.S_IFMT) === constants.S_IFREG | |
| 1778 | } | |
| 1779 | ||
| 1780 | isBlockDevice() { | |
| 1781 | return (this.mode & constants.S_IFMT) === constants.S_IFBLK | |
| 1782 | } | |
| 1783 | ||
| 1784 | isCharacterDevice() { | |
| 1785 | return (this.mode & constants.S_IFMT) === constants.S_IFCHR | |
| 1786 | } | |
| 1787 | ||
| 1788 | isFIFO() { | |
| 1789 | return (this.mode & constants.S_IFMT) === constants.S_IFIFO | |
| 1790 | } | |
| 1791 | ||
| 1792 | isSymbolicLink() { | |
| 1793 | return (this.mode & constants.S_IFMT) === constants.S_IFLNK | |
| 1794 | } | |
| 1795 | ||
| 1796 | isSocket() { | |
| 1797 | return (this.mode & constants.S_IFMT) === constants.S_IFSOCK | |
| 1798 | } | |
| 1799 | } | |
| 1800 | ||
| 1801 | class Dir { | |
| 1802 | constructor(path, handle, opts = {}) { | |
| 1803 | const { encoding = 'utf8', bufferSize = 32 } = opts | |
| 1804 | ||
| 1805 | this.path = path | |
| 1806 | ||
| 1807 | this._encoding = encoding | |
| 1808 | this._capacity = bufferSize | |
| 1809 | this._buffer = new FIFO() | |
| 1810 | this._ended = false | |
| 1811 | this._handle = handle | |
| 1812 | } | |
| 1813 | ||
| 1814 | async read(cb) { | |
| 1815 | if (this._buffer.length) return ok(this._buffer.shift(), cb) | |
| 1816 | if (this._ended) return ok(null, cb) | |
| 1817 | ||
| 1818 | const req = FileRequest.borrow() | |
| 1819 | ||
| 1820 | let entries | |
| 1821 | let err = null | |
| 1822 | try { | |
| 1823 | req.retain(binding.readdir(req.handle, this._handle, this._capacity)) | |
| 1824 | ||
| 1825 | await req | |
| 1826 | ||
| 1827 | entries = binding.requestResultDirents(req.handle) | |
| 1828 | } catch (e) { | |
| 1829 | err = new FileError(e.message, { | |
| 1830 | operation: 'readdir', | |
| 1831 | code: e.code, | |
| 1832 | path: this.path | |
| 1833 | }) | |
| 1834 | } finally { | |
| 1835 | req.return() | |
| 1836 | } | |
| 1837 | ||
| 1838 | if (err) return fail(err, cb) | |
| 1839 | ||
| 1840 | if (entries.length === 0) { | |
| 1841 | this._ended = true | |
| 1842 | ||
| 1843 | return ok(null, cb) | |
| 1844 | } | |
| 1845 | ||
| 1846 | for (const entry of entries) { | |
| 1847 | let name = Buffer.from(entry.name) | |
| 1848 | ||
| 1849 | if (this._encoding !== 'buffer') name = name.toString(this._encoding) | |
| 1850 | ||
| 1851 | this._buffer.push(new Dirent(this.path, name, entry.type)) | |
| 1852 | } | |
| 1853 | ||
| 1854 | return ok(this._buffer.shift(), cb) | |
| 1855 | } | |
| 1856 | ||
| 1857 | readSync() { | |
| 1858 | if (this._buffer.length) return this._buffer.shift() | |
| 1859 | if (this._ended) return null | |
| 1860 | ||
| 1861 | const req = FileRequest.borrow() | |
| 1862 | ||
| 1863 | let entries | |
| 1864 | try { | |
| 1865 | req.retain(binding.readdirSync(req.handle, this._handle, this._capacity)) | |
| 1866 | ||
| 1867 | entries = binding.requestResultDirents(req.handle) | |
| 1868 | } catch (e) { | |
| 1869 | throw new FileError(e.message, { | |
| 1870 | operation: 'readdir', | |
| 1871 | code: e.code, | |
| 1872 | path: this.path | |
| 1873 | }) | |
| 1874 | } finally { | |
| 1875 | req.return() | |
| 1876 | } | |
| 1877 | ||
| 1878 | if (entries.length === 0) { | |
| 1879 | this._ended = true | |
| 1880 | ||
| 1881 | return null | |
| 1882 | } | |
| 1883 | ||
| 1884 | for (const entry of entries) { | |
| 1885 | let name = Buffer.from(entry.name) | |
| 1886 | ||
| 1887 | if (this._encoding !== 'buffer') name = name.toString(this._encoding) | |
| 1888 | ||
| 1889 | this._buffer.push(new Dirent(this.path, name, entry.type)) | |
| 1890 | } | |
| 1891 | ||
| 1892 | return this._buffer.shift() | |
| 1893 | } | |
| 1894 | ||
| 1895 | async close(cb) { | |
| 1896 | const req = FileRequest.borrow() | |
| 1897 | ||
| 1898 | let err = null | |
| 1899 | try { | |
| 1900 | binding.closedir(req.handle, this._handle) | |
| 1901 | ||
| 1902 | await req | |
| 1903 | } catch (e) { | |
| 1904 | err = new FileError(e.message, { | |
| 1905 | operation: 'closedir', | |
| 1906 | code: e.code, | |
| 1907 | path: this.path | |
| 1908 | }) | |
| 1909 | } finally { | |
| 1910 | req.return() | |
| 1911 | } | |
| 1912 | ||
| 1913 | this._handle = null | |
| 1914 | ||
| 1915 | return done(err, cb) | |
| 1916 | } | |
| 1917 | ||
| 1918 | closeSync() { | |
| 1919 | const req = FileRequest.borrow() | |
| 1920 | ||
| 1921 | try { | |
| 1922 | binding.closedirSync(req.handle, this._handle) | |
| 1923 | } catch (e) { | |
| 1924 | throw new FileError(e.message, { | |
| 1925 | operation: 'closedir', | |
| 1926 | code: e.code, | |
| 1927 | path: this.path | |
| 1928 | }) | |
| 1929 | } finally { | |
| 1930 | req.return() | |
| 1931 | } | |
| 1932 | ||
| 1933 | this._handle = null | |
| 1934 | } | |
| 1935 | ||
| 1936 | [Symbol.dispose]() { | |
| 1937 | this.closeSync() | |
| 1938 | } | |
| 1939 | ||
| 1940 | async [Symbol.asyncDispose]() { | |
| 1941 | await this.close() | |
| 1942 | } | |
| 1943 | ||
| 1944 | *[Symbol.iterator]() { | |
| 1945 | while (true) { | |
| 1946 | const entry = this.readSync() | |
| 1947 | if (entry === null) break | |
| 1948 | yield entry | |
| 1949 | } | |
| 1950 | ||
| 1951 | this.closeSync() | |
| 1952 | } | |
| 1953 | ||
| 1954 | async *[Symbol.asyncIterator]() { | |
| 1955 | while (true) { | |
| 1956 | const entry = await this.read() | |
| 1957 | if (entry === null) break | |
| 1958 | yield entry | |
| 1959 | } | |
| 1960 | ||
| 1961 | await this.close() | |
| 1962 | } | |
| 1963 | } | |
| 1964 | ||
| 1965 | class Dirent { | |
| 1966 | constructor(parentPath, name, type) { | |
| 1967 | this.parentPath = parentPath | |
| 1968 | this.name = name | |
| 1969 | this.type = type | |
| 1970 | } | |
| 1971 | ||
| 1972 | isFile() { | |
| 1973 | return this.type === constants.UV_DIRENT_FILE | |
| 1974 | } | |
| 1975 | ||
| 1976 | isDirectory() { | |
| 1977 | return this.type === constants.UV_DIRENT_DIR | |
| 1978 | } | |
| 1979 | ||
| 1980 | isSymbolicLink() { | |
| 1981 | return this.type === constants.UV_DIRENT_LINK | |
| 1982 | } | |
| 1983 | ||
| 1984 | isFIFO() { | |
| 1985 | return this.type === constants.UV_DIRENT_FIFO | |
| 1986 | } | |
| 1987 | ||
| 1988 | isSocket() { | |
| 1989 | return this.type === constants.UV_DIRENT_SOCKET | |
| 1990 | } | |
| 1991 | ||
| 1992 | isCharacterDevice() { | |
| 1993 | return this.type === constants.UV_DIRENT_CHAR | |
| 1994 | } | |
| 1995 | ||
| 1996 | isBlockDevice() { | |
| 1997 | return this.type === constants.UV_DIRENT_BLOCK | |
| 1998 | } | |
| 1999 | } | |
| 2000 | ||
| 2001 | class FileReadStream extends Readable { | |
| 2002 | constructor(path, opts = {}) { | |
| 2003 | const { eagerOpen = true } = opts | |
| 2004 | ||
| 2005 | super({ eagerOpen, ...opts }) | |
| 2006 | ||
| 2007 | this.path = path | |
| 2008 | this.fd = typeof opts.fd === 'number' ? opts.fd : -1 | |
| 2009 | this.flags = opts.flags || 'r' | |
| 2010 | this.mode = opts.mode || 0o666 | |
| 2011 | ||
| 2012 | this._offset = opts.start || 0 | |
| 2013 | this._missing = 0 | |
| 2014 | ||
| 2015 | if (opts.length) { | |
| 2016 | this._missing = opts.length | |
| 2017 | } else if (typeof opts.end === 'number') { | |
| 2018 | this._missing = opts.end - this._offset + 1 | |
| 2019 | } else { | |
| 2020 | this._missing = -1 | |
| 2021 | } | |
| 2022 | } | |
| 2023 | ||
| 2024 | async _open(cb) { | |
| 2025 | let err | |
| 2026 | ||
| 2027 | if (this.fd === -1) { | |
| 2028 | err = null | |
| 2029 | try { | |
| 2030 | this.fd = await open(this.path, this.flags, this.mode) | |
| 2031 | } catch (e) { | |
| 2032 | err = e | |
| 2033 | } | |
| 2034 | ||
| 2035 | if (err) return cb(err) | |
| 2036 | } | |
| 2037 | ||
| 2038 | let st | |
| 2039 | err = null | |
| 2040 | try { | |
| 2041 | st = await fstat(this.fd) | |
| 2042 | } catch (e) { | |
| 2043 | err = e | |
| 2044 | } | |
| 2045 | ||
| 2046 | if (err) return cb(err) | |
| 2047 | ||
| 2048 | if (this._missing === -1) this._missing = st.size | |
| 2049 | ||
| 2050 | if (st.size < this._offset) { | |
| 2051 | this._offset = st.size | |
| 2052 | this._missing = 0 | |
| 2053 | } else if (st.size < this._offset + this._missing) { | |
| 2054 | this._missing = st.size - this._offset | |
| 2055 | } | |
| 2056 | ||
| 2057 | cb(null) | |
| 2058 | } | |
| 2059 | ||
| 2060 | async _read(size) { | |
| 2061 | if (this._missing <= 0) return this.push(null) | |
| 2062 | ||
| 2063 | const data = Buffer.allocUnsafe(Math.min(this._missing, size)) | |
| 2064 | ||
| 2065 | let len | |
| 2066 | let err = null | |
| 2067 | try { | |
| 2068 | len = await read(this.fd, data, 0, data.byteLength, this._offset) | |
| 2069 | } catch (e) { | |
| 2070 | err = e | |
| 2071 | } | |
| 2072 | ||
| 2073 | if (err) return this.destroy(err) | |
| 2074 | ||
| 2075 | if (len === 0) return this.push(null) | |
| 2076 | ||
| 2077 | if (this._missing < len) len = this._missing | |
| 2078 | ||
| 2079 | this._missing -= len | |
| 2080 | this._offset += len | |
| 2081 | ||
| 2082 | this.push(data.subarray(0, len)) | |
| 2083 | } | |
| 2084 | ||
| 2085 | async _destroy(err, cb) { | |
| 2086 | if (this.fd === -1) return cb(err) | |
| 2087 | ||
| 2088 | try { | |
| 2089 | await close(this.fd) | |
| 2090 | } catch (e) { | |
| 2091 | err = err || e | |
| 2092 | } | |
| 2093 | ||
| 2094 | cb(err) | |
| 2095 | } | |
| 2096 | } | |
| 2097 | ||
| 2098 | class FileWriteStream extends Writable { | |
| 2099 | constructor(path, opts = {}) { | |
| 2100 | const { eagerOpen = true } = opts | |
| 2101 | ||
| 2102 | super({ eagerOpen, ...opts }) | |
| 2103 | ||
| 2104 | this.path = path | |
| 2105 | this.fd = typeof opts.fd === 'number' ? opts.fd : -1 | |
| 2106 | this.flags = opts.flags || 'w' | |
| 2107 | this.mode = opts.mode || 0o666 | |
| 2108 | } | |
| 2109 | ||
| 2110 | async _open(cb) { | |
| 2111 | if (this.fd !== -1) return cb(null) | |
| 2112 | ||
| 2113 | let err = null | |
| 2114 | try { | |
| 2115 | this.fd = await open(this.path, this.flags, this.mode) | |
| 2116 | } catch (e) { | |
| 2117 | err = e | |
| 2118 | } | |
| 2119 | ||
| 2120 | cb(err) | |
| 2121 | } | |
| 2122 | ||
| 2123 | async _writev(batch, cb) { | |
| 2124 | let err = null | |
| 2125 | try { | |
| 2126 | await writev( | |
| 2127 | this.fd, | |
| 2128 | batch.map(({ chunk }) => chunk) | |
| 2129 | ) | |
| 2130 | } catch (e) { | |
| 2131 | err = e | |
| 2132 | } | |
| 2133 | ||
| 2134 | cb(err) | |
| 2135 | } | |
| 2136 | ||
| 2137 | async _destroy(err, cb) { | |
| 2138 | if (this.fd === -1) return cb(err) | |
| 2139 | ||
| 2140 | try { | |
| 2141 | await close(this.fd) | |
| 2142 | } catch (e) { | |
| 2143 | err = err || e | |
| 2144 | } | |
| 2145 | ||
| 2146 | cb(err) | |
| 2147 | } | |
| 2148 | } | |
| 2149 | ||
| 2150 | class Watcher extends EventEmitter { | |
| 2151 | constructor(path, opts, onchange) { | |
| 2152 | if (typeof opts === 'function') { | |
| 2153 | onchange = opts | |
| 2154 | opts = {} | |
| 2155 | } | |
| 2156 | ||
| 2157 | if (!opts) opts = {} | |
| 2158 | ||
| 2159 | const { persistent = true, recursive = false, encoding = 'utf8' } = opts | |
| 2160 | ||
| 2161 | super() | |
| 2162 | ||
| 2163 | this._closed = false | |
| 2164 | this._encoding = encoding | |
| 2165 | this._handle = binding.watcherInit(path, recursive, this, this._onevent, this._onclose) | |
| 2166 | ||
| 2167 | if (!persistent) this.unref() | |
| 2168 | ||
| 2169 | if (onchange) this.on('change', onchange) | |
| 2170 | } | |
| 2171 | ||
| 2172 | close() { | |
| 2173 | if (this._closed) return | |
| 2174 | this._closed = true | |
| 2175 | ||
| 2176 | binding.watcherClose(this._handle) | |
| 2177 | } | |
| 2178 | ||
| 2179 | ref() { | |
| 2180 | if (this._handle) binding.watcherRef(this._handle) | |
| 2181 | return this | |
| 2182 | } | |
| 2183 | ||
| 2184 | unref() { | |
| 2185 | if (this._handle) binding.watcherUnref(this._handle) | |
| 2186 | return this | |
| 2187 | } | |
| 2188 | ||
| 2189 | [Symbol.asyncIterator]() { | |
| 2190 | const buffer = [] | |
| 2191 | let done = false | |
| 2192 | let error = null | |
| 2193 | let next = null | |
| 2194 | ||
| 2195 | this.on('change', (eventType, filename) => { | |
| 2196 | if (next) { | |
| 2197 | next.resolve({ done: false, value: { eventType, filename } }) | |
| 2198 | next = null | |
| 2199 | } else { | |
| 2200 | buffer.push({ eventType, filename }) | |
| 2201 | } | |
| 2202 | }) | |
| 2203 | .on('error', (err) => { | |
| 2204 | done = true | |
| 2205 | error = err | |
| 2206 | ||
| 2207 | if (next) { | |
| 2208 | next.reject(error) | |
| 2209 | next = null | |
| 2210 | } | |
| 2211 | }) | |
| 2212 | .on('close', () => { | |
| 2213 | done = true | |
| 2214 | ||
| 2215 | if (next) { | |
| 2216 | next.resolve({ done }) | |
| 2217 | next = null | |
| 2218 | } | |
| 2219 | }) | |
| 2220 | ||
| 2221 | return { | |
| 2222 | next: () => | |
| 2223 | new Promise((resolve, reject) => { | |
| 2224 | if (error) return reject(error) | |
| 2225 | ||
| 2226 | if (buffer.length) return resolve({ done: false, value: buffer.shift() }) | |
| 2227 | ||
| 2228 | if (done) return resolve({ done }) | |
| 2229 | ||
| 2230 | next = { resolve, reject } | |
| 2231 | }) | |
| 2232 | } | |
| 2233 | } | |
| 2234 | ||
| 2235 | _onevent(err, events, filename) { | |
| 2236 | if (err) { | |
| 2237 | this.close() | |
| 2238 | this.emit('error', err) | |
| 2239 | } else { | |
| 2240 | const path = | |
| 2241 | this._encoding === 'buffer' | |
| 2242 | ? Buffer.from(filename) | |
| 2243 | : Buffer.from(filename).toString(this._encoding) | |
| 2244 | ||
| 2245 | if (events & binding.UV_RENAME) { | |
| 2246 | this.emit('change', 'rename', path) | |
| 2247 | } | |
| 2248 | ||
| 2249 | if (events & binding.UV_CHANGE) { | |
| 2250 | this.emit('change', 'change', path) | |
| 2251 | } | |
| 2252 | } | |
| 2253 | } | |
| 2254 | ||
| 2255 | _onclose() { | |
| 2256 | this._handle = null | |
| 2257 | ||
| 2258 | this.emit('close') | |
| 2259 | } | |
| 2260 | } | |
| 2261 | ||
| 2262 | exports.access = access | |
| 2263 | exports.appendFile = appendFile | |
| 2264 | exports.chmod = chmod | |
| 2265 | // exports.chown = chown TODO | |
| 2266 | exports.close = close | |
| 2267 | exports.copyFile = copyFile | |
| 2268 | exports.cp = cp | |
| 2269 | exports.exists = exists | |
| 2270 | exports.fchmod = fchmod | |
| 2271 | // exports.fchown = fchown TODO | |
| 2272 | // exports.fdatasync = fdatasync TODO | |
| 2273 | exports.fstat = fstat | |
| 2274 | // exports.fsync = fsync TODO | |
| 2275 | exports.ftruncate = ftruncate | |
| 2276 | // exports.futimes = futimes TODO | |
| 2277 | // exports.lchmod = lchmod TODO | |
| 2278 | // exports.lchown = lchown TODO | |
| 2279 | // exports.lutimes = lutimes TODO | |
| 2280 | // exports.link = link TODO | |
| 2281 | exports.lstat = lstat | |
| 2282 | exports.mkdir = mkdir | |
| 2283 | // exports.mkdtemp = mkdtemp TODO | |
| 2284 | exports.open = open | |
| 2285 | exports.opendir = opendir | |
| 2286 | exports.read = read | |
| 2287 | exports.readFile = readFile | |
| 2288 | exports.readdir = readdir | |
| 2289 | exports.readlink = readlink | |
| 2290 | exports.readv = readv | |
| 2291 | exports.realpath = realpath | |
| 2292 | exports.rename = rename | |
| 2293 | exports.rm = rm | |
| 2294 | exports.rmdir = rmdir | |
| 2295 | exports.stat = stat | |
| 2296 | // exports.statfs = statfs TODO | |
| 2297 | exports.symlink = symlink | |
| 2298 | // exports.truncate = truncate TODO | |
| 2299 | exports.unlink = unlink | |
| 2300 | exports.utimes = utimes | |
| 2301 | exports.watch = watch | |
| 2302 | exports.write = write | |
| 2303 | exports.writeFile = writeFile | |
| 2304 | exports.writev = writev | |
| 2305 | ||
| 2306 | exports.accessSync = accessSync | |
| 2307 | exports.appendFileSync = appendFileSync | |
| 2308 | exports.chmodSync = chmodSync | |
| 2309 | // exports.chownSync = chownSync TODO | |
| 2310 | exports.closeSync = closeSync | |
| 2311 | exports.copyFileSync = copyFileSync | |
| 2312 | exports.existsSync = existsSync | |
| 2313 | exports.fchmodSync = fchmodSync | |
| 2314 | // exports.fchownSync = fchownSync TODO | |
| 2315 | // exports.fdatasyncSync = fdatasyncSync TODO | |
| 2316 | exports.fstatSync = fstatSync | |
| 2317 | // exports.fsyncSync = fsyncSync TODO | |
| 2318 | exports.ftruncateSync = ftruncateSync | |
| 2319 | // exports.futimesSync = futimesSync TODO | |
| 2320 | // exports.lchmodSync = lchmodSync TODO | |
| 2321 | // exports.lchownSync = lchownSync TODO | |
| 2322 | // exports.lutimesSync = lutimesSync TODO | |
| 2323 | // exports.linkSync = linkSync TODO | |
| 2324 | exports.lstatSync = lstatSync | |
| 2325 | exports.mkdirSync = mkdirSync | |
| 2326 | // exports.mkdtempSync = mkdtempSync TODO | |
| 2327 | exports.openSync = openSync | |
| 2328 | exports.opendirSync = opendirSync | |
| 2329 | exports.readFileSync = readFileSync | |
| 2330 | exports.readSync = readSync | |
| 2331 | exports.readdirSync = readdirSync | |
| 2332 | exports.readlinkSync = readlinkSync | |
| 2333 | exports.readvSync = readvSync | |
| 2334 | exports.realpathSync = realpathSync | |
| 2335 | exports.renameSync = renameSync | |
| 2336 | exports.rmSync = rmSync | |
| 2337 | exports.rmdirSync = rmdirSync | |
| 2338 | exports.statSync = statSync | |
| 2339 | // exports.statfsSync = statfsSync TODO | |
| 2340 | exports.symlinkSync = symlinkSync | |
| 2341 | // exports.truncateSync = truncateSync TODO | |
| 2342 | exports.unlinkSync = unlinkSync | |
| 2343 | exports.utimesSync = utimesSync | |
| 2344 | exports.writeFileSync = writeFileSync | |
| 2345 | exports.writeSync = writeSync | |
| 2346 | exports.writevSync = writevSync | |
| 2347 | ||
| 2348 | exports.promises = require('./promises') | |
| 2349 | ||
| 2350 | exports.Stats = Stats | |
| 2351 | exports.Dir = Dir | |
| 2352 | exports.Dirent = Dirent | |
| 2353 | exports.Watcher = Watcher | |
| 2354 | ||
| 2355 | exports.ReadStream = FileReadStream | |
| 2356 | ||
| 2357 | exports.createReadStream = function createReadStream(path, opts) { | |
| 2358 | return new FileReadStream(path, opts) | |
| 2359 | } | |
| 2360 | ||
| 2361 | exports.WriteStream = FileWriteStream | |
| 2362 | ||
| 2363 | exports.createWriteStream = function createWriteStream(path, opts) { | |
| 2364 | return new FileWriteStream(path, opts) | |
| 2365 | } | |
| 2366 | ||
| 2367 | function toNamespacedPath(filepath) { | |
| 2368 | if (typeof filepath !== 'string') { | |
| 2369 | if (isURL(filepath)) filepath = fileURLToPath(filepath) | |
| 2370 | else filepath = filepath.toString() | |
| 2371 | } | |
| 2372 | ||
| 2373 | return path.toNamespacedPath(filepath) | |
| 2374 | } | |
| 2375 | ||
| 2376 | function toFlags(flags) { | |
| 2377 | switch (flags) { | |
| 2378 | case 'r': | |
| 2379 | return constants.O_RDONLY | |
| 2380 | case 'rs': // Fall through. | |
| 2381 | case 'sr': | |
| 2382 | return constants.O_RDONLY | constants.O_SYNC | |
| 2383 | case 'r+': | |
| 2384 | return constants.O_RDWR | |
| 2385 | case 'rs+': // Fall through. | |
| 2386 | case 'sr+': | |
| 2387 | return constants.O_RDWR | constants.O_SYNC | |
| 2388 | ||
| 2389 | case 'w': | |
| 2390 | return constants.O_TRUNC | constants.O_CREAT | constants.O_WRONLY | |
| 2391 | case 'wx': // Fall through. | |
| 2392 | case 'xw': | |
| 2393 | return constants.O_TRUNC | constants.O_CREAT | constants.O_WRONLY | constants.O_EXCL | |
| 2394 | ||
| 2395 | case 'w+': | |
| 2396 | return constants.O_TRUNC | constants.O_CREAT | constants.O_RDWR | |
| 2397 | case 'wx+': // Fall through. | |
| 2398 | case 'xw+': | |
| 2399 | return constants.O_TRUNC | constants.O_CREAT | constants.O_RDWR | constants.O_EXCL | |
| 2400 | ||
| 2401 | case 'a': | |
| 2402 | return constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY | |
| 2403 | case 'ax': // Fall through. | |
| 2404 | case 'xa': | |
| 2405 | return constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY | constants.O_EXCL | |
| 2406 | case 'as': // Fall through. | |
| 2407 | case 'sa': | |
| 2408 | return constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY | constants.O_SYNC | |
| 2409 | ||
| 2410 | case 'a+': | |
| 2411 | return constants.O_APPEND | constants.O_CREAT | constants.O_RDWR | |
| 2412 | case 'ax+': // Fall through. | |
| 2413 | case 'xa+': | |
| 2414 | return constants.O_APPEND | constants.O_CREAT | constants.O_RDWR | constants.O_EXCL | |
| 2415 | case 'as+': // Fall through. | |
| 2416 | case 'sa+': | |
| 2417 | return constants.O_APPEND | constants.O_CREAT | constants.O_RDWR | constants.O_SYNC | |
| 2418 | default: | |
| 2419 | return 0 | |
| 2420 | } | |
| 2421 | } | |
| 2422 | ||
| 2423 | function toMode(mode) { | |
| 2424 | return parseInt(mode, 8) | |
| 2425 | } |
| … | … | @@ -0,0 +1,49 @@ |
| 1 | declare const constants: { | |
| 2 | O_RDWR: number | |
| 3 | O_RDONLY: number | |
| 4 | O_WRONLY: number | |
| 5 | O_CREAT: number | |
| 6 | O_TRUNC: number | |
| 7 | O_APPEND: number | |
| 8 | ||
| 9 | F_OK: number | |
| 10 | R_OK: number | |
| 11 | W_OK: number | |
| 12 | X_OK: number | |
| 13 | ||
| 14 | S_IFMT: number | |
| 15 | S_IFREG: number | |
| 16 | S_IFDIR: number | |
| 17 | S_IFCHR: number | |
| 18 | S_IFLNK: number | |
| 19 | S_IFBLK: number | |
| 20 | S_IFIFO: number | |
| 21 | S_IFSOCK: number | |
| 22 | ||
| 23 | S_IRUSR: number | |
| 24 | S_IWUSR: number | |
| 25 | S_IXUSR: number | |
| 26 | S_IRGRP: number | |
| 27 | S_IWGRP: number | |
| 28 | S_IXGRP: number | |
| 29 | S_IROTH: number | |
| 30 | S_IWOTH: number | |
| 31 | S_IXOTH: number | |
| 32 | ||
| 33 | UV_DIRENT_UNKNOWN: number | |
| 34 | UV_DIRENT_FILE: number | |
| 35 | UV_DIRENT_DIR: number | |
| 36 | UV_DIRENT_LINK: number | |
| 37 | UV_DIRENT_FIFO: number | |
| 38 | UV_DIRENT_SOCKET: number | |
| 39 | UV_DIRENT_CHAR: number | |
| 40 | UV_DIRENT_BLOCK: number | |
| 41 | ||
| 42 | COPYFILE_EXCL: number | |
| 43 | COPYFILE_FICLONE: number | |
| 44 | COPYFILE_FICLONE_FORCE: number | |
| 45 | UV_FS_SYMLINK_DIR: number | |
| 46 | UV_FS_SYMLINK_JUNCTION: number | |
| 47 | } | |
| 48 | ||
| 49 | export = constants |
| … | … | @@ -0,0 +1,49 @@ |
| 1 | const binding = require('../binding') | |
| 2 | ||
| 3 | module.exports = { | |
| 4 | O_RDWR: binding.O_RDWR, | |
| 5 | O_RDONLY: binding.O_RDONLY, | |
| 6 | O_WRONLY: binding.O_WRONLY, | |
| 7 | O_CREAT: binding.O_CREAT, | |
| 8 | O_TRUNC: binding.O_TRUNC, | |
| 9 | O_APPEND: binding.O_APPEND, | |
| 10 | ||
| 11 | F_OK: binding.F_OK || 0, | |
| 12 | R_OK: binding.R_OK || 0, | |
| 13 | W_OK: binding.W_OK || 0, | |
| 14 | X_OK: binding.X_OK || 0, | |
| 15 | ||
| 16 | S_IFMT: binding.S_IFMT, | |
| 17 | S_IFREG: binding.S_IFREG, | |
| 18 | S_IFDIR: binding.S_IFDIR, | |
| 19 | S_IFCHR: binding.S_IFCHR, | |
| 20 | S_IFLNK: binding.S_IFLNK, | |
| 21 | S_IFBLK: binding.S_IFBLK || 0, | |
| 22 | S_IFIFO: binding.S_IFIFO || 0, | |
| 23 | S_IFSOCK: binding.S_IFSOCK || 0, | |
| 24 | ||
| 25 | S_IRUSR: binding.S_IRUSR || 0, | |
| 26 | S_IWUSR: binding.S_IWUSR || 0, | |
| 27 | S_IXUSR: binding.S_IXUSR || 0, | |
| 28 | S_IRGRP: binding.S_IRGRP || 0, | |
| 29 | S_IWGRP: binding.S_IWGRP || 0, | |
| 30 | S_IXGRP: binding.S_IXGRP || 0, | |
| 31 | S_IROTH: binding.S_IROTH || 0, | |
| 32 | S_IWOTH: binding.S_IWOTH || 0, | |
| 33 | S_IXOTH: binding.S_IXOTH || 0, | |
| 34 | ||
| 35 | UV_DIRENT_UNKNOWN: binding.UV_DIRENT_UNKNOWN, | |
| 36 | UV_DIRENT_FILE: binding.UV_DIRENT_FILE, | |
| 37 | UV_DIRENT_DIR: binding.UV_DIRENT_DIR, | |
| 38 | UV_DIRENT_LINK: binding.UV_DIRENT_LINK, | |
| 39 | UV_DIRENT_FIFO: binding.UV_DIRENT_FIFO, | |
| 40 | UV_DIRENT_SOCKET: binding.UV_DIRENT_SOCKET, | |
| 41 | UV_DIRENT_CHAR: binding.UV_DIRENT_CHAR, | |
| 42 | UV_DIRENT_BLOCK: binding.UV_DIRENT_BLOCK, | |
| 43 | ||
| 44 | COPYFILE_EXCL: binding.UV_FS_COPYFILE_EXCL, | |
| 45 | COPYFILE_FICLONE: binding.UV_FS_COPYFILE_FICLONE, | |
| 46 | COPYFILE_FICLONE_FORCE: binding.UV_FS_COPYFILE_FICLONE_FORCE, | |
| 47 | UV_FS_SYMLINK_DIR: binding.UV_FS_SYMLINK_DIR, | |
| 48 | UV_FS_SYMLINK_JUNCTION: binding.UV_FS_SYMLINK_JUNCTION | |
| 49 | } |
| … | … | @@ -0,0 +1,11 @@ |
| 1 | declare class FileError extends Error { | |
| 2 | private constructor(msg: string) | |
| 3 | ||
| 4 | readonly code: string | |
| 5 | readonly operation?: string | |
| 6 | readonly path?: string | |
| 7 | readonly destination?: string | |
| 8 | readonly fd?: number | |
| 9 | } | |
| 10 | ||
| 11 | export = FileError |
| … | … | @@ -0,0 +1,55 @@ |
| 1 | const os = require('bare-os') | |
| 2 | ||
| 3 | module.exports = class FileError extends Error { | |
| 4 | constructor(msg, opts = {}) { | |
| 5 | const { code, operation = null, path = null, destination = null, fd = -1 } = opts | |
| 6 | ||
| 7 | if (operation !== null) msg += describe(operation, opts) | |
| 8 | ||
| 9 | super(`${code}: ${msg}`) | |
| 10 | ||
| 11 | this.code = code | |
| 12 | ||
| 13 | if (operation !== null) this.operation = operation | |
| 14 | if (path !== null) this.path = path | |
| 15 | if (destination !== null) this.destination = destination | |
| 16 | if (fd !== -1) this.fd = fd | |
| 17 | } | |
| 18 | ||
| 19 | get name() { | |
| 20 | return 'FileError' | |
| 21 | } | |
| 22 | ||
| 23 | // For Node.js compatibility | |
| 24 | get errno() { | |
| 25 | return os.constants.errnos[this.code] | |
| 26 | } | |
| 27 | ||
| 28 | // For Node.js compatibility | |
| 29 | get syscall() { | |
| 30 | return this.operation | |
| 31 | } | |
| 32 | ||
| 33 | // For Node.js compatibility | |
| 34 | get dest() { | |
| 35 | return this.destination | |
| 36 | } | |
| 37 | } | |
| 38 | ||
| 39 | function describe(operation, opts) { | |
| 40 | const { path = null, destination = null, fd = -1 } = opts | |
| 41 | ||
| 42 | let result = `, ${operation}` | |
| 43 | ||
| 44 | if (path !== null) { | |
| 45 | result += ` ${JSON.stringify(path)}` | |
| 46 | ||
| 47 | if (destination !== null) { | |
| 48 | result += ` -> ${JSON.stringify(destination)}` | |
| 49 | } | |
| 50 | } else if (fd !== -1) { | |
| 51 | result += ` ${fd}` | |
| 52 | } | |
| 53 | ||
| 54 | return result | |
| 55 | } |
| … | … | @@ -0,0 +1,71 @@ |
| 1 | { | |
| 2 | "name": "bare-fs", | |
| 3 | "version": "4.5.6", | |
| 4 | "description": "Native file system operations for Javascript", | |
| 5 | "exports": { | |
| 6 | "./package": "./package.json", | |
| 7 | ".": { | |
| 8 | "types": "./index.d.ts", | |
| 9 | "default": "./index.js" | |
| 10 | }, | |
| 11 | "./promises": { | |
| 12 | "types": "./promises.d.ts", | |
| 13 | "default": "./promises.js" | |
| 14 | }, | |
| 15 | "./constants": { | |
| 16 | "types": "./lib/constants.d.ts", | |
| 17 | "default": "./lib/constants.js" | |
| 18 | } | |
| 19 | }, | |
| 20 | "files": [ | |
| 21 | "index.js", | |
| 22 | "index.d.ts", | |
| 23 | "promises.js", | |
| 24 | "promises.d.ts", | |
| 25 | "binding.c", | |
| 26 | "binding.js", | |
| 27 | "CMakeLists.txt", | |
| 28 | "lib", | |
| 29 | "prebuilds" | |
| 30 | ], | |
| 31 | "addon": true, | |
| 32 | "scripts": { | |
| 33 | "test": "prettier . --check && bare test.js" | |
| 34 | }, | |
| 35 | "repository": { | |
| 36 | "type": "git", | |
| 37 | "url": "git+https://github.com/holepunchto/bare-fs.git" | |
| 38 | }, | |
| 39 | "author": "Holepunch", | |
| 40 | "license": "Apache-2.0", | |
| 41 | "bugs": { | |
| 42 | "url": "https://github.com/holepunchto/bare-fs/issues" | |
| 43 | }, | |
| 44 | "homepage": "https://github.com/holepunchto/bare-fs#readme", | |
| 45 | "engines": { | |
| 46 | "bare": ">=1.16.0" | |
| 47 | }, | |
| 48 | "dependencies": { | |
| 49 | "bare-events": "^2.5.4", | |
| 50 | "bare-path": "^3.0.0", | |
| 51 | "bare-stream": "^2.6.4", | |
| 52 | "bare-url": "^2.2.2", | |
| 53 | "fast-fifo": "^1.3.2" | |
| 54 | }, | |
| 55 | "devDependencies": { | |
| 56 | "bare-buffer": "^3.0.2", | |
| 57 | "bare-crypto": "^1.11.2", | |
| 58 | "brittle": "^3.1.1", | |
| 59 | "cmake-bare": "^1.1.7", | |
| 60 | "prettier": "^3.4.1", | |
| 61 | "prettier-config-holepunch": "^2.0.0" | |
| 62 | }, | |
| 63 | "peerDependencies": { | |
| 64 | "bare-buffer": "*" | |
| 65 | }, | |
| 66 | "peerDependenciesMeta": { | |
| 67 | "bare-buffer": { | |
| 68 | "optional": true | |
| 69 | } | |
| 70 | } | |
| 71 | } |
| … | … | @@ -0,0 +1,282 @@ |
| 1 | import EventEmitter, { EventMap } from 'bare-events' | |
| 2 | import Buffer, { BufferEncoding } from 'bare-buffer' | |
| 3 | import { | |
| 4 | constants, | |
| 5 | AppendFileOptions, | |
| 6 | CpOptions, | |
| 7 | Dir, | |
| 8 | MkdirOptions, | |
| 9 | OpendirOptions, | |
| 10 | Path, | |
| 11 | ReadFileOptions, | |
| 12 | ReadStream, | |
| 13 | ReadStreamOptions, | |
| 14 | ReaddirOptions, | |
| 15 | ReadlinkOptions, | |
| 16 | RealpathOptions, | |
| 17 | RmOptions, | |
| 18 | Stats, | |
| 19 | Watcher, | |
| 20 | WatcherOptions, | |
| 21 | WriteFileOptions, | |
| 22 | WriteStream, | |
| 23 | WriteStreamOptions | |
| 24 | } from '.' | |
| 25 | ||
| 26 | export { constants } | |
| 27 | ||
| 28 | interface FileHandleEvents extends EventMap { | |
| 29 | close: [] | |
| 30 | } | |
| 31 | ||
| 32 | interface FileHandle extends EventEmitter<FileHandleEvents>, AsyncDisposable { | |
| 33 | close(): Promise<void> | |
| 34 | ||
| 35 | read( | |
| 36 | buffer: Buffer | ArrayBufferView, | |
| 37 | offset?: number, | |
| 38 | len?: number, | |
| 39 | pos?: number | |
| 40 | ): Promise<number> | |
| 41 | ||
| 42 | readv(buffers: ArrayBufferView[], position?: number): Promise<number> | |
| 43 | ||
| 44 | write( | |
| 45 | data: Buffer | ArrayBufferView, | |
| 46 | offset?: number, | |
| 47 | len?: number, | |
| 48 | pos?: number | |
| 49 | ): Promise<number> | |
| 50 | ||
| 51 | write(data: string, pos?: number, encoding?: BufferEncoding): Promise<number> | |
| 52 | ||
| 53 | stat(): Promise<Stats> | |
| 54 | ||
| 55 | chmod(mode: string | number): Promise<void> | |
| 56 | ||
| 57 | createReadStream(opts?: ReadStreamOptions): ReadStream | |
| 58 | ||
| 59 | createWriteStream(opts?: WriteStreamOptions): WriteStream | |
| 60 | } | |
| 61 | ||
| 62 | declare class FileHandle { | |
| 63 | private constructor(fd: number) | |
| 64 | } | |
| 65 | ||
| 66 | export function open( | |
| 67 | filepath: Path, | |
| 68 | flags?: Flag | number, | |
| 69 | mode?: string | number | |
| 70 | ): Promise<FileHandle> | |
| 71 | ||
| 72 | export function access(filepath: Path, mode?: number): Promise<void> | |
| 73 | ||
| 74 | export function appendFile( | |
| 75 | filepath: Path, | |
| 76 | data: string | Buffer | ArrayBufferView, | |
| 77 | opts?: AppendFileOptions | |
| 78 | ): Promise<void> | |
| 79 | ||
| 80 | export function appendFile( | |
| 81 | filepath: Path, | |
| 82 | data: string | Buffer | ArrayBufferView, | |
| 83 | encoding: BufferEncoding | |
| 84 | ): Promise<void> | |
| 85 | ||
| 86 | export function chmod(filepath: Path, mode: string | number): Promise<void> | |
| 87 | ||
| 88 | export function copyFile(src: Path, dst: Path, mode?: number): Promise<void> | |
| 89 | ||
| 90 | export function cp(src: Path, dst: Path, opts?: CpOptions): Promise<void> | |
| 91 | ||
| 92 | export function lstat(filepath: Path): Promise<Stats> | |
| 93 | ||
| 94 | export function mkdir(filepath: Path, opts?: MkdirOptions): Promise<void> | |
| 95 | ||
| 96 | export function mkdir(filepath: Path, mode: number): Promise<void> | |
| 97 | ||
| 98 | export function opendir( | |
| 99 | filepath: Path, | |
| 100 | opts: OpendirOptions & { encoding?: BufferEncoding } | |
| 101 | ): Promise<Dir<string>> | |
| 102 | ||
| 103 | export function opendir( | |
| 104 | filepath: Path, | |
| 105 | opts: OpendirOptions & { encoding: 'buffer' } | |
| 106 | ): Promise<Dir<Buffer>> | |
| 107 | ||
| 108 | export function opendir(filepath: Path, opts: OpendirOptions): Promise<Dir> | |
| 109 | ||
| 110 | export function opendir(filepath: Path, encoding: BufferEncoding): Promise<Dir<string>> | |
| 111 | ||
| 112 | export function opendir(filepath: Path, encoding: 'buffer'): Promise<Dir<Buffer>> | |
| 113 | ||
| 114 | export function opendir(filepath: Path, encoding: BufferEncoding | 'buffer'): Promise<Dir> | |
| 115 | ||
| 116 | export function opendir(filepath: Path): Promise<Dir<string>> | |
| 117 | ||
| 118 | export function readFile( | |
| 119 | filepath: Path, | |
| 120 | opts: ReadFileOptions & { encoding: BufferEncoding } | |
| 121 | ): Promise<string> | |
| 122 | ||
| 123 | export function readFile( | |
| 124 | filepath: Path, | |
| 125 | opts: ReadFileOptions & { encoding?: 'buffer' } | |
| 126 | ): Promise<Buffer> | |
| 127 | ||
| 128 | export function readFile(filepath: Path, opts: ReadFileOptions): Promise<string | Buffer> | |
| 129 | ||
| 130 | export function readFile(filepath: Path, encoding: BufferEncoding): Promise<string> | |
| 131 | ||
| 132 | export function readFile(filepath: Path, encoding: 'buffer'): Promise<Buffer> | |
| 133 | ||
| 134 | export function readFile( | |
| 135 | filepath: Path, | |
| 136 | encoding?: BufferEncoding | 'buffer' | |
| 137 | ): Promise<string | Buffer> | |
| 138 | ||
| 139 | export function readFile(filepath: Path): Promise<Buffer> | |
| 140 | ||
| 141 | export function readdir( | |
| 142 | filepath: Path, | |
| 143 | opts: ReaddirOptions & { encoding?: BufferEncoding } | |
| 144 | ): Promise<Dir<string>[] | string[]> | |
| 145 | ||
| 146 | export function readdir( | |
| 147 | filepath: Path, | |
| 148 | opts: ReaddirOptions & { encoding?: BufferEncoding; withFileTypes: true } | |
| 149 | ): Promise<Dir<string>[]> | |
| 150 | ||
| 151 | export function readdir( | |
| 152 | filepath: Path, | |
| 153 | opts: ReaddirOptions & { encoding?: BufferEncoding; withFileTypes?: false } | |
| 154 | ): Promise<string[]> | |
| 155 | ||
| 156 | export function readdir( | |
| 157 | filepath: Path, | |
| 158 | opts: ReaddirOptions & { encoding: 'buffer' } | |
| 159 | ): Promise<Dir<Buffer>[] | Buffer[]> | |
| 160 | ||
| 161 | export function readdir( | |
| 162 | filepath: Path, | |
| 163 | opts: ReaddirOptions & { encoding: 'buffer'; withFileTypes: true } | |
| 164 | ): Promise<Dir<Buffer>[]> | |
| 165 | ||
| 166 | export function readdir( | |
| 167 | filepath: Path, | |
| 168 | opts: ReaddirOptions & { encoding: 'buffer'; withFileTypes?: false } | |
| 169 | ): Promise<Buffer[]> | |
| 170 | ||
| 171 | export function readdir( | |
| 172 | filepath: Path, | |
| 173 | opts: ReaddirOptions & { withFileTypes: true } | |
| 174 | ): Promise<Dir<string | Buffer>[]> | |
| 175 | ||
| 176 | export function readdir( | |
| 177 | filepath: Path, | |
| 178 | opts: ReaddirOptions & { withFileTypes?: false } | |
| 179 | ): Promise<string[] | Buffer[]> | |
| 180 | ||
| 181 | export function readdir(filepath: Path, opts: ReaddirOptions): Promise<Dir[] | string[] | Buffer[]> | |
| 182 | ||
| 183 | export function readdir(filepath: Path, encoding: BufferEncoding): Promise<string[]> | |
| 184 | ||
| 185 | export function readdir(filepath: Path, encoding: 'buffer'): Promise<Buffer[]> | |
| 186 | ||
| 187 | export function readdir( | |
| 188 | filepath: Path, | |
| 189 | encoding: BufferEncoding | 'buffer' | |
| 190 | ): Promise<string[] | Buffer[]> | |
| 191 | ||
| 192 | export function readdir(filepath: Path): Promise<string[]> | |
| 193 | ||
| 194 | export function readlink( | |
| 195 | filepath: Path, | |
| 196 | opts: ReadlinkOptions & { encoding?: BufferEncoding } | |
| 197 | ): Promise<string> | |
| 198 | ||
| 199 | export function readlink( | |
| 200 | filepath: Path, | |
| 201 | opts: ReadlinkOptions & { encoding: 'buffer' } | |
| 202 | ): Promise<Buffer> | |
| 203 | ||
| 204 | export function readlink(filepath: Path, opts: ReadlinkOptions): Promise<string | Buffer> | |
| 205 | ||
| 206 | export function readlink(filepath: Path, encoding: BufferEncoding): Promise<string> | |
| 207 | ||
| 208 | export function readlink(filepath: Path, encoding: 'buffer'): Promise<Buffer> | |
| 209 | ||
| 210 | export function readlink( | |
| 211 | filepath: Path, | |
| 212 | encoding: BufferEncoding | 'buffer' | |
| 213 | ): Promise<string | Buffer> | |
| 214 | ||
| 215 | export function readlink(filepath: Path): Promise<string> | |
| 216 | ||
| 217 | export function realpath( | |
| 218 | filepath: Path, | |
| 219 | opts: RealpathOptions & { encoding?: BufferEncoding } | |
| 220 | ): Promise<string> | |
| 221 | ||
| 222 | export function realpath( | |
| 223 | filepath: Path, | |
| 224 | opts: RealpathOptions & { encoding: 'buffer' } | |
| 225 | ): Promise<Buffer> | |
| 226 | ||
| 227 | export function realpath(filepath: Path, opts: RealpathOptions): Promise<string | Buffer> | |
| 228 | ||
| 229 | export function realpath(filepath: Path, encoding: BufferEncoding): Promise<string> | |
| 230 | ||
| 231 | export function realpath(filepath: Path, encoding: 'buffer'): Promise<Buffer> | |
| 232 | ||
| 233 | export function realpath( | |
| 234 | filepath: Path, | |
| 235 | encoding: BufferEncoding | 'buffer' | |
| 236 | ): Promise<string | Buffer> | |
| 237 | ||
| 238 | export function realpath(filepath: Path): Promise<string> | |
| 239 | ||
| 240 | export function rename(src: Path, dst: Path): Promise<void> | |
| 241 | ||
| 242 | export function rm(filepath: Path, opts?: RmOptions): Promise<void> | |
| 243 | ||
| 244 | export function rmdir(filepath: Path): Promise<void> | |
| 245 | ||
| 246 | export function stat(filepath: Path): Promise<Stats> | |
| 247 | ||
| 248 | export function symlink(target: Path, filepath: Path, type?: string | number): Promise<void> | |
| 249 | ||
| 250 | export function unlink(filepath: Path): Promise<void> | |
| 251 | ||
| 252 | export function watch( | |
| 253 | filepath: Path, | |
| 254 | opts: WatcherOptions & { encoding?: BufferEncoding } | |
| 255 | ): Watcher<string> | |
| 256 | ||
| 257 | export function watch( | |
| 258 | filepath: Path, | |
| 259 | opts: WatcherOptions & { encoding: 'buffer' } | |
| 260 | ): Watcher<Buffer> | |
| 261 | ||
| 262 | export function watch(filepath: Path, opts: WatcherOptions): Watcher | |
| 263 | ||
| 264 | export function watch(filepath: Path, encoding: BufferEncoding): Watcher<string> | |
| 265 | ||
| 266 | export function watch(filepath: Path, encoding: 'buffer'): Watcher<Buffer> | |
| 267 | ||
| 268 | export function watch(filepath: Path, encoding: BufferEncoding | 'buffer'): Watcher | |
| 269 | ||
| 270 | export function watch(filepath: Path): Watcher<string> | |
| 271 | ||
| 272 | export function writeFile( | |
| 273 | filepath: Path, | |
| 274 | data: string | Buffer | ArrayBufferView, | |
| 275 | opts?: WriteFileOptions | |
| 276 | ): Promise<void> | |
| 277 | ||
| 278 | export function writeFile( | |
| 279 | filepath: Path, | |
| 280 | data: string | Buffer | ArrayBufferView, | |
| 281 | encoding: BufferEncoding | |
| 282 | ): Promise<void> |
| … | … | @@ -0,0 +1,90 @@ |
| 1 | const EventEmitter = require('bare-events') | |
| 2 | const fs = require('.') | |
| 3 | ||
| 4 | class FileHandle extends EventEmitter { | |
| 5 | constructor(fd) { | |
| 6 | this.fd = fd | |
| 7 | } | |
| 8 | ||
| 9 | async close() { | |
| 10 | await fs.close(fd) | |
| 11 | ||
| 12 | this.fd = -1 | |
| 13 | this.emit('close') | |
| 14 | } | |
| 15 | ||
| 16 | async read(buffer, ...args) { | |
| 17 | return { | |
| 18 | bytesRead: await fs.read(this.fd, buffer, ...args), | |
| 19 | buffer | |
| 20 | } | |
| 21 | } | |
| 22 | ||
| 23 | async readv(buffers, ...args) { | |
| 24 | return { | |
| 25 | bytesRead: await fs.readv(this.fd, buffers, ...args), | |
| 26 | buffers | |
| 27 | } | |
| 28 | } | |
| 29 | ||
| 30 | async write(buffer, ...args) { | |
| 31 | return { | |
| 32 | bytesWritten: await fs.write(this.fd, buffer, ...args), | |
| 33 | buffer | |
| 34 | } | |
| 35 | } | |
| 36 | ||
| 37 | async writev(buffers, ...args) { | |
| 38 | return { | |
| 39 | bytesWritten: await fs.writev(this.fd, buffers, ...args), | |
| 40 | buffers | |
| 41 | } | |
| 42 | } | |
| 43 | ||
| 44 | async stat() { | |
| 45 | return fs.fstat(this.fd) | |
| 46 | } | |
| 47 | ||
| 48 | async chmod(mode) { | |
| 49 | await fs.fchmod(this.fd, mode) | |
| 50 | } | |
| 51 | ||
| 52 | createReadStream(opts) { | |
| 53 | return fs.createReadStream(null, { ...opts, fd: this.fd }) | |
| 54 | } | |
| 55 | ||
| 56 | createWriteStream(opts) { | |
| 57 | return fs.createWriteStream(null, { ...opts, fd: this.fd }) | |
| 58 | } | |
| 59 | ||
| 60 | async [Symbol.asyncDispose]() { | |
| 61 | await this.close() | |
| 62 | } | |
| 63 | } | |
| 64 | ||
| 65 | exports.open = async function open(filepath, flags, mode) { | |
| 66 | return new FileHandle(await fs.open(filepath, flags, mode)) | |
| 67 | } | |
| 68 | ||
| 69 | exports.access = fs.access | |
| 70 | exports.appendFile = fs.appendFile | |
| 71 | exports.chmod = fs.chmod | |
| 72 | exports.constants = fs.constants | |
| 73 | exports.copyFile = fs.copyFile | |
| 74 | exports.cp = fs.cp | |
| 75 | exports.lstat = fs.lstat | |
| 76 | exports.mkdir = fs.mkdir | |
| 77 | exports.opendir = fs.opendir | |
| 78 | exports.readFile = fs.readFile | |
| 79 | exports.readdir = fs.readdir | |
| 80 | exports.readlink = fs.readlink | |
| 81 | exports.realpath = fs.realpath | |
| 82 | exports.rename = fs.rename | |
| 83 | exports.rm = fs.rm | |
| 84 | exports.rmdir = fs.rmdir | |
| 85 | exports.stat = fs.stat | |
| 86 | exports.symlink = fs.symlink | |
| 87 | exports.unlink = fs.unlink | |
| 88 | exports.utimes = fs.utimes | |
| 89 | exports.watch = fs.watch | |
| 90 | exports.writeFile = fs.writeFile |
| … | … | @@ -0,0 +1,13 @@ |
| 1 | cmake_minimum_required(VERSION 3.25) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | ||
| 5 | project(bare_inspect C) | |
| 6 | ||
| 7 | add_bare_module(bare_inspect) | |
| 8 | ||
| 9 | target_sources( | |
| 10 | ${bare_inspect} | |
| 11 | PRIVATE | |
| 12 | binding.c | |
| 13 | ) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,19 @@ |
| 1 | # bare-inspect | |
| 2 | ||
| 3 | Inspect objects as strings for debugging. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i bare-inspect | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const inspect = require('bare-inspect') | |
| 13 | ||
| 14 | console.log(inspect(globalThis)) | |
| 15 | ``` | |
| 16 | ||
| 17 | ## License | |
| 18 | ||
| 19 | Apache-2.0 |
| … | … | @@ -0,0 +1,111 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <bare.h> | |
| 3 | #include <js.h> | |
| 4 | ||
| 5 | static js_value_t * | |
| 6 | bare_inspect_get_promise_state(js_env_t *env, js_callback_info_t *info) { | |
| 7 | int err; | |
| 8 | ||
| 9 | js_value_t *argv[1]; | |
| 10 | size_t argc = 1; | |
| 11 | ||
| 12 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 13 | assert(err == 0); | |
| 14 | ||
| 15 | assert(argc == 1); | |
| 16 | ||
| 17 | js_promise_state_t state; | |
| 18 | err = js_get_promise_state(env, argv[0], &state); | |
| 19 | assert(err == 0); | |
| 20 | ||
| 21 | js_value_t *result; | |
| 22 | err = js_create_uint32(env, state, &result); | |
| 23 | assert(err == 0); | |
| 24 | ||
| 25 | return result; | |
| 26 | } | |
| 27 | ||
| 28 | static js_value_t * | |
| 29 | bare_inspect_get_promise_result(js_env_t *env, js_callback_info_t *info) { | |
| 30 | int err; | |
| 31 | ||
| 32 | js_value_t *argv[1]; | |
| 33 | size_t argc = 1; | |
| 34 | ||
| 35 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 36 | assert(err == 0); | |
| 37 | ||
| 38 | assert(argc == 1); | |
| 39 | ||
| 40 | js_value_t *result; | |
| 41 | err = js_get_promise_result(env, argv[0], &result); | |
| 42 | assert(err == 0); | |
| 43 | ||
| 44 | return result; | |
| 45 | } | |
| 46 | ||
| 47 | static js_value_t * | |
| 48 | bare_inspect_get_external(js_env_t *env, js_callback_info_t *info) { | |
| 49 | int err; | |
| 50 | ||
| 51 | size_t argc = 1; | |
| 52 | js_value_t *argv[1]; | |
| 53 | ||
| 54 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 55 | assert(err == 0); | |
| 56 | ||
| 57 | assert(argc == 1); | |
| 58 | ||
| 59 | void *data; | |
| 60 | err = js_get_value_external(env, argv[0], &data); | |
| 61 | assert(err == 0); | |
| 62 | ||
| 63 | js_value_t *result; | |
| 64 | err = js_create_bigint_uint64(env, (uintptr_t) data, &result); | |
| 65 | assert(err == 0); | |
| 66 | ||
| 67 | return result; | |
| 68 | } | |
| 69 | ||
| 70 | static js_value_t * | |
| 71 | bare_inspect_get_own_non_index_property_names(js_env_t *env, js_callback_info_t *info) { | |
| 72 | int err; | |
| 73 | ||
| 74 | size_t argc = 1; | |
| 75 | js_value_t *argv[1]; | |
| 76 | ||
| 77 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 78 | assert(err == 0); | |
| 79 | ||
| 80 | assert(argc == 1); | |
| 81 | ||
| 82 | js_value_t *result; | |
| 83 | err = js_get_filtered_property_names(env, argv[0], js_key_own_only, js_property_only_enumerable, js_index_skip_indices, js_key_convert_to_string, &result); | |
| 84 | assert(err == 0); | |
| 85 | ||
| 86 | return result; | |
| 87 | } | |
| 88 | ||
| 89 | static js_value_t * | |
| 90 | bare_inspect_exports(js_env_t *env, js_value_t *exports) { | |
| 91 | int err; | |
| 92 | ||
| 93 | #define V(name, fn) \ | |
| 94 | { \ | |
| 95 | js_value_t *val; \ | |
| 96 | err = js_create_function(env, name, -1, fn, NULL, &val); \ | |
| 97 | assert(err == 0); \ | |
| 98 | err = js_set_named_property(env, exports, name, val); \ | |
| 99 | assert(err == 0); \ | |
| 100 | } | |
| 101 | ||
| 102 | V("getPromiseState", bare_inspect_get_promise_state) | |
| 103 | V("getPromiseResult", bare_inspect_get_promise_result) | |
| 104 | V("getExternal", bare_inspect_get_external) | |
| 105 | V("getOwnNonIndexPropertyNames", bare_inspect_get_own_non_index_property_names) | |
| 106 | #undef V | |
| 107 | ||
| 108 | return exports; | |
| 109 | } | |
| 110 | ||
| 111 | BARE_MODULE(bare_inspect, bare_inspect_exports) |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require.addon() |
| … | … | @@ -0,0 +1,33 @@ |
| 1 | interface InspectStylize { | |
| 2 | (value: string, style: keyof typeof inspect.styles): string | |
| 3 | } | |
| 4 | ||
| 5 | interface InspectOptions { | |
| 6 | colors?: boolean | |
| 7 | depth?: number | |
| 8 | breakLength?: number | |
| 9 | stylize?: InspectStylize | |
| 10 | } | |
| 11 | ||
| 12 | declare function inspect(value: unknown, opts?: InspectOptions): string | |
| 13 | ||
| 14 | declare namespace inspect { | |
| 15 | export const styles: { | |
| 16 | bigint: string | |
| 17 | boolean: string | |
| 18 | date: string | |
| 19 | module: string | |
| 20 | name: string | |
| 21 | null: string | |
| 22 | number: string | |
| 23 | regexp: string | |
| 24 | special: string | |
| 25 | string: string | |
| 26 | symbol: string | |
| 27 | undefined: string | |
| 28 | } | |
| 29 | ||
| 30 | export { type InspectOptions, type InspectStylize } | |
| 31 | } | |
| 32 | ||
| 33 | export = inspect |
| … | … | @@ -0,0 +1,1063 @@ |
| 1 | const ansiEscapes = require('bare-ansi-escapes') | |
| 2 | const getType = require('bare-type') | |
| 3 | const binding = require('./binding') | |
| 4 | ||
| 5 | const PLAIN_KEY = /^[a-zA-Z_][a-zA-Z_0-9]*$/ | |
| 6 | ||
| 7 | const defaultDepth = 2 | |
| 8 | const defaultBreakLength = 80 | |
| 9 | const defaultMaxArrayLength = 40 | |
| 10 | ||
| 11 | module.exports = exports = function inspect(value, opts = {}) { | |
| 12 | const { | |
| 13 | colors = false, | |
| 14 | depth = defaultDepth, | |
| 15 | breakLength = defaultBreakLength, | |
| 16 | stylize = defaultStylize(colors) | |
| 17 | } = opts | |
| 18 | ||
| 19 | const references = new InspectRefMap() | |
| 20 | ||
| 21 | const tree = inspectValue(value, 0, { | |
| 22 | colors, | |
| 23 | depth, | |
| 24 | breakLength, | |
| 25 | stylize, | |
| 26 | references | |
| 27 | }) | |
| 28 | ||
| 29 | return tree.toString() | |
| 30 | } | |
| 31 | ||
| 32 | exports.styles = { | |
| 33 | bigint: ansiEscapes.colorYellow, | |
| 34 | boolean: ansiEscapes.colorYellow, | |
| 35 | date: ansiEscapes.colorMagenta, | |
| 36 | module: ansiEscapes.modifierUnderline, | |
| 37 | name: ansiEscapes.modifierReset, | |
| 38 | null: ansiEscapes.modifierBold, | |
| 39 | number: ansiEscapes.colorYellow, | |
| 40 | regexp: ansiEscapes.colorRed, | |
| 41 | special: ansiEscapes.colorCyan, | |
| 42 | string: ansiEscapes.colorGreen, | |
| 43 | symbol: ansiEscapes.colorGreen, | |
| 44 | undefined: ansiEscapes.colorBrightBlack | |
| 45 | } | |
| 46 | ||
| 47 | const styles = exports.styles | |
| 48 | ||
| 49 | function defaultStylize(colors) { | |
| 50 | return function stylize(value, style) { | |
| 51 | const color = colors && styles[style] | |
| 52 | ||
| 53 | if (color) return color + value + ansiEscapes.modifierReset | |
| 54 | ||
| 55 | return value | |
| 56 | } | |
| 57 | } | |
| 58 | ||
| 59 | class InspectRefMap { | |
| 60 | constructor() { | |
| 61 | this.refs = new WeakMap() | |
| 62 | this.ids = new WeakMap() | |
| 63 | this.nextId = 1 | |
| 64 | } | |
| 65 | ||
| 66 | has(object) { | |
| 67 | return this.refs.has(object) | |
| 68 | } | |
| 69 | ||
| 70 | get(object) { | |
| 71 | return this.refs.get(object) || null | |
| 72 | } | |
| 73 | ||
| 74 | set(object, ref) { | |
| 75 | this.refs.set(object, ref) | |
| 76 | } | |
| 77 | ||
| 78 | id(object) { | |
| 79 | let id = this.ids.get(object) | |
| 80 | if (id) return id | |
| 81 | ||
| 82 | id = this.nextId++ | |
| 83 | this.ids.set(object, id) | |
| 84 | ||
| 85 | return id | |
| 86 | } | |
| 87 | } | |
| 88 | ||
| 89 | class InspectNode { | |
| 90 | constructor(depth, length, opts) { | |
| 91 | const { breakLength = defaultBreakLength, breakAlways = false } = opts | |
| 92 | ||
| 93 | this.depth = depth | |
| 94 | this.length = length | |
| 95 | this.breakLength = breakLength | |
| 96 | this.breakAlways = breakAlways | |
| 97 | } | |
| 98 | ||
| 99 | pad(n, string) { | |
| 100 | return string.padStart(n, ' ') | |
| 101 | } | |
| 102 | ||
| 103 | indent(n, string) { | |
| 104 | return ' '.repeat(n) + string | |
| 105 | } | |
| 106 | } | |
| 107 | ||
| 108 | class InspectRef extends InspectNode { | |
| 109 | constructor(depth, opts) { | |
| 110 | super(depth, '[circular *]'.length, opts) | |
| 111 | ||
| 112 | this.refs = opts.references | |
| 113 | this.count = 0 | |
| 114 | this.circular = false | |
| 115 | this.color = opts.colors && styles.special | |
| 116 | } | |
| 117 | ||
| 118 | get id() { | |
| 119 | return this.refs.id(this) | |
| 120 | } | |
| 121 | ||
| 122 | increment() { | |
| 123 | return ++this.count | |
| 124 | } | |
| 125 | ||
| 126 | decrement() { | |
| 127 | return --this.count | |
| 128 | } | |
| 129 | ||
| 130 | toString(opts = {}) { | |
| 131 | const { offset = 0, pad = 0, indent = 0 } = opts | |
| 132 | ||
| 133 | let value = this.pad(pad, '[circular *' + this.id + ']') | |
| 134 | ||
| 135 | if (this.color) value = this.color + value + ansiEscapes.modifierReset | |
| 136 | ||
| 137 | return offset ? value : this.indent(indent, value) | |
| 138 | } | |
| 139 | } | |
| 140 | ||
| 141 | class InspectLeaf extends InspectNode { | |
| 142 | constructor(value, color, depth, opts) { | |
| 143 | const length = value.length | |
| 144 | ||
| 145 | if (value.includes('\n')) { | |
| 146 | value = value.replaceAll('\n', '\n' + ' '.repeat(depth)) | |
| 147 | ||
| 148 | opts = { ...opts, breakAlways: true } | |
| 149 | } | |
| 150 | ||
| 151 | super(depth, length, opts) | |
| 152 | ||
| 153 | this.value = value | |
| 154 | this.color = opts.colors && color | |
| 155 | } | |
| 156 | ||
| 157 | toString(opts = {}) { | |
| 158 | const { offset = 0, pad = 0, indent = 0 } = opts | |
| 159 | ||
| 160 | let value = this.pad(pad, this.value) | |
| 161 | ||
| 162 | if (this.color) value = this.color + value + ansiEscapes.modifierReset | |
| 163 | ||
| 164 | return offset ? value : this.indent(indent, value) | |
| 165 | } | |
| 166 | } | |
| 167 | ||
| 168 | class InspectPair extends InspectNode { | |
| 169 | constructor(delim, left, right, depth, opts) { | |
| 170 | const length = left.length + delim.length + right.length | |
| 171 | ||
| 172 | if (left.breakAlways || right.breakAlways) { | |
| 173 | opts = { ...opts, breakAlways: true } | |
| 174 | } | |
| 175 | ||
| 176 | super(depth, length, opts) | |
| 177 | ||
| 178 | this.delim = delim | |
| 179 | this.left = left | |
| 180 | this.right = right | |
| 181 | } | |
| 182 | ||
| 183 | toString(opts = {}) { | |
| 184 | const { indent = 0 } = opts | |
| 185 | ||
| 186 | return this.indent( | |
| 187 | indent, | |
| 188 | this.left + | |
| 189 | this.delim + | |
| 190 | this.right.toString({ | |
| 191 | indent, | |
| 192 | offset: this.left.length + this.delim.length | |
| 193 | }) | |
| 194 | ) | |
| 195 | } | |
| 196 | } | |
| 197 | ||
| 198 | class InspectSuspension extends InspectNode { | |
| 199 | constructor(overflow, depth, opts) { | |
| 200 | const label = `... ${overflow} more` | |
| 201 | ||
| 202 | super(depth, label.length, opts) | |
| 203 | ||
| 204 | this.label = label | |
| 205 | } | |
| 206 | ||
| 207 | toString(opts = {}) { | |
| 208 | const { indent = 0 } = opts | |
| 209 | ||
| 210 | return this.indent(indent, this.label) | |
| 211 | } | |
| 212 | } | |
| 213 | ||
| 214 | class InspectSequence extends InspectNode { | |
| 215 | constructor(header, footer, delim, values, ref, depth, opts) { | |
| 216 | const { tabulate = false } = opts | |
| 217 | ||
| 218 | const length = | |
| 219 | (ref.circular ? '<ref *>'.length + 1 : 0) + | |
| 220 | header.length + | |
| 221 | values.reduce( | |
| 222 | (length, value, i) => | |
| 223 | length + value.length + (i === 0 ? 0 : delim.length), | |
| 224 | 0 | |
| 225 | ) + | |
| 226 | footer.length | |
| 227 | ||
| 228 | if (values.some((value) => value.breakAlways)) { | |
| 229 | opts = { ...opts, breakAlways: true } | |
| 230 | } | |
| 231 | ||
| 232 | super(depth, length, opts) | |
| 233 | ||
| 234 | this.header = header | |
| 235 | this.footer = footer | |
| 236 | this.delim = delim | |
| 237 | this.values = values | |
| 238 | this.ref = ref | |
| 239 | this.tabulate = tabulate | |
| 240 | } | |
| 241 | ||
| 242 | toString(opts = {}) { | |
| 243 | const { offset = 0, indent = 0 } = opts | |
| 244 | ||
| 245 | const split = | |
| 246 | this.breakAlways || | |
| 247 | (this.values.length && | |
| 248 | (offset + this.length > this.breakLength || | |
| 249 | indent * 2 + this.length > this.breakLength)) | |
| 250 | ||
| 251 | let header = this.header | |
| 252 | ||
| 253 | if (this.ref.circular) { | |
| 254 | header = '<ref *' + this.ref.id + '> ' + header | |
| 255 | } | |
| 256 | ||
| 257 | if (this.values.length === 0) { | |
| 258 | header = header.trimEnd() | |
| 259 | } | |
| 260 | ||
| 261 | if (offset === 0) { | |
| 262 | header = this.indent(indent, header) | |
| 263 | } | |
| 264 | ||
| 265 | if (split) { | |
| 266 | header = header.trimEnd() + '\n' | |
| 267 | } | |
| 268 | ||
| 269 | let string = header | |
| 270 | ||
| 271 | let columns = 1 | |
| 272 | let pad = 0 | |
| 273 | ||
| 274 | if (this.tabulate) { | |
| 275 | const widest = this.values.reduce( | |
| 276 | (length, value) => | |
| 277 | value.breakAlways ? length : Math.max(length, value.length), | |
| 278 | 0 | |
| 279 | ) | |
| 280 | ||
| 281 | if (widest) { | |
| 282 | columns = Math.max( | |
| 283 | columns, | |
| 284 | Math.floor( | |
| 285 | (this.breakLength - indent * 2) / (widest + this.delim.length) | |
| 286 | ) | |
| 287 | ) | |
| 288 | ||
| 289 | if (columns > 1) pad = widest | |
| 290 | } | |
| 291 | } | |
| 292 | ||
| 293 | for (let i = 0, n = this.values.length, offset = 0; i < n; i++) { | |
| 294 | const value = this.values[i] | |
| 295 | ||
| 296 | if (split) { | |
| 297 | let part | |
| 298 | ||
| 299 | if (i % columns === 0 || value.breakAlways) { | |
| 300 | part = value.toString({ indent: indent + 1, pad }) | |
| 301 | } else { | |
| 302 | part = value.toString({ pad }) | |
| 303 | } | |
| 304 | ||
| 305 | string += part | |
| 306 | ||
| 307 | if (i < n - 1) { | |
| 308 | if (i % columns === columns - 1 || this.values[i + 1].breakAlways) { | |
| 309 | string += this.delim.trimEnd() + '\n' | |
| 310 | } else { | |
| 311 | string += this.delim | |
| 312 | } | |
| 313 | } | |
| 314 | } else { | |
| 315 | if (i > 0) string += this.delim | |
| 316 | ||
| 317 | string += value.toString({ offset }) | |
| 318 | ||
| 319 | offset += value.length | |
| 320 | } | |
| 321 | } | |
| 322 | ||
| 323 | let footer = this.footer | |
| 324 | ||
| 325 | if (this.values.length === 0) { | |
| 326 | footer = footer.trimStart() | |
| 327 | } | |
| 328 | ||
| 329 | if (split) { | |
| 330 | string += '\n' + this.indent(indent, footer.trimStart()) | |
| 331 | } else { | |
| 332 | string += footer | |
| 333 | } | |
| 334 | ||
| 335 | return string | |
| 336 | } | |
| 337 | } | |
| 338 | ||
| 339 | function inspectValue(value, depth, opts) { | |
| 340 | const type = getType(value) | |
| 341 | ||
| 342 | if (type.isUndefined()) return inspectUndefined(depth, opts) | |
| 343 | if (type.isNull()) return inspectNull(depth, opts) | |
| 344 | if (type.isBoolean()) return inspectBoolean(value, depth, opts) | |
| 345 | if (type.isNumber()) return inspectNumber(value, depth, opts) | |
| 346 | if (type.isBigInt()) return inspectBigInt(value, depth, opts) | |
| 347 | if (type.isString()) return inspectString(value, depth, opts) | |
| 348 | if (type.isSymbol()) return inspectSymbol(value, depth, opts) | |
| 349 | if (type.isObject()) return inspectObject(type, value, depth, opts) | |
| 350 | if (type.isFunction()) return inspectFunction(type, value, depth, opts) | |
| 351 | if (type.isExternal()) return inspectExternal(value, opts, opts) | |
| 352 | } | |
| 353 | ||
| 354 | function inspectUndefined(depth, opts) { | |
| 355 | return new InspectLeaf('undefined', styles.undefined, depth, opts) | |
| 356 | } | |
| 357 | ||
| 358 | function inspectNull(depth, opts) { | |
| 359 | return new InspectLeaf('null', styles.null, depth, opts) | |
| 360 | } | |
| 361 | ||
| 362 | function inspectBoolean(value, depth, opts) { | |
| 363 | return new InspectLeaf(value.toString(), styles.boolean, depth, opts) | |
| 364 | } | |
| 365 | ||
| 366 | function inspectNumber(value, depth, opts) { | |
| 367 | let string | |
| 368 | ||
| 369 | if (Object.is(value, -0)) { | |
| 370 | string = '-0' | |
| 371 | } else { | |
| 372 | string = value.toString(10) | |
| 373 | } | |
| 374 | ||
| 375 | return new InspectLeaf(string, styles.number, depth, opts) | |
| 376 | } | |
| 377 | ||
| 378 | function inspectBigInt(value, depth, opts) { | |
| 379 | return new InspectLeaf(value.toString(10) + 'n', styles.bigint, depth, opts) | |
| 380 | } | |
| 381 | ||
| 382 | const STRING_ESCAPES = | |
| 383 | /[\ud800-\udbff][\udc00-\udfff]|[\u0000-\u001f'\\\ud800-\udfff]/g | |
| 384 | ||
| 385 | function inspectString(value, depth, opts) { | |
| 386 | // https://tc39.es/ecma262/multipage/structured-data.html#sec-quotejsonstring | |
| 387 | const string = value.replace(STRING_ESCAPES, (match) => { | |
| 388 | if (match.length === 2) return match | |
| 389 | ||
| 390 | switch (match) { | |
| 391 | case "'": | |
| 392 | return "\\'" | |
| 393 | case '\\': | |
| 394 | return '\\\\' | |
| 395 | case '\b': | |
| 396 | return '\\b' | |
| 397 | case '\t': | |
| 398 | return '\\t' | |
| 399 | case '\n': | |
| 400 | return '\\n' | |
| 401 | case '\f': | |
| 402 | return '\\f' | |
| 403 | case '\r': | |
| 404 | return '\\r' | |
| 405 | default: | |
| 406 | return '\\u' + match.charCodeAt(0).toString(16).padStart(4, '0') | |
| 407 | } | |
| 408 | }) | |
| 409 | ||
| 410 | return new InspectLeaf("'" + string + "'", styles.string, depth, opts) | |
| 411 | } | |
| 412 | ||
| 413 | function inspectSymbol(value, depth, opts) { | |
| 414 | return new InspectLeaf(value.toString(), styles.symbol, depth, opts) | |
| 415 | } | |
| 416 | ||
| 417 | function inspectKey(value, depth, opts) { | |
| 418 | if (PLAIN_KEY.test(value)) { | |
| 419 | return new InspectLeaf(value, null, depth, opts) | |
| 420 | } else { | |
| 421 | return inspectValue(value, depth, opts) | |
| 422 | } | |
| 423 | } | |
| 424 | ||
| 425 | function inspectObject(type, object, depth, opts) { | |
| 426 | const refs = opts.references | |
| 427 | ||
| 428 | let ref = refs.get(object) | |
| 429 | if (ref === null) { | |
| 430 | ref = new InspectRef(depth, opts) | |
| 431 | refs.set(object, ref) | |
| 432 | } else if (ref.count) { | |
| 433 | ref.circular = true | |
| 434 | return ref | |
| 435 | } | |
| 436 | ||
| 437 | const maxDepth = typeof opts.depth === 'number' ? opts.depth : Infinity | |
| 438 | ||
| 439 | if (maxDepth < depth) { | |
| 440 | const constructor = object.constructor | |
| 441 | ||
| 442 | return new InspectLeaf( | |
| 443 | '[' + | |
| 444 | (constructor && constructor.name ? constructor.name : 'Object') + | |
| 445 | ']', | |
| 446 | styles.special, | |
| 447 | depth, | |
| 448 | opts | |
| 449 | ) | |
| 450 | } | |
| 451 | ||
| 452 | const inspect = | |
| 453 | object[Symbol.for('bare.inspect')] || | |
| 454 | object[Symbol.for('nodejs.util.inspect.custom')] | |
| 455 | ||
| 456 | if (typeof inspect === 'function') { | |
| 457 | const value = inspect.call( | |
| 458 | object, | |
| 459 | typeof opts.depth === 'number' ? opts.depth - depth : null, | |
| 460 | { | |
| 461 | colors: opts.colors, | |
| 462 | breakLength: opts.breakLength, | |
| 463 | stylize: opts.stylize | |
| 464 | }, | |
| 465 | exports | |
| 466 | ) | |
| 467 | ||
| 468 | if (typeof value === 'object' && value !== null) { | |
| 469 | refs.set(value, ref) | |
| 470 | } | |
| 471 | ||
| 472 | if (typeof value !== 'string') { | |
| 473 | return inspectValue(value, depth, opts) | |
| 474 | } | |
| 475 | ||
| 476 | return new InspectLeaf(value, null, depth, opts) | |
| 477 | } | |
| 478 | ||
| 479 | if (type.isArray()) return inspectArray(object, ref, depth, opts) | |
| 480 | if (type.isDate()) return inspectDate(object, ref, depth, opts) | |
| 481 | if (type.isRegExp()) return inspectRegExp(object, ref, depth, opts) | |
| 482 | if (type.isError()) return inspectError(object, ref, depth, opts) | |
| 483 | if (type.isPromise()) return inspectPromise(object, ref, depth, opts) | |
| 484 | if (type.isMap()) return inspectMap(object, ref, depth, opts) | |
| 485 | if (type.isSet()) return inspectSet(object, ref, depth, opts) | |
| 486 | if (type.isWeakMap()) return inspectWeakMap(object, ref, depth, opts) | |
| 487 | if (type.isWeakSet()) return inspectWeakSet(object, ref, depth, opts) | |
| 488 | if (type.isWeakRef()) return inspectWeakRef(object, ref, depth, opts) | |
| 489 | if (type.isArrayBuffer()) return inspectArrayBuffer(object, ref, depth, opts) | |
| 490 | if (type.isSharedArrayBuffer()) | |
| 491 | return inspectSharedArrayBuffer(object, ref, depth, opts) | |
| 492 | if (type.isTypedArray()) return inspectTypedArray(object, ref, depth, opts) | |
| 493 | if (type.isDataView()) return inspectDataView(object, ref, depth, opts) | |
| 494 | ||
| 495 | ref.increment() | |
| 496 | ||
| 497 | const values = [] | |
| 498 | ||
| 499 | for (const key in object) { | |
| 500 | if (key === 'constructor') continue | |
| 501 | ||
| 502 | values.push( | |
| 503 | new InspectPair( | |
| 504 | ': ', | |
| 505 | inspectKey(key, depth + 1, opts), | |
| 506 | inspectValue(object[key], depth + 1, opts), | |
| 507 | depth + 1, | |
| 508 | opts | |
| 509 | ) | |
| 510 | ) | |
| 511 | } | |
| 512 | ||
| 513 | ref.decrement() | |
| 514 | ||
| 515 | let header = '{ ' | |
| 516 | ||
| 517 | const tag = object[Symbol.toStringTag] | |
| 518 | ||
| 519 | if (tag) header = '[' + tag + '] ' + header | |
| 520 | ||
| 521 | if (object.constructor) { | |
| 522 | const name = object.constructor.name | |
| 523 | ||
| 524 | if (name && name !== 'Object') { | |
| 525 | header = object.constructor.name + ' ' + header | |
| 526 | } | |
| 527 | } | |
| 528 | ||
| 529 | return new InspectSequence(header, ' }', ', ', values, ref, depth, opts) | |
| 530 | } | |
| 531 | ||
| 532 | function inspectArray(array, ref, depth, opts) { | |
| 533 | const { maxArrayLength = defaultMaxArrayLength } = opts | |
| 534 | ||
| 535 | ref.increment() | |
| 536 | ||
| 537 | const values = [] | |
| 538 | ||
| 539 | let remaining = Math.max(maxArrayLength, 0) | |
| 540 | ||
| 541 | for (let i = 0, n = array.length; i < n; i++) { | |
| 542 | if (remaining-- === 0) { | |
| 543 | values.push( | |
| 544 | new InspectSuspension(array.length - values.length, depth + 1, { | |
| 545 | ...opts, | |
| 546 | breakAlways: true | |
| 547 | }) | |
| 548 | ) | |
| 549 | ||
| 550 | break | |
| 551 | } | |
| 552 | ||
| 553 | values.push(inspectValue(array[i], depth + 1, opts)) | |
| 554 | } | |
| 555 | ||
| 556 | for (const key of binding.getOwnNonIndexPropertyNames(array)) { | |
| 557 | values.push( | |
| 558 | new InspectPair( | |
| 559 | ': ', | |
| 560 | inspectKey(key, depth + 1, opts), | |
| 561 | inspectValue(array[key], depth + 1, opts), | |
| 562 | depth + 1, | |
| 563 | { ...opts, breakAlways: remaining < 0 } | |
| 564 | ) | |
| 565 | ) | |
| 566 | } | |
| 567 | ||
| 568 | ref.decrement() | |
| 569 | ||
| 570 | let header = '[ ' | |
| 571 | ||
| 572 | if (array.constructor.name !== 'Array') { | |
| 573 | header = array.constructor.name + '(' + array.length + ') ' + header | |
| 574 | } | |
| 575 | ||
| 576 | return new InspectSequence(header, ' ]', ', ', values, ref, depth, { | |
| 577 | ...opts, | |
| 578 | tabulate: true | |
| 579 | }) | |
| 580 | } | |
| 581 | ||
| 582 | function inspectDate(date, ref, depth, opts) { | |
| 583 | return new InspectLeaf(date.toISOString(), styles.date, depth, opts) | |
| 584 | } | |
| 585 | ||
| 586 | function inspectRegExp(regExp, ref, depth, opts) { | |
| 587 | return new InspectLeaf(regExp.toString(), styles.regexp, depth, opts) | |
| 588 | } | |
| 589 | ||
| 590 | function inspectError(error, ref, depth, opts) { | |
| 591 | let header | |
| 592 | ||
| 593 | if ('stack' in error) { | |
| 594 | header = error.stack | |
| 595 | ||
| 596 | if (depth > 0) { | |
| 597 | header = header.replaceAll('\n', '\n' + ' '.repeat(depth)) | |
| 598 | } | |
| 599 | } else { | |
| 600 | header = error.toString() | |
| 601 | } | |
| 602 | ||
| 603 | const builtins = ['cause'] | |
| 604 | ||
| 605 | if (error.name === 'AggregateError') { | |
| 606 | builtins.push('errors') | |
| 607 | } else if (error.name === 'SuppressedError') { | |
| 608 | builtins.push('error', 'suppressed') | |
| 609 | } | |
| 610 | ||
| 611 | const values = [] | |
| 612 | ||
| 613 | for (const key of builtins) { | |
| 614 | if (key in error === false) continue | |
| 615 | ||
| 616 | values.push( | |
| 617 | new InspectPair( | |
| 618 | ': ', | |
| 619 | new InspectLeaf('[' + key + ']', null, depth + 1, opts), | |
| 620 | inspectValue(error[key], depth + 1, opts), | |
| 621 | depth + 1, | |
| 622 | opts | |
| 623 | ) | |
| 624 | ) | |
| 625 | } | |
| 626 | ||
| 627 | for (const key in error) { | |
| 628 | if (key === 'constructor' || builtins.includes(key)) continue | |
| 629 | ||
| 630 | values.push( | |
| 631 | new InspectPair( | |
| 632 | ': ', | |
| 633 | inspectKey(key, depth + 1, opts), | |
| 634 | inspectValue(error[key], depth + 1, opts), | |
| 635 | depth + 1, | |
| 636 | opts | |
| 637 | ) | |
| 638 | ) | |
| 639 | } | |
| 640 | ||
| 641 | if (values.length === 0) return new InspectLeaf(header, null, depth, opts) | |
| 642 | ||
| 643 | return new InspectSequence( | |
| 644 | header + ' {', | |
| 645 | ' }', | |
| 646 | ', ', | |
| 647 | values, | |
| 648 | ref, | |
| 649 | depth, | |
| 650 | opts | |
| 651 | ) | |
| 652 | } | |
| 653 | ||
| 654 | function inspectPromise(promise, ref, depth, opts) { | |
| 655 | ref.increment() | |
| 656 | ||
| 657 | const state = binding.getPromiseState(promise) | |
| 658 | ||
| 659 | const values = [] | |
| 660 | ||
| 661 | switch (state) { | |
| 662 | case 0: // Pending | |
| 663 | values.push(new InspectLeaf('<pending>', styles.special, depth, opts)) | |
| 664 | break | |
| 665 | ||
| 666 | case 1: // Fulfilled | |
| 667 | values.push(inspectValue(binding.getPromiseResult(promise), depth, opts)) | |
| 668 | break | |
| 669 | ||
| 670 | case 2: // Rejected | |
| 671 | values.push( | |
| 672 | new InspectLeaf('<rejected>', styles.special, depth, opts), | |
| 673 | inspectValue(binding.getPromiseResult(promise), depth, opts) | |
| 674 | ) | |
| 675 | } | |
| 676 | ||
| 677 | ref.decrement() | |
| 678 | ||
| 679 | const header = promise.constructor.name + ' { ' | |
| 680 | ||
| 681 | return new InspectSequence(header, ' }', ' ', values, ref, depth, opts) | |
| 682 | } | |
| 683 | ||
| 684 | function inspectMap(map, ref, depth, opts) { | |
| 685 | const { | |
| 686 | maxArrayLength = defaultMaxArrayLength, | |
| 687 | maxMapLength = maxArrayLength | |
| 688 | } = opts | |
| 689 | ||
| 690 | ref.increment() | |
| 691 | ||
| 692 | const values = [] | |
| 693 | ||
| 694 | let remaining = maxMapLength | |
| 695 | ||
| 696 | for (const entry of map) { | |
| 697 | if (remaining-- === 0) { | |
| 698 | values.push( | |
| 699 | new InspectSuspension(map.size - values.length, depth + 1, { | |
| 700 | ...opts, | |
| 701 | breakAlways: true | |
| 702 | }) | |
| 703 | ) | |
| 704 | break | |
| 705 | } | |
| 706 | ||
| 707 | values.push( | |
| 708 | new InspectPair( | |
| 709 | ' => ', | |
| 710 | inspectValue(entry[0], depth + 1, opts), | |
| 711 | inspectValue(entry[1], depth + 1, opts), | |
| 712 | depth + 1, | |
| 713 | opts | |
| 714 | ) | |
| 715 | ) | |
| 716 | } | |
| 717 | ||
| 718 | for (const key in map) { | |
| 719 | if (key === 'constructor') continue | |
| 720 | ||
| 721 | const value = inspectValue(map[key], depth + 1, opts) | |
| 722 | ||
| 723 | values.push( | |
| 724 | new InspectPair( | |
| 725 | ': ', | |
| 726 | inspectKey(key, depth + 1, opts), | |
| 727 | value, | |
| 728 | depth + 1, | |
| 729 | { ...opts, breakAlways: remaining < 0 } | |
| 730 | ) | |
| 731 | ) | |
| 732 | } | |
| 733 | ||
| 734 | ref.decrement() | |
| 735 | ||
| 736 | const header = map.constructor.name + '(' + map.size + ') { ' | |
| 737 | ||
| 738 | return new InspectSequence(header, ' }', ', ', values, ref, depth, { | |
| 739 | ...opts, | |
| 740 | tabulate: true | |
| 741 | }) | |
| 742 | } | |
| 743 | ||
| 744 | function inspectSet(set, ref, depth, opts) { | |
| 745 | const { | |
| 746 | maxArrayLength = defaultMaxArrayLength, | |
| 747 | maxSetLength = maxArrayLength | |
| 748 | } = opts | |
| 749 | ||
| 750 | ref.increment() | |
| 751 | ||
| 752 | const values = [] | |
| 753 | ||
| 754 | let remaining = maxSetLength | |
| 755 | ||
| 756 | for (const entry of set) { | |
| 757 | if (remaining-- === 0) { | |
| 758 | values.push( | |
| 759 | new InspectSuspension(set.size - values.length, depth + 1, { | |
| 760 | ...opts, | |
| 761 | breakAlways: true | |
| 762 | }) | |
| 763 | ) | |
| 764 | break | |
| 765 | } | |
| 766 | ||
| 767 | values.push(inspectValue(entry, depth + 1, opts)) | |
| 768 | } | |
| 769 | ||
| 770 | for (const key in set) { | |
| 771 | if (key === 'constructor') continue | |
| 772 | ||
| 773 | const value = inspectValue(set[key], depth + 1, opts) | |
| 774 | ||
| 775 | values.push( | |
| 776 | new InspectPair( | |
| 777 | ': ', | |
| 778 | inspectKey(key, depth + 1, opts), | |
| 779 | value, | |
| 780 | depth + 1, | |
| 781 | { ...opts, breakAlways: remaining < 0 } | |
| 782 | ) | |
| 783 | ) | |
| 784 | } | |
| 785 | ||
| 786 | ref.decrement() | |
| 787 | ||
| 788 | const header = set.constructor.name + '(' + set.size + ') { ' | |
| 789 | ||
| 790 | return new InspectSequence(header, ' }', ', ', values, ref, depth, { | |
| 791 | ...opts, | |
| 792 | tabulate: true | |
| 793 | }) | |
| 794 | } | |
| 795 | ||
| 796 | function inspectWeakMap(weakMap, ref, depth, opts) { | |
| 797 | const header = weakMap.constructor.name + ' { ' | |
| 798 | ||
| 799 | return new InspectSequence( | |
| 800 | header, | |
| 801 | ' }', | |
| 802 | ' ', | |
| 803 | [new InspectLeaf('<items unknown>', styles.special, depth + 1, opts)], | |
| 804 | ref, | |
| 805 | depth, | |
| 806 | opts | |
| 807 | ) | |
| 808 | } | |
| 809 | ||
| 810 | function inspectWeakSet(weakSet, ref, depth, opts) { | |
| 811 | const header = weakSet.constructor.name + ' { ' | |
| 812 | ||
| 813 | return new InspectSequence( | |
| 814 | header, | |
| 815 | ' }', | |
| 816 | ' ', | |
| 817 | [new InspectLeaf('<items unknown>', styles.special, depth + 1, opts)], | |
| 818 | ref, | |
| 819 | depth, | |
| 820 | opts | |
| 821 | ) | |
| 822 | } | |
| 823 | ||
| 824 | function inspectWeakRef(weakRef, ref, depth, opts) { | |
| 825 | const target = weakRef.deref() | |
| 826 | ||
| 827 | let value | |
| 828 | ||
| 829 | if (target === undefined) { | |
| 830 | value = new InspectLeaf('<cleared>', styles.special, depth + 1, opts) | |
| 831 | } else { | |
| 832 | value = inspectValue(target, depth + 1, opts) | |
| 833 | } | |
| 834 | ||
| 835 | const header = weakRef.constructor.name + ' { ' | |
| 836 | ||
| 837 | return new InspectSequence(header, ' }', ' ', [value], ref, depth, opts) | |
| 838 | } | |
| 839 | ||
| 840 | function inspectArrayBuffer(arrayBuffer, ref, depth, opts) { | |
| 841 | ref.increment() | |
| 842 | ||
| 843 | const values = [] | |
| 844 | ||
| 845 | for (const key of ['byteLength']) { | |
| 846 | values.push( | |
| 847 | new InspectPair( | |
| 848 | ': ', | |
| 849 | inspectKey(key, depth + 1, opts), | |
| 850 | inspectValue(arrayBuffer[key], depth + 1, opts), | |
| 851 | depth + 1, | |
| 852 | opts | |
| 853 | ) | |
| 854 | ) | |
| 855 | } | |
| 856 | ||
| 857 | ref.decrement() | |
| 858 | ||
| 859 | const header = arrayBuffer.constructor.name + ' { ' | |
| 860 | ||
| 861 | return new InspectSequence(header, ' }', ', ', values, ref, depth, opts) | |
| 862 | } | |
| 863 | ||
| 864 | function inspectSharedArrayBuffer(sharedArrayBuffer, ref, depth, opts) { | |
| 865 | ref.increment() | |
| 866 | ||
| 867 | const values = [] | |
| 868 | ||
| 869 | for (const key of ['byteLength']) { | |
| 870 | values.push( | |
| 871 | new InspectPair( | |
| 872 | ': ', | |
| 873 | inspectKey(key, depth + 1, opts), | |
| 874 | inspectValue(sharedArrayBuffer[key], depth + 1, opts), | |
| 875 | depth + 1, | |
| 876 | opts | |
| 877 | ) | |
| 878 | ) | |
| 879 | } | |
| 880 | ||
| 881 | ref.decrement() | |
| 882 | ||
| 883 | const header = sharedArrayBuffer.constructor.name + ' { ' | |
| 884 | ||
| 885 | return new InspectSequence(header, ' }', ', ', values, ref, depth, opts) | |
| 886 | } | |
| 887 | ||
| 888 | function inspectTypedArray(typedArray, ref, depth, opts) { | |
| 889 | if (Buffer.isBuffer(typedArray)) { | |
| 890 | return inspectBuffer(typedArray, ref, depth, opts) | |
| 891 | } | |
| 892 | ||
| 893 | const { | |
| 894 | maxArrayLength = defaultMaxArrayLength, | |
| 895 | maxTypedArrayLength = maxArrayLength | |
| 896 | } = opts | |
| 897 | ||
| 898 | ref.increment() | |
| 899 | ||
| 900 | const values = [] | |
| 901 | ||
| 902 | let remaining = Math.max(maxTypedArrayLength, 0) | |
| 903 | ||
| 904 | for (let i = 0, n = typedArray.length; i < n; i++) { | |
| 905 | if (remaining-- === 0) { | |
| 906 | values.push( | |
| 907 | new InspectSuspension(typedArray.length - values.length, depth + 1, { | |
| 908 | ...opts, | |
| 909 | breakAlways: true | |
| 910 | }) | |
| 911 | ) | |
| 912 | ||
| 913 | break | |
| 914 | } | |
| 915 | ||
| 916 | values.push(inspectValue(typedArray[i], depth + 1, opts)) | |
| 917 | } | |
| 918 | ||
| 919 | for (const key of binding.getOwnNonIndexPropertyNames(typedArray)) { | |
| 920 | values.push( | |
| 921 | new InspectPair( | |
| 922 | ': ', | |
| 923 | inspectKey(key, depth + 1, opts), | |
| 924 | inspectValue(typedArray[key], depth + 1, opts), | |
| 925 | depth + 1, | |
| 926 | { ...opts, breakAlways: remaining < 0 } | |
| 927 | ) | |
| 928 | ) | |
| 929 | } | |
| 930 | ||
| 931 | ref.decrement() | |
| 932 | ||
| 933 | const header = typedArray.constructor.name + '(' + typedArray.length + ') [ ' | |
| 934 | ||
| 935 | return new InspectSequence(header, ' ]', ', ', values, ref, depth, { | |
| 936 | ...opts, | |
| 937 | tabulate: true | |
| 938 | }) | |
| 939 | } | |
| 940 | ||
| 941 | function inspectBuffer(buffer, ref, depth, opts) { | |
| 942 | const { | |
| 943 | maxArrayLength = defaultMaxArrayLength, | |
| 944 | maxBufferLength = maxArrayLength | |
| 945 | } = opts | |
| 946 | ||
| 947 | ref.increment() | |
| 948 | ||
| 949 | const values = [] | |
| 950 | ||
| 951 | let remaining = Math.max(maxBufferLength, 0) | |
| 952 | ||
| 953 | for (let i = 0, n = buffer.byteLength; i < n; i++) { | |
| 954 | if (remaining-- === 0) { | |
| 955 | values.push( | |
| 956 | new InspectSuspension(buffer.length - values.length, depth + 1, { | |
| 957 | ...opts, | |
| 958 | breakAlways: true | |
| 959 | }) | |
| 960 | ) | |
| 961 | ||
| 962 | break | |
| 963 | } | |
| 964 | ||
| 965 | values.push( | |
| 966 | new InspectLeaf( | |
| 967 | buffer[i].toString(16).padStart(2, '0'), | |
| 968 | null, | |
| 969 | depth + 1, | |
| 970 | opts | |
| 971 | ) | |
| 972 | ) | |
| 973 | } | |
| 974 | ||
| 975 | for (const key of binding.getOwnNonIndexPropertyNames(buffer)) { | |
| 976 | values.push( | |
| 977 | new InspectPair( | |
| 978 | ': ', | |
| 979 | inspectKey(key, depth + 1, opts), | |
| 980 | inspectValue(buffer[key], depth + 1, opts), | |
| 981 | depth + 1, | |
| 982 | { ...opts, breakAlways: remaining < 0 } | |
| 983 | ) | |
| 984 | ) | |
| 985 | } | |
| 986 | ||
| 987 | ref.decrement() | |
| 988 | ||
| 989 | return new InspectSequence('<Buffer ', '>', ' ', values, ref, depth, { | |
| 990 | ...opts, | |
| 991 | tabulate: true | |
| 992 | }) | |
| 993 | } | |
| 994 | ||
| 995 | function inspectDataView(dataView, ref, depth, opts) { | |
| 996 | ref.increment() | |
| 997 | ||
| 998 | const values = [] | |
| 999 | ||
| 1000 | for (const key of ['byteLength', 'byteOffset', 'buffer']) { | |
| 1001 | values.push( | |
| 1002 | new InspectPair( | |
| 1003 | ': ', | |
| 1004 | inspectKey(key, depth + 1, opts), | |
| 1005 | inspectValue(dataView[key], depth + 1, opts), | |
| 1006 | depth + 1, | |
| 1007 | opts | |
| 1008 | ) | |
| 1009 | ) | |
| 1010 | } | |
| 1011 | ||
| 1012 | for (const key of binding.getOwnNonIndexPropertyNames(dataView)) { | |
| 1013 | values.push( | |
| 1014 | new InspectPair( | |
| 1015 | ': ', | |
| 1016 | inspectKey(key, depth + 1, opts), | |
| 1017 | inspectValue(dataView[key], depth + 1, opts), | |
| 1018 | depth + 1, | |
| 1019 | opts | |
| 1020 | ) | |
| 1021 | ) | |
| 1022 | } | |
| 1023 | ||
| 1024 | ref.decrement() | |
| 1025 | ||
| 1026 | const header = dataView.constructor.name + ' { ' | |
| 1027 | ||
| 1028 | return new InspectSequence(header, ' }', ', ', values, ref, depth, opts) | |
| 1029 | } | |
| 1030 | ||
| 1031 | function inspectFunction(type, fn, depth, opts) { | |
| 1032 | if (fn.toString().startsWith('class')) return inspectClass(fn, depth, opts) | |
| 1033 | ||
| 1034 | let tag = 'function' | |
| 1035 | ||
| 1036 | if (type.isGeneratorFunction()) tag = 'generator ' + tag | |
| 1037 | if (type.isAsyncFunction()) tag = 'async ' + tag | |
| 1038 | ||
| 1039 | return new InspectLeaf( | |
| 1040 | '[' + tag + ' ' + (fn.name ? fn.name : '(anonymous)') + ']', | |
| 1041 | styles.special, | |
| 1042 | depth, | |
| 1043 | opts | |
| 1044 | ) | |
| 1045 | } | |
| 1046 | ||
| 1047 | function inspectClass(ctor, depth, opts) { | |
| 1048 | return new InspectLeaf( | |
| 1049 | '[class ' + (ctor.name ? ctor.name : '(anonymous)') + ']', | |
| 1050 | styles.special, | |
| 1051 | depth, | |
| 1052 | opts | |
| 1053 | ) | |
| 1054 | } | |
| 1055 | ||
| 1056 | function inspectExternal(external, depth, opts) { | |
| 1057 | return new InspectLeaf( | |
| 1058 | '[external 0x' + binding.getExternal(external).toString(16) + ']', | |
| 1059 | styles.special, | |
| 1060 | depth, | |
| 1061 | opts | |
| 1062 | ) | |
| 1063 | } |
| … | … | @@ -0,0 +1,47 @@ |
| 1 | { | |
| 2 | "name": "bare-inspect", | |
| 3 | "version": "3.1.4", | |
| 4 | "description": "Inspect objects as strings for debugging", | |
| 5 | "exports": { | |
| 6 | ".": { | |
| 7 | "types": "./index.d.ts", | |
| 8 | "default": "./index.js" | |
| 9 | }, | |
| 10 | "./package": "./package.json" | |
| 11 | }, | |
| 12 | "files": [ | |
| 13 | "index.js", | |
| 14 | "index.d.ts", | |
| 15 | "binding.c", | |
| 16 | "binding.js", | |
| 17 | "CMakeLists.txt", | |
| 18 | "prebuilds" | |
| 19 | ], | |
| 20 | "addon": true, | |
| 21 | "scripts": { | |
| 22 | "test": "prettier . --check && bare test.js" | |
| 23 | }, | |
| 24 | "repository": { | |
| 25 | "type": "git", | |
| 26 | "url": "git+https://github.com/holepunchto/bare-inspect.git" | |
| 27 | }, | |
| 28 | "author": "Holepunch", | |
| 29 | "license": "Apache-2.0", | |
| 30 | "bugs": { | |
| 31 | "url": "https://github.com/holepunchto/bare-inspect/issues" | |
| 32 | }, | |
| 33 | "homepage": "https://github.com/holepunchto/bare-inspect#readme", | |
| 34 | "engines": { | |
| 35 | "bare": ">=1.18.0" | |
| 36 | }, | |
| 37 | "dependencies": { | |
| 38 | "bare-ansi-escapes": "^2.1.0", | |
| 39 | "bare-type": "^1.0.0" | |
| 40 | }, | |
| 41 | "devDependencies": { | |
| 42 | "brittle": "^3.3.2", | |
| 43 | "cmake-bare": "^1.1.6", | |
| 44 | "prettier": "^3.4.1", | |
| 45 | "prettier-config-standard": "^7.0.0" | |
| 46 | } | |
| 47 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,352 @@ |
| 1 | # bare-module-resolve | |
| 2 | ||
| 3 | Low-level module resolution algorithm for Bare. The algorithm is implemented as a generator function that yields either package manifests to be read or resolution candidates to be tested by the caller. As a convenience, the main export is a synchronous and asynchronous iterable that relies on package manifests being read by a callback. For asynchronous iteration, the callback may return promises which will be awaited before being passed to the generator. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i bare-module-resolve | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | For synchronous resolution: | |
| 12 | ||
| 13 | ```js | |
| 14 | const resolve = require('bare-module-resolve') | |
| 15 | ||
| 16 | function readPackage(url) { | |
| 17 | // Read and parse `url` if it exists, otherwise `null` | |
| 18 | } | |
| 19 | ||
| 20 | for (const resolution of resolve('./file.js', new URL('file:///directory/'), readPackage)) { | |
| 21 | console.log(resolution) | |
| 22 | } | |
| 23 | ``` | |
| 24 | ||
| 25 | For asynchronous resolution: | |
| 26 | ||
| 27 | ```js | |
| 28 | const resolve = require('bare-module-resolve') | |
| 29 | ||
| 30 | async function readPackage(url) { | |
| 31 | // Read and parse `url` if it exists, otherwise `null` | |
| 32 | } | |
| 33 | ||
| 34 | for await (const resolution of resolve('./file.js', new URL('file:///directory/'), readPackage)) { | |
| 35 | console.log(resolution) | |
| 36 | } | |
| 37 | ``` | |
| 38 | ||
| 39 | ## API | |
| 40 | ||
| 41 | #### `const resolver = resolve(specifier, parentURL[, options][, readPackage])` | |
| 42 | ||
| 43 | Resolve `specifier` relative to `parentURL`, which must be a WHATWG `URL` instance. `readPackage` is called with a `URL` instance for every package manifest to be read and must either return the parsed JSON package manifest, if it exists, or `null`. If `readPackage` returns a promise, synchronous iteration is not supported. | |
| 44 | ||
| 45 | Options include: | |
| 46 | ||
| 47 | ```js | |
| 48 | options = { | |
| 49 | // A default "imports" map to apply to all specifiers. Follows the same | |
| 50 | // syntax and rules as the "imports" property defined in `package.json`. | |
| 51 | imports, | |
| 52 | // A list of builtin module specifiers. If matched, the protocol of the | |
| 53 | // resolved URL will be `builtinProtocol`. | |
| 54 | builtins: [], | |
| 55 | // The protocol to use for resolved builtin module specifiers. | |
| 56 | builtinProtocol: 'builtin:', | |
| 57 | // A list of module specifiers whose resolution should be deferred. If matched, | |
| 58 | // the protocol of the resolved URL will be `deferredProtocol`. | |
| 59 | defer: [], | |
| 60 | // The protocol to use for resolved deferred module specifiers. | |
| 61 | deferredProtocol: 'deferred:', | |
| 62 | // The supported import conditions. "default" is always recognized. | |
| 63 | conditions: [], | |
| 64 | // An array reference which will contain the matched conditions when yielding | |
| 65 | // resolutions. | |
| 66 | matchedConditions: [], | |
| 67 | // The supported engine versions. | |
| 68 | engines: {}, | |
| 69 | // The file extensions to look for. Must be provided to support extensionless | |
| 70 | // specifier resolution and directory support, such as resolving './foo' to | |
| 71 | // './foo.js' or './foo/index.js'. | |
| 72 | extensions: [], | |
| 73 | // A map of preresolved imports with keys being serialized parent URLs and | |
| 74 | // values being "imports" maps. | |
| 75 | resolutions | |
| 76 | } | |
| 77 | ``` | |
| 78 | ||
| 79 | #### `for (const resolution of resolver)` | |
| 80 | ||
| 81 | Synchronously iterate the module resolution candidates. The resolved module is the first candidate that exists, either as a file on a file system, a resource at a URL, or something else entirely. | |
| 82 | ||
| 83 | #### `for await (const resolution of resolver)` | |
| 84 | ||
| 85 | Asynchronously iterate the module resolution candidates. If `readPackage` returns promises, these will be awaited. The same comments as `for (const resolution of resolver)` apply. | |
| 86 | ||
| 87 | ### Algorithm | |
| 88 | ||
| 89 | The following generator functions implement the resolution algorithm, which has been adapted from the Node.js resolution algorithms for CommonJS and ES modules. Unlike Node.js, Bare uses the same resolution algorithm for both module formats. The yielded values have the following shape: | |
| 90 | ||
| 91 | **Package manifest** | |
| 92 | ||
| 93 | ```js | |
| 94 | next.value = { | |
| 95 | package: URL | |
| 96 | } | |
| 97 | ``` | |
| 98 | ||
| 99 | If the package manifest identified by `next.value.package` exists, `generator.next()` must be passed the parsed JSON value of the manifest. If it does not exist, pass `null` instead. | |
| 100 | ||
| 101 | **Resolution candidate** | |
| 102 | ||
| 103 | ```js | |
| 104 | next.value = { | |
| 105 | resolution: URL | |
| 106 | } | |
| 107 | ``` | |
| 108 | ||
| 109 | If the module identified by `next.value.resolution` exists, `generator.next()` may be passed `true` to signal that the resolution for the current set of conditions has been identified. If it does not exist, pass `false` instead. | |
| 110 | ||
| 111 | To drive the generator functions, a loop like the following can be used: | |
| 112 | ||
| 113 | ```js | |
| 114 | const generator = resolve.module(specifier, parentURL) | |
| 115 | ||
| 116 | let next = generator.next() | |
| 117 | ||
| 118 | while (next.done !== true) { | |
| 119 | const value = next.value | |
| 120 | ||
| 121 | if (value.package) { | |
| 122 | // Read and parse `value.package` if it exists, otherwise `null` | |
| 123 | let info | |
| 124 | ||
| 125 | next = generator.next(info) | |
| 126 | } else { | |
| 127 | const resolution = value.resolution | |
| 128 | ||
| 129 | // `true` if `resolution` was the correct candidate, otherwise `false` | |
| 130 | let resolved | |
| 131 | ||
| 132 | next = generator.next(resolved) | |
| 133 | } | |
| 134 | } | |
| 135 | ``` | |
| 136 | ||
| 137 | Options are the same as `resolve()` for all functions. | |
| 138 | ||
| 139 | > [!WARNING] | |
| 140 | > These functions are currently subject to change between minor releases. If using them directly, make sure to specify a tilde range (`~1.2.3`) when declaring the module dependency. | |
| 141 | ||
| 142 | #### `const generator = resolve.module(specifier, parentURL[, options])` | |
| 143 | ||
| 144 | 1. If `specifier` [starts with a Windows drive letter](https://url.spec.whatwg.org/#start-with-a-windows-drive-letter): | |
| 145 | 1. Prepend `/` to `specifier`. | |
| 146 | 2. If `options.resolutions` is set: | |
| 147 | 1. If `preresolved(specifier, options.resolutions, parentURL, options)` yields, return. | |
| 148 | 3. If `url(specifier, parentURL, options)` yields, return. | |
| 149 | 4. If `packageImports(specifier, parentURL, options)` yields, return. | |
| 150 | 5. If `specifier` equals `.` or `..`, or if `specifier` starts with `/`, `\`, `./`, `.\`, `../`, or `..\`: | |
| 151 | 1. If `options.imports` is set: | |
| 152 | 1. If `packageImportsExports(specifier, options.imports, parentURL, true, options)` yields, return. | |
| 153 | 2. If `deferred(specifier, options)` yields, return. | |
| 154 | 3. If `file(specifier, parentURL, false, options)` resolves, return. | |
| 155 | 4. Return `directory(specifier, parentURL, options)`. | |
| 156 | 6. Return `package(specifier, parentURL, options)`. | |
| 157 | ||
| 158 | #### `const generator = resolve.url(url, parentURL[, options])` | |
| 159 | ||
| 160 | 1. If `url` is not a valid URL, return. | |
| 161 | 2. If `options.imports` is set: | |
| 162 | 1. If `packageImportsExports(url.href, options.imports, parentURL, true, options)` yields, return. | |
| 163 | 3. If `url.protocol` equals `options.deferredProtocol`: | |
| 164 | 1. Let `specifier` be `url.pathname`. | |
| 165 | 2. If `options.resolutions` is set: | |
| 166 | 1. Let `imports` be `options.resolutions[parentURL]`. | |
| 167 | 2. If `imports` is a non-`null` object: | |
| 168 | 1. Set `imports[specifier]` to `null`. | |
| 169 | 3. Return `module(specifier, parentURL, options)`. | |
| 170 | 4. If `url.protocol` equals `node:`: | |
| 171 | 1. Let `specifier` be `url.pathname`. | |
| 172 | 2. If `specifier` equals `.` or `..`, or if `specifier` starts with `/`, `\`, `./`, `.\`, `../`, or `..\`, throw. | |
| 173 | 3. Return `package(specifier, parentURL, options)`. | |
| 174 | 5. Yield `url`. | |
| 175 | ||
| 176 | #### `const generator = resolve.preresolved(specifier, resolutions, parentURL[, options])` | |
| 177 | ||
| 178 | 1. Let `imports` be `resolutions[parentURL]`. | |
| 179 | 2. If `imports` is a non-`null` object: | |
| 180 | 1. Return `packageImportsExports(specifier, imports, parentURL, true, options)`. | |
| 181 | ||
| 182 | #### `const generator = resolve.deferred(specifier[, options])` | |
| 183 | ||
| 184 | 1. If `options.defer` includes `specifier`: | |
| 185 | 1. Yield `options.deferredProtocol` concatenated with `specifier` and return. | |
| 186 | ||
| 187 | #### `const generator = resolve.package(packageSpecifier, parentURL[, options])` | |
| 188 | ||
| 189 | 1. If `packageSpecifier` is the empty string, throw. | |
| 190 | 2. If `packageSpecifier` does not start with `@`: | |
| 191 | 1. Set `packageName` to the substring of `packageSpecifier` until the first `/` or the end of the string. | |
| 192 | 3. Let `packageName` be `undefined`. | |
| 193 | 4. Otherwise: | |
| 194 | 1. If `packageSpecifier` does not include `/`, throw. | |
| 195 | 2. Set `packageName` to the substring of `packageSpecifier` until the second `/` or the end of the string. | |
| 196 | 5. If `packageName` starts with `.` or includes `\` or `%`, throw. | |
| 197 | 6. If `builtinTarget(packageSpecifier, null, options.builtins, options)` yields, return. | |
| 198 | 7. If `deferred(packageSpecifier, options)` yields, return. | |
| 199 | 8. Let `packageSubpath` be `.` concatenated with the substring of `packageSpecifier` from the position at the length of `packageName`. | |
| 200 | 9. If `packageSelf(packageName, packageSubpath, parentURL, options)` yields, return. | |
| 201 | 10. For each value `packageURL` of `lookupPackageRoot(packageName, parentURL, options)`: | |
| 202 | 1. Let `info` be the result of yielding `packageURL`. | |
| 203 | 2. If `info` is not `null`: | |
| 204 | 1. If `info.engines` is set: | |
| 205 | 1. Call `validateEngines(packageURL, info.engines, options)`. | |
| 206 | 2. If `info.exports` is set: | |
| 207 | 1. Return `packageExports(packageURL, packageSubpath, info.exports, options)`. | |
| 208 | 3. If `packageSubpath` is `.`: | |
| 209 | 1. If `info.main` is a non-empty string: | |
| 210 | 1. Set `packageSubpath` to `info.main`. | |
| 211 | 2. Otherwise: | |
| 212 | 1. Return `file('index', packageURL, true, options)`. | |
| 213 | 4. If `file(packageSubpath, packageURL, false, options)` resolves, return. | |
| 214 | 5. Return `directory(packageSubpath, packageURL, options)`. | |
| 215 | ||
| 216 | #### `const generator = resolve.packageSelf(packageName, packageSubpath, parentURL[, options])` | |
| 217 | ||
| 218 | 1. For each value `packageURL` of `lookupPackageScope(parentURL, options)`: | |
| 219 | 1. Let `info` be the result of yielding `packageURL`. | |
| 220 | 2. If `info` is not `null`: | |
| 221 | 1. If `info.name` does not equal `packageName`, return. | |
| 222 | 2. If `info.exports` is set: | |
| 223 | 1. Return `packageExports(packageURL, packageSubpath, info.exports, options)`. | |
| 224 | 3. If `packageSubpath` is `.`: | |
| 225 | 1. If `info.main` is a non-empty string: | |
| 226 | 1. Set `packageSubpath` to `info.main`. | |
| 227 | 2. Otherwise: | |
| 228 | 1. Return `file('index', packageURL, true, options)`. | |
| 229 | 4. If `file(packageSubpath, packageURL, false, options)` resolves, return. | |
| 230 | 5. Return `directory(packageSubpath, packageURL, options)`. | |
| 231 | ||
| 232 | #### `const generator = resolve.packageExports(packageURL, subpath, exports[, options])` | |
| 233 | ||
| 234 | 1. If `subpath` is `.`: | |
| 235 | 1. Let `mainExport` be `undefined`. | |
| 236 | 2. If `exports` is a string or an array: | |
| 237 | 1. Set `mainExport` to `exports`. | |
| 238 | 3. If `exports` is a non-`null` object: | |
| 239 | 1. If some keys of `exports` start with `.`: | |
| 240 | 1. If `.` is a key of `exports`: | |
| 241 | 1. Set `mainExport` to `exports['.']`. | |
| 242 | 2. Otherwise: | |
| 243 | 1. Set `mainExport` to `exports`. | |
| 244 | 4. If `mainExport` is not `undefined`: | |
| 245 | 1. If `packageTarget(packageURL, mainExport, null, false, options)` yields, return. | |
| 246 | 2. Otherwise, if `exports` is a non-`null` object: | |
| 247 | 1. If every key of `exports` starts with `.`: | |
| 248 | 1. If `packageImportsExports(subpath, exports, packageURL, false, options)` yields, return. | |
| 249 | 3. Throw. | |
| 250 | ||
| 251 | #### `const generator = resolve.packageImports(specifier, parentURL[, options])` | |
| 252 | ||
| 253 | 1. If `specifier` is `#` or starts with `#/`, throw. | |
| 254 | 2. For each value `packageURL` of `lookupPackageScope(parentURL, options)`: | |
| 255 | 1. Let `info` be the result of yielding `packageURL`. | |
| 256 | 2. If `info` is not `null`: | |
| 257 | 1. If `info.imports` is set: | |
| 258 | 1. If `packageImportsExports(specifier, info.imports, packageURL, true, options)` yields, return. | |
| 259 | 2. If specifier starts with `#`, throw. | |
| 260 | 3. Return. | |
| 261 | 3. If `options.imports` is set: | |
| 262 | 1. If `packageImportsExports(url.href, options.imports, parentURL, true, options)` yields, return. | |
| 263 | ||
| 264 | #### `const generator = resolve.packageImportsExports(matchKey, matchObject, packageURL, isImports[, options])` | |
| 265 | ||
| 266 | 1. If `matchKey` is a key of `matchObject` and `matchKey` does not include `*`: | |
| 267 | 1. Let `target` be `matchObject[matchKey]`. | |
| 268 | 2. Return `packageTarget(packageURL, target, null, isImports, options)`. | |
| 269 | 2. Let `expansionKeys` be the keys of `matchObject` that include `*` sorted by `patternKeyCompare`. | |
| 270 | 3. For each value `expansionKey` of `expansionKeys`: | |
| 271 | 1. Let `patternBase` be the substring of `expansionKey` until the first `*`. | |
| 272 | 2. If `matchKey` starts with but isn't equal to `patternBase`: | |
| 273 | 1. Let `patternTrailer` be the substring of `expansionKey` from the position at the index after the first `*`. | |
| 274 | 2. If `patternTrailer` is the empty string, or if `matchKey` ends with `patternTrailer` and the length of `matchKey` is greater than or equal to the length of `expansionKey`: | |
| 275 | 1. Let `target` be `matchObject[expansionKey]`. | |
| 276 | 2. Let `patternMatch` be the substring of `matchKey` from the position at the length of `patternBase` until the length of `matchKey` minus the length of `patternTrailer`. | |
| 277 | 3. Return `packageTarget(packageURL, target, patternMatch, isImports, options)`. | |
| 278 | ||
| 279 | #### `const generator = resolve.packageTarget(packageURL, target, patternMatch, isImports[, options])` | |
| 280 | ||
| 281 | 1. If `target` is a string: | |
| 282 | 1. If `target` does not start with `./` and `isImports` is `false`, throw. | |
| 283 | 2. If `patternMatch` is not `null`: | |
| 284 | 1. Replace every instance of `*` in `target` with `patternMatch`. | |
| 285 | 3. If `url(target, packageURL, options)` yields, return. | |
| 286 | 4. If `target` equals `.` or `..`, or if `target` starts with `/`, `./`, or `../`: | |
| 287 | 1. Yield the resolution of `target` relative to `packageURL` and return. | |
| 288 | 5. Return `package(target, packageURL, options)`. | |
| 289 | 2. If `target` is an array: | |
| 290 | 1. For each value `targetValue` of `target`: | |
| 291 | 1. If `packageTarget(packageURL, targetValue, patternMatch, isImports, options)` yields, return. | |
| 292 | 3. If `target` is a non-`null` object: | |
| 293 | 1. For each key `condition` of `target`: | |
| 294 | 1. If `condition` equals `default` or if `options.conditions` includes `condition`: | |
| 295 | 1. Let `targetValue` be `target[condition]`. | |
| 296 | 2. Return `packageTarget(packageURL, targetValue, patternMatch, isImports, options)`. | |
| 297 | ||
| 298 | #### `const generator = resolve.builtinTarget(packageSpecifier, packageVersion, target[, options])` | |
| 299 | ||
| 300 | 1. If `target` is a string: | |
| 301 | 1. If `target` does not start with `@`: | |
| 302 | 1. Let `targetName` be the substring of `target` until the first `@` or the end of the string. | |
| 303 | 2. Let `targetVersion` be the substring of `target` from the character following the first `@` and to the end of string, or `null` if no such substring exists. | |
| 304 | 2. Otherwise: | |
| 305 | 1. Let `targetName` be the substring of `target` until the second `@` or the end of the string. | |
| 306 | 2. Let `targetVersion` be the substring of `target` from the character following the second `@` and to the end of string, or `null` if no such substring exists. | |
| 307 | 3. If `packageSpecifier` equals `targetName`: | |
| 308 | 1. If `packageVersion` is `null` and `targetVersion` is `null`: | |
| 309 | 1. Yield `options.builtinProtocol` concatenated with `packageSpecifier` and return. | |
| 310 | 2. Let `version` be `null`. | |
| 311 | 3. If `packageVersion` is `null`, let `version` be `targetVersion`. | |
| 312 | 4. Otherwise, if `targetVersion` is either `null` or equals `packageVersion`, let `version` be `packageVersion` | |
| 313 | 5. If `version` is not `null`: | |
| 314 | 1. Yield `options.builtinProtocol` concatenated with `packageSpecifier`, `@`, and `version` and return. | |
| 315 | 2. If `target` is an array: | |
| 316 | 1. For each value `targetValue` of `target`: | |
| 317 | 1. If `builtinTarget(packageSpecifier, packageVersion, targetValue, options)` yields, return. | |
| 318 | 3. If `target` is a non-`null` object: | |
| 319 | 1. For each key `condition` of `target`: | |
| 320 | 1. If `condition` equals `default` or if `options.conditions` includes `condition`: | |
| 321 | 1. Let `targetValue` be `target[condition]`. | |
| 322 | 2. Return `builtinTarget(packageSpecifier, packageVersion, targetValue, options)`. | |
| 323 | ||
| 324 | #### `const generator = resolve.file(filename, parentURL, isIndex[, options])` | |
| 325 | ||
| 326 | 1. If `filename` equals `.` or `..`, or if `filename` ends with `/` or `\`, return. | |
| 327 | 2. If `parentURL` is a `file:` URL and `filename` includes encoded `/` or `\`, throw. | |
| 328 | 3. If `isIndex` is `false`: | |
| 329 | 1. Yield the resolution of `filename` relative to `parentURL`. | |
| 330 | 4. For each value `ext` of `options.extensions`: | |
| 331 | 1. If `filename` ends with `ext`, continue. | |
| 332 | 2. Yield the resolution of `filename` concatenated with `ext` relative to `parentURL`. | |
| 333 | ||
| 334 | #### `const generator = resolve.directory(dirname, parentURL[, options])` | |
| 335 | ||
| 336 | 1. Let `directoryURL` be `undefined`. | |
| 337 | 2. If `dirname` ends with `/` or `\`: | |
| 338 | 1. Set `directoryURL` to the resolution of `dirname` relative to `parentURL`. | |
| 339 | 3. Otherwise: | |
| 340 | 1. Set `directoryURL` to the resolution of `dirname` concatenated with `/` relative to `parentURL`. | |
| 341 | 4. Let `info` be the result of yielding the resolution of `package.json` relative to `directoryURL`. | |
| 342 | 5. If `info` is not `null`: | |
| 343 | 1. If `info.exports` is set: | |
| 344 | 1. Return `packageExports(directoryURL, '.', info.exports, options)`. | |
| 345 | 2. If `info.main` is a non-empty string: | |
| 346 | 1. If `file(info.main, directoryURL, false, options)` resolves, return. | |
| 347 | 2. Return `directory(info.main, directoryURL, options)`. | |
| 348 | 6. Return `file('index', directoryURL, true, options)`. | |
| 349 | ||
| 350 | ## License | |
| 351 | ||
| 352 | Apache-2.0 |
| … | … | @@ -0,0 +1,147 @@ |
| 1 | import URL from 'bare-url' | |
| 2 | ||
| 3 | type ConditionalSpecifier = | |
| 4 | | string | |
| 5 | | ConditionalSpecifier[] | |
| 6 | | { [condition: string]: ConditionalSpecifier } | |
| 7 | ||
| 8 | type ImportsMap = { [specifier: string]: ConditionalSpecifier } | |
| 9 | ||
| 10 | type ExportsMap = ImportsMap | |
| 11 | ||
| 12 | type ResolutionsMap = { [href: string]: ImportsMap } | |
| 13 | ||
| 14 | type Builtins = ConditionalSpecifier[] | |
| 15 | ||
| 16 | type Conditions = string[] | Conditions[] | |
| 17 | ||
| 18 | type Engines = { [name: string]: string } | |
| 19 | ||
| 20 | type JSON = string | number | boolean | JSON[] | { [key: string]: JSON } | |
| 21 | ||
| 22 | interface ResolveOptions { | |
| 23 | builtinProtocol?: string | |
| 24 | builtins?: Builtins | |
| 25 | conditions?: Conditions | |
| 26 | defer?: string[] | |
| 27 | deferredProtocol?: string | |
| 28 | engines?: Engines | |
| 29 | extensions?: string[] | |
| 30 | imports?: ImportsMap | |
| 31 | matchedConditions?: string[] | |
| 32 | resolutions?: ResolutionsMap | |
| 33 | } | |
| 34 | ||
| 35 | declare function resolve( | |
| 36 | specifier: string, | |
| 37 | parentURL: URL, | |
| 38 | readPackage?: (url: URL) => JSON | null | |
| 39 | ): Iterable<URL> | |
| 40 | ||
| 41 | declare function resolve( | |
| 42 | specifier: string, | |
| 43 | parentURL: URL, | |
| 44 | readPackage: (url: URL) => Promise<JSON | null> | |
| 45 | ): AsyncIterable<URL> | |
| 46 | ||
| 47 | declare function resolve( | |
| 48 | specifier: string, | |
| 49 | parentURL: URL, | |
| 50 | opts: ResolveOptions, | |
| 51 | readPackage?: (url: URL) => JSON | null | |
| 52 | ): Iterable<URL> | |
| 53 | ||
| 54 | declare function resolve( | |
| 55 | specifier: string, | |
| 56 | parentURL: URL, | |
| 57 | opts: ResolveOptions, | |
| 58 | readPackage: (url: URL) => Promise<JSON | null> | |
| 59 | ): AsyncIterable<URL> | |
| 60 | ||
| 61 | declare namespace resolve { | |
| 62 | export { | |
| 63 | type ConditionalSpecifier, | |
| 64 | type ImportsMap, | |
| 65 | type ExportsMap, | |
| 66 | type ResolutionsMap, | |
| 67 | type Builtins, | |
| 68 | type Conditions, | |
| 69 | type Engines, | |
| 70 | type ResolveOptions | |
| 71 | } | |
| 72 | ||
| 73 | export const constants: { | |
| 74 | UNRESOLVED: number | |
| 75 | YIELDED: number | |
| 76 | RESOLVED: number | |
| 77 | } | |
| 78 | ||
| 79 | export type Resolver = Generator< | |
| 80 | { package: URL } | { resolution: URL }, | |
| 81 | number, | |
| 82 | void | boolean | JSON | null | |
| 83 | > | |
| 84 | ||
| 85 | export function module(specifier: string, parentURL: URL, opts?: ResolveOptions): Resolver | |
| 86 | ||
| 87 | export function url(url: string, parentURL: URL, opts?: ResolveOptions): Resolver | |
| 88 | ||
| 89 | export function preresolved( | |
| 90 | specifier: string, | |
| 91 | resolutions: ResolutionsMap, | |
| 92 | parentURL: URL, | |
| 93 | opts?: ResolveOptions | |
| 94 | ): Resolver | |
| 95 | ||
| 96 | export function package(packageSpecifier: string, parentURL: URL, opts?: ResolveOptions): Resolver | |
| 97 | ||
| 98 | export function packageSelf( | |
| 99 | packageName: string, | |
| 100 | packageSubpath: string, | |
| 101 | parentURL: URL, | |
| 102 | opts?: ResolveOptions | |
| 103 | ): Resolver | |
| 104 | ||
| 105 | export function packageExports( | |
| 106 | packageURL: URL, | |
| 107 | subpath: string, | |
| 108 | packageExports: ExportsMap, | |
| 109 | opts?: ResolveOptions | |
| 110 | ): Resolver | |
| 111 | ||
| 112 | export function packageImports(specifier: string, parentURL: URL, opts?: ResolveOptions): Resolver | |
| 113 | ||
| 114 | export function packageImportsExports( | |
| 115 | matchKey: string, | |
| 116 | matchObject: ImportsMap | ExportsMap, | |
| 117 | packageURL: URL, | |
| 118 | isImports: boolean, | |
| 119 | opts?: ResolveOptions | |
| 120 | ): Resolver | |
| 121 | ||
| 122 | export function packageTarget( | |
| 123 | packageURL: URL, | |
| 124 | target: ConditionalSpecifier, | |
| 125 | patternMatch: string, | |
| 126 | isImports: boolean, | |
| 127 | opts?: ResolveOptions | |
| 128 | ): Resolver | |
| 129 | ||
| 130 | export function builtinTarget( | |
| 131 | packageSpecifier: string, | |
| 132 | packageVersion: string, | |
| 133 | target: ConditionalSpecifier, | |
| 134 | opts?: ResolveOptions | |
| 135 | ): Resolver | |
| 136 | ||
| 137 | export function file( | |
| 138 | filename: string, | |
| 139 | parentURL: URL, | |
| 140 | isIndex: boolean, | |
| 141 | opts?: ResolveOptions | |
| 142 | ): Resolver | |
| 143 | ||
| 144 | export function directory(dirname: string, parentURL: URL, opts?: ResolveOptions): Resolver | |
| 145 | } | |
| 146 | ||
| 147 | export = resolve |
| … | … | @@ -0,0 +1,796 @@ |
| 1 | const { satisfies } = require('bare-semver') | |
| 2 | const errors = require('./lib/errors') | |
| 3 | ||
| 4 | module.exports = exports = function resolve(specifier, parentURL, opts, readPackage) { | |
| 5 | if (typeof opts === 'function') { | |
| 6 | readPackage = opts | |
| 7 | opts = {} | |
| 8 | } else if (typeof readPackage !== 'function') { | |
| 9 | readPackage = defaultReadPackage | |
| 10 | } | |
| 11 | ||
| 12 | return { | |
| 13 | *[Symbol.iterator]() { | |
| 14 | const generator = exports.module(specifier, parentURL, opts) | |
| 15 | ||
| 16 | let next = generator.next() | |
| 17 | ||
| 18 | while (next.done !== true) { | |
| 19 | const value = next.value | |
| 20 | ||
| 21 | if (value.package) { | |
| 22 | next = generator.next(readPackage(value.package)) | |
| 23 | } else { | |
| 24 | next = generator.next(yield value.resolution) | |
| 25 | } | |
| 26 | } | |
| 27 | ||
| 28 | return next.value | |
| 29 | }, | |
| 30 | ||
| 31 | async *[Symbol.asyncIterator]() { | |
| 32 | const generator = exports.module(specifier, parentURL, opts) | |
| 33 | ||
| 34 | let next = generator.next() | |
| 35 | ||
| 36 | while (next.done !== true) { | |
| 37 | const value = next.value | |
| 38 | ||
| 39 | if (value.package) { | |
| 40 | next = generator.next(await readPackage(value.package)) | |
| 41 | } else { | |
| 42 | next = generator.next(yield value.resolution) | |
| 43 | } | |
| 44 | } | |
| 45 | ||
| 46 | return next.value | |
| 47 | } | |
| 48 | } | |
| 49 | } | |
| 50 | ||
| 51 | function defaultReadPackage() { | |
| 52 | return null | |
| 53 | } | |
| 54 | ||
| 55 | // No resolution candidate was yielded | |
| 56 | const UNRESOLVED = 0x0 | |
| 57 | // At least 1 resolution candidate was yielded | |
| 58 | const YIELDED = 0x1 | |
| 59 | // At least 1 resolution candidate was yielded and resolved | |
| 60 | const RESOLVED = YIELDED | 0x2 | |
| 61 | ||
| 62 | exports.constants = { | |
| 63 | UNRESOLVED, | |
| 64 | YIELDED, | |
| 65 | RESOLVED | |
| 66 | } | |
| 67 | ||
| 68 | exports.module = function* (specifier, parentURL, opts = {}) { | |
| 69 | const { resolutions = null, imports = null } = opts | |
| 70 | ||
| 71 | if (exports.startsWithWindowsDriveLetter(specifier)) { | |
| 72 | specifier = '/' + specifier | |
| 73 | } | |
| 74 | ||
| 75 | let status | |
| 76 | ||
| 77 | if (resolutions) { | |
| 78 | status = yield* exports.preresolved(specifier, resolutions, parentURL, opts) | |
| 79 | ||
| 80 | if (status) return status | |
| 81 | } | |
| 82 | ||
| 83 | status = yield* exports.url(specifier, parentURL, opts) | |
| 84 | ||
| 85 | if (status) return status | |
| 86 | ||
| 87 | status = yield* exports.packageImports(specifier, parentURL, opts) | |
| 88 | ||
| 89 | if (status) return status | |
| 90 | ||
| 91 | if ( | |
| 92 | specifier === '.' || | |
| 93 | specifier === '..' || | |
| 94 | specifier[0] === '/' || | |
| 95 | specifier[0] === '\\' || | |
| 96 | specifier.startsWith('./') || | |
| 97 | specifier.startsWith('.\\') || | |
| 98 | specifier.startsWith('../') || | |
| 99 | specifier.startsWith('..\\') | |
| 100 | ) { | |
| 101 | if (imports) { | |
| 102 | status = yield* exports.packageImportsExports(specifier, imports, parentURL, true, opts) | |
| 103 | ||
| 104 | if (status) return status | |
| 105 | } | |
| 106 | ||
| 107 | status = yield* exports.deferred(specifier, opts) | |
| 108 | ||
| 109 | if (status) return status | |
| 110 | ||
| 111 | status = yield* exports.file(specifier, parentURL, false, opts) | |
| 112 | ||
| 113 | if (status === RESOLVED) return status | |
| 114 | ||
| 115 | return yield* exports.directory(specifier, parentURL, opts) | |
| 116 | } | |
| 117 | ||
| 118 | return yield* exports.package(specifier, parentURL, opts) | |
| 119 | } | |
| 120 | ||
| 121 | exports.url = function* (url, parentURL, opts = {}) { | |
| 122 | const { imports = null, deferredProtocol = 'deferred:', resolutions = null } = opts | |
| 123 | ||
| 124 | let resolution | |
| 125 | try { | |
| 126 | resolution = new URL(url) | |
| 127 | } catch { | |
| 128 | return UNRESOLVED | |
| 129 | } | |
| 130 | ||
| 131 | if (imports) { | |
| 132 | const status = yield* exports.packageImportsExports( | |
| 133 | resolution.href, | |
| 134 | imports, | |
| 135 | parentURL, | |
| 136 | true, | |
| 137 | opts | |
| 138 | ) | |
| 139 | ||
| 140 | if (status) return status | |
| 141 | } | |
| 142 | ||
| 143 | if (resolution.protocol === deferredProtocol) { | |
| 144 | const specifier = resolution.pathname | |
| 145 | ||
| 146 | if (resolutions) { | |
| 147 | const imports = resolutions[parentURL.href] | |
| 148 | ||
| 149 | if (typeof imports === 'object' && imports !== null) { | |
| 150 | opts = { | |
| 151 | ...opts, | |
| 152 | resolutions: { ...resolutions, [parentURL.href]: { ...imports, [specifier]: null } } | |
| 153 | } | |
| 154 | } | |
| 155 | } | |
| 156 | ||
| 157 | return yield* exports.module(specifier, parentURL, opts) | |
| 158 | } | |
| 159 | ||
| 160 | if (resolution.protocol === 'node:') { | |
| 161 | const specifier = resolution.pathname | |
| 162 | ||
| 163 | if ( | |
| 164 | specifier === '.' || | |
| 165 | specifier === '..' || | |
| 166 | specifier[0] === '/' || | |
| 167 | specifier.startsWith('./') || | |
| 168 | specifier.startsWith('../') | |
| 169 | ) { | |
| 170 | throw errors.INVALID_MODULE_SPECIFIER(`Module specifier '${url}' is not a valid package name`) | |
| 171 | } | |
| 172 | ||
| 173 | return yield* exports.package(specifier, parentURL, opts) | |
| 174 | } | |
| 175 | ||
| 176 | const resolved = yield { resolution } | |
| 177 | ||
| 178 | return resolved ? RESOLVED : YIELDED | |
| 179 | } | |
| 180 | ||
| 181 | exports.preresolved = function* (specifier, resolutions, parentURL, opts = {}) { | |
| 182 | const imports = resolutions[parentURL.href] | |
| 183 | ||
| 184 | if (typeof imports === 'object' && imports !== null) { | |
| 185 | return yield* exports.packageImportsExports(specifier, imports, parentURL, true, opts) | |
| 186 | } | |
| 187 | ||
| 188 | return UNRESOLVED | |
| 189 | } | |
| 190 | ||
| 191 | exports.deferred = function* (specifier, opts = {}) { | |
| 192 | const { deferredProtocol = 'deferred:', defer = [] } = opts | |
| 193 | ||
| 194 | if (defer.includes(specifier)) { | |
| 195 | const resolved = yield { resolution: new URL(deferredProtocol + specifier) } | |
| 196 | ||
| 197 | return resolved ? RESOLVED : YIELDED | |
| 198 | } | |
| 199 | ||
| 200 | return UNRESOLVED | |
| 201 | } | |
| 202 | ||
| 203 | exports.package = function* (packageSpecifier, parentURL, opts = {}) { | |
| 204 | const { builtins = [] } = opts | |
| 205 | ||
| 206 | if (packageSpecifier === '') { | |
| 207 | throw errors.INVALID_MODULE_SPECIFIER( | |
| 208 | `Module specifier '${packageSpecifier}' is not a valid package name` | |
| 209 | ) | |
| 210 | } | |
| 211 | ||
| 212 | let packageName | |
| 213 | ||
| 214 | if (packageSpecifier[0] !== '@') { | |
| 215 | packageName = packageSpecifier.split('/', 1).join() | |
| 216 | } else { | |
| 217 | if (!packageSpecifier.includes('/')) { | |
| 218 | throw errors.INVALID_MODULE_SPECIFIER( | |
| 219 | `Module specifier '${packageSpecifier}' is not a valid package name` | |
| 220 | ) | |
| 221 | } | |
| 222 | ||
| 223 | packageName = packageSpecifier.split('/', 2).join('/') | |
| 224 | } | |
| 225 | ||
| 226 | if (packageName[0] === '.' || packageName.includes('\\') || packageName.includes('%')) { | |
| 227 | throw errors.INVALID_MODULE_SPECIFIER( | |
| 228 | `Module specifier '${packageSpecifier}' is not a valid package name` | |
| 229 | ) | |
| 230 | } | |
| 231 | ||
| 232 | let status | |
| 233 | ||
| 234 | status = yield* exports.builtinTarget(packageSpecifier, null, builtins, opts) | |
| 235 | ||
| 236 | if (status) return status | |
| 237 | ||
| 238 | status = yield* exports.deferred(packageSpecifier, opts) | |
| 239 | ||
| 240 | if (status) return status | |
| 241 | ||
| 242 | let packageSubpath = '.' + packageSpecifier.substring(packageName.length) | |
| 243 | ||
| 244 | status = yield* exports.packageSelf(packageName, packageSubpath, parentURL, opts) | |
| 245 | ||
| 246 | if (status) return status | |
| 247 | ||
| 248 | parentURL = new URL(parentURL.href) | |
| 249 | ||
| 250 | for (const packageURL of exports.lookupPackageRoot(packageName, parentURL)) { | |
| 251 | const info = yield { package: packageURL } | |
| 252 | ||
| 253 | if (info) { | |
| 254 | if (info.engines) exports.validateEngines(packageURL, info.engines, opts) | |
| 255 | ||
| 256 | if (info.exports) { | |
| 257 | return yield* exports.packageExports(packageURL, packageSubpath, info.exports, opts) | |
| 258 | } | |
| 259 | ||
| 260 | if (packageSubpath === '.') { | |
| 261 | if (typeof info.main === 'string' && info.main !== '') { | |
| 262 | packageSubpath = info.main | |
| 263 | } else { | |
| 264 | return yield* exports.file('index', packageURL, true, opts) | |
| 265 | } | |
| 266 | } | |
| 267 | ||
| 268 | status = yield* exports.file(packageSubpath, packageURL, false, opts) | |
| 269 | ||
| 270 | if (status === RESOLVED) return status | |
| 271 | ||
| 272 | return yield* exports.directory(packageSubpath, packageURL, opts) | |
| 273 | } | |
| 274 | } | |
| 275 | ||
| 276 | return UNRESOLVED | |
| 277 | } | |
| 278 | ||
| 279 | exports.packageSelf = function* (packageName, packageSubpath, parentURL, opts = {}) { | |
| 280 | for (const packageURL of exports.lookupPackageScope(parentURL, opts)) { | |
| 281 | const info = yield { package: packageURL } | |
| 282 | ||
| 283 | if (info) { | |
| 284 | if (info.name !== packageName) return false | |
| 285 | ||
| 286 | if (info.exports) { | |
| 287 | return yield* exports.packageExports(packageURL, packageSubpath, info.exports, opts) | |
| 288 | } | |
| 289 | ||
| 290 | if (packageSubpath === '.') { | |
| 291 | if (typeof info.main === 'string' && info.main !== '') { | |
| 292 | packageSubpath = info.main | |
| 293 | } else { | |
| 294 | return yield* exports.file('index', packageURL, true, opts) | |
| 295 | } | |
| 296 | } | |
| 297 | ||
| 298 | const status = yield* exports.file(packageSubpath, packageURL, false, opts) | |
| 299 | ||
| 300 | if (status === RESOLVED) return status | |
| 301 | ||
| 302 | return yield* exports.directory(packageSubpath, packageURL, opts) | |
| 303 | } | |
| 304 | } | |
| 305 | ||
| 306 | return UNRESOLVED | |
| 307 | } | |
| 308 | ||
| 309 | exports.packageExports = function* (packageURL, subpath, packageExports, opts = {}) { | |
| 310 | if (subpath === '.') { | |
| 311 | let mainExport | |
| 312 | ||
| 313 | if (typeof packageExports === 'string' || Array.isArray(packageExports)) { | |
| 314 | mainExport = packageExports | |
| 315 | } else if (typeof packageExports === 'object' && packageExports !== null) { | |
| 316 | const keys = Object.keys(packageExports) | |
| 317 | ||
| 318 | if (keys.some((key) => key.startsWith('.'))) { | |
| 319 | if ('.' in packageExports) mainExport = packageExports['.'] | |
| 320 | } else { | |
| 321 | mainExport = packageExports | |
| 322 | } | |
| 323 | } | |
| 324 | ||
| 325 | if (mainExport) { | |
| 326 | const status = yield* exports.packageTarget(packageURL, mainExport, null, false, opts) | |
| 327 | ||
| 328 | if (status) return status | |
| 329 | } | |
| 330 | } else if (typeof packageExports === 'object' && packageExports !== null) { | |
| 331 | const keys = Object.keys(packageExports) | |
| 332 | ||
| 333 | if (keys.every((key) => key.startsWith('.'))) { | |
| 334 | const status = yield* exports.packageImportsExports( | |
| 335 | subpath, | |
| 336 | packageExports, | |
| 337 | packageURL, | |
| 338 | false, | |
| 339 | opts | |
| 340 | ) | |
| 341 | ||
| 342 | if (status) return status | |
| 343 | } | |
| 344 | } | |
| 345 | ||
| 346 | throw errors.PACKAGE_PATH_NOT_EXPORTED( | |
| 347 | `Package subpath '${subpath}' is not defined by "exports" in '${packageURL}'` | |
| 348 | ) | |
| 349 | } | |
| 350 | ||
| 351 | exports.packageImports = function* (specifier, parentURL, opts = {}) { | |
| 352 | const { imports = null } = opts | |
| 353 | ||
| 354 | if (specifier === '#' || specifier.startsWith('#/')) { | |
| 355 | throw errors.INVALID_MODULE_SPECIFIER( | |
| 356 | `Module specifier '${specifier}' is not a valid internal imports specifier` | |
| 357 | ) | |
| 358 | } | |
| 359 | ||
| 360 | for (const packageURL of exports.lookupPackageScope(parentURL, opts)) { | |
| 361 | const info = yield { package: packageURL } | |
| 362 | ||
| 363 | if (info) { | |
| 364 | if (info.imports) { | |
| 365 | const status = yield* exports.packageImportsExports( | |
| 366 | specifier, | |
| 367 | info.imports, | |
| 368 | packageURL, | |
| 369 | true, | |
| 370 | opts | |
| 371 | ) | |
| 372 | ||
| 373 | if (status) return status | |
| 374 | } | |
| 375 | ||
| 376 | if (specifier.startsWith('#')) { | |
| 377 | throw errors.PACKAGE_IMPORT_NOT_DEFINED( | |
| 378 | `Package import specifier '${specifier}' is not defined by "imports" in '${packageURL}'` | |
| 379 | ) | |
| 380 | } | |
| 381 | ||
| 382 | break | |
| 383 | } | |
| 384 | } | |
| 385 | ||
| 386 | if (imports) { | |
| 387 | const status = yield* exports.packageImportsExports(specifier, imports, parentURL, true, opts) | |
| 388 | ||
| 389 | if (status) return status | |
| 390 | } | |
| 391 | ||
| 392 | return UNRESOLVED | |
| 393 | } | |
| 394 | ||
| 395 | exports.packageImportsExports = function* ( | |
| 396 | matchKey, | |
| 397 | matchObject, | |
| 398 | packageURL, | |
| 399 | isImports, | |
| 400 | opts = {} | |
| 401 | ) { | |
| 402 | if (matchKey in matchObject && !matchKey.includes('*')) { | |
| 403 | const target = matchObject[matchKey] | |
| 404 | ||
| 405 | return yield* exports.packageTarget(packageURL, target, null, isImports, opts) | |
| 406 | } | |
| 407 | ||
| 408 | const expansionKeys = Object.keys(matchObject) | |
| 409 | .filter((key) => key.includes('*')) | |
| 410 | .sort(exports.patternKeyCompare) | |
| 411 | ||
| 412 | for (const expansionKey of expansionKeys) { | |
| 413 | const patternIndex = expansionKey.indexOf('*') | |
| 414 | const patternBase = expansionKey.substring(0, patternIndex) | |
| 415 | ||
| 416 | if (matchKey.startsWith(patternBase) && matchKey !== patternBase) { | |
| 417 | const patternTrailer = expansionKey.substring(patternIndex + 1) | |
| 418 | ||
| 419 | if ( | |
| 420 | patternTrailer === '' || | |
| 421 | (matchKey.endsWith(patternTrailer) && matchKey.length >= expansionKey.length) | |
| 422 | ) { | |
| 423 | const target = matchObject[expansionKey] | |
| 424 | ||
| 425 | const patternMatch = matchKey.substring( | |
| 426 | patternBase.length, | |
| 427 | matchKey.length - patternTrailer.length | |
| 428 | ) | |
| 429 | ||
| 430 | return yield* exports.packageTarget(packageURL, target, patternMatch, isImports, opts) | |
| 431 | } | |
| 432 | } | |
| 433 | } | |
| 434 | ||
| 435 | return UNRESOLVED | |
| 436 | } | |
| 437 | ||
| 438 | exports.validateEngines = function validateEngines(packageURL, packageEngines, opts = {}) { | |
| 439 | const { engines = {} } = opts | |
| 440 | ||
| 441 | for (const [engine, range] of Object.entries(packageEngines)) { | |
| 442 | if (engine in engines) { | |
| 443 | const version = engines[engine] | |
| 444 | ||
| 445 | if (!satisfies(version, range)) { | |
| 446 | throw errors.UNSUPPORTED_ENGINE( | |
| 447 | `Package not compatible with engine '${engine}' ${version}, requires range '${range}' defined by "engines" in '${packageURL}'` | |
| 448 | ) | |
| 449 | } | |
| 450 | } | |
| 451 | } | |
| 452 | } | |
| 453 | ||
| 454 | exports.patternKeyCompare = function patternKeyCompare(keyA, keyB) { | |
| 455 | const patternIndexA = keyA.indexOf('*') | |
| 456 | const patternIndexB = keyB.indexOf('*') | |
| 457 | const baseLengthA = patternIndexA === -1 ? keyA.length : patternIndexA + 1 | |
| 458 | const baseLengthB = patternIndexB === -1 ? keyB.length : patternIndexB + 1 | |
| 459 | if (baseLengthA > baseLengthB) return -1 | |
| 460 | if (baseLengthB > baseLengthA) return 1 | |
| 461 | if (patternIndexA === -1) return 1 | |
| 462 | if (patternIndexB === -1) return -1 | |
| 463 | if (keyA.length > keyB.length) return -1 | |
| 464 | if (keyB.length > keyA.length) return 1 | |
| 465 | return 0 | |
| 466 | } | |
| 467 | ||
| 468 | exports.packageTarget = function* (packageURL, target, patternMatch, isImports, opts = {}) { | |
| 469 | const { conditions = [], matchedConditions = [] } = opts | |
| 470 | ||
| 471 | if (typeof target === 'string') { | |
| 472 | if (!target.startsWith('./') && !isImports) { | |
| 473 | throw errors.INVALID_PACKAGE_TARGET( | |
| 474 | `Invalid target '${target}' defined by "exports" in '${packageURL}'` | |
| 475 | ) | |
| 476 | } | |
| 477 | ||
| 478 | if (patternMatch !== null) { | |
| 479 | target = target.replaceAll('*', patternMatch) | |
| 480 | } | |
| 481 | ||
| 482 | const status = yield* exports.url(target, packageURL, opts) | |
| 483 | ||
| 484 | if (status) return status | |
| 485 | ||
| 486 | if ( | |
| 487 | target === '.' || | |
| 488 | target === '..' || | |
| 489 | target[0] === '/' || | |
| 490 | target.startsWith('./') || | |
| 491 | target.startsWith('../') | |
| 492 | ) { | |
| 493 | const resolved = yield { resolution: new URL(target, packageURL) } | |
| 494 | ||
| 495 | return resolved ? RESOLVED : YIELDED | |
| 496 | } | |
| 497 | ||
| 498 | return yield* exports.package(target, packageURL, opts) | |
| 499 | } | |
| 500 | ||
| 501 | if (Array.isArray(target)) { | |
| 502 | for (const targetValue of target) { | |
| 503 | const status = yield* exports.packageTarget( | |
| 504 | packageURL, | |
| 505 | targetValue, | |
| 506 | patternMatch, | |
| 507 | isImports, | |
| 508 | opts | |
| 509 | ) | |
| 510 | ||
| 511 | if (status) return status | |
| 512 | } | |
| 513 | } else if (typeof target === 'object' && target !== null) { | |
| 514 | let status = UNRESOLVED | |
| 515 | ||
| 516 | for (const [condition, targetValue, subset] of exports.conditionMatches( | |
| 517 | target, | |
| 518 | conditions, | |
| 519 | opts | |
| 520 | )) { | |
| 521 | matchedConditions.push(condition) | |
| 522 | ||
| 523 | status |= yield* exports.packageTarget(packageURL, targetValue, patternMatch, isImports, { | |
| 524 | ...opts, | |
| 525 | conditions: subset | |
| 526 | }) | |
| 527 | ||
| 528 | matchedConditions.pop() | |
| 529 | } | |
| 530 | ||
| 531 | if (status) return status | |
| 532 | } | |
| 533 | ||
| 534 | return UNRESOLVED | |
| 535 | } | |
| 536 | ||
| 537 | exports.builtinTarget = function* (packageSpecifier, packageVersion, target, opts = {}) { | |
| 538 | const { builtinProtocol = 'builtin:', conditions = [], matchedConditions = [] } = opts | |
| 539 | ||
| 540 | if (typeof target === 'string') { | |
| 541 | const targetParts = target.split('@') | |
| 542 | ||
| 543 | let targetName | |
| 544 | let targetVersion | |
| 545 | ||
| 546 | if (target[0] !== '@') { | |
| 547 | targetName = targetParts[0] | |
| 548 | targetVersion = targetParts[1] || null | |
| 549 | } else { | |
| 550 | targetName = targetParts.slice(0, 2).join('@') | |
| 551 | targetVersion = targetParts[2] || null | |
| 552 | } | |
| 553 | ||
| 554 | if (packageSpecifier === targetName) { | |
| 555 | if (packageVersion === null && targetVersion === null) { | |
| 556 | const resolved = yield { | |
| 557 | resolution: new URL(builtinProtocol + packageSpecifier) | |
| 558 | } | |
| 559 | ||
| 560 | return resolved ? RESOLVED : YIELDED | |
| 561 | } | |
| 562 | ||
| 563 | let version = null | |
| 564 | ||
| 565 | if (packageVersion === null) { | |
| 566 | version = targetVersion | |
| 567 | } else if (targetVersion === null || packageVersion === targetVersion) { | |
| 568 | version = packageVersion | |
| 569 | } | |
| 570 | ||
| 571 | if (version !== null) { | |
| 572 | const resolved = yield { | |
| 573 | resolution: new URL(builtinProtocol + packageSpecifier + '@' + version) | |
| 574 | } | |
| 575 | ||
| 576 | return resolved ? RESOLVED : YIELDED | |
| 577 | } | |
| 578 | } | |
| 579 | } else if (Array.isArray(target)) { | |
| 580 | for (const targetValue of target) { | |
| 581 | const status = yield* exports.builtinTarget( | |
| 582 | packageSpecifier, | |
| 583 | packageVersion, | |
| 584 | targetValue, | |
| 585 | opts | |
| 586 | ) | |
| 587 | ||
| 588 | if (status) return status | |
| 589 | } | |
| 590 | } else if (typeof target === 'object' && target !== null) { | |
| 591 | let status = UNRESOLVED | |
| 592 | ||
| 593 | for (const [condition, targetValue, subset] of exports.conditionMatches( | |
| 594 | target, | |
| 595 | conditions, | |
| 596 | opts | |
| 597 | )) { | |
| 598 | matchedConditions.push(condition) | |
| 599 | ||
| 600 | status |= yield* exports.builtinTarget(packageSpecifier, packageVersion, targetValue, { | |
| 601 | ...opts, | |
| 602 | conditions: subset | |
| 603 | }) | |
| 604 | ||
| 605 | matchedConditions.pop() | |
| 606 | } | |
| 607 | ||
| 608 | if (status) return status | |
| 609 | } | |
| 610 | ||
| 611 | return UNRESOLVED | |
| 612 | } | |
| 613 | ||
| 614 | exports.conditionMatches = function* conditionMatches(target, conditions, opts = {}) { | |
| 615 | if (conditions.every((condition) => typeof condition === 'string')) { | |
| 616 | const keys = Object.keys(target) | |
| 617 | ||
| 618 | for (const condition of keys) { | |
| 619 | if (condition === 'default' || conditions.includes(condition)) { | |
| 620 | yield [condition, target[condition], conditions] | |
| 621 | ||
| 622 | return true | |
| 623 | } | |
| 624 | } | |
| 625 | ||
| 626 | return false | |
| 627 | } | |
| 628 | ||
| 629 | let yielded = false | |
| 630 | ||
| 631 | for (const subset of conditions) { | |
| 632 | if (yield* conditionMatches(target, subset, opts)) { | |
| 633 | yielded = true | |
| 634 | } | |
| 635 | } | |
| 636 | ||
| 637 | return yielded | |
| 638 | } | |
| 639 | ||
| 640 | exports.lookupPackageRoot = function* (packageName, parentURL) { | |
| 641 | parentURL = new URL(parentURL.href) | |
| 642 | ||
| 643 | do { | |
| 644 | const packageURL = new URL('node_modules/' + packageName + '/', parentURL) | |
| 645 | ||
| 646 | const info = yield new URL('package.json', packageURL) | |
| 647 | ||
| 648 | if (info) return info | |
| 649 | ||
| 650 | parentURL.pathname = parentURL.pathname.substring(0, parentURL.pathname.lastIndexOf('/')) | |
| 651 | ||
| 652 | if ( | |
| 653 | parentURL.pathname.length === 3 && | |
| 654 | exports.isWindowsDriveLetter(parentURL.pathname.substring(1)) | |
| 655 | ) { | |
| 656 | break | |
| 657 | } | |
| 658 | } while (parentURL.pathname !== '' && parentURL.pathname !== '/') | |
| 659 | ||
| 660 | return null | |
| 661 | } | |
| 662 | ||
| 663 | exports.lookupPackageScope = function* lookupPackageScope(scopeURL, opts = {}) { | |
| 664 | const { resolutions = null } = opts | |
| 665 | ||
| 666 | if (resolutions) { | |
| 667 | for (const { resolution } of exports.preresolved('#package', resolutions, scopeURL, opts)) { | |
| 668 | if (resolution) return yield resolution | |
| 669 | } | |
| 670 | } | |
| 671 | ||
| 672 | scopeURL = new URL(scopeURL.href) | |
| 673 | ||
| 674 | do { | |
| 675 | if (scopeURL.pathname.endsWith('/node_modules')) break | |
| 676 | ||
| 677 | const info = yield new URL('package.json', scopeURL) | |
| 678 | ||
| 679 | if (info) return info | |
| 680 | ||
| 681 | scopeURL.pathname = scopeURL.pathname.substring(0, scopeURL.pathname.lastIndexOf('/')) | |
| 682 | ||
| 683 | if ( | |
| 684 | scopeURL.pathname.length === 3 && | |
| 685 | exports.isWindowsDriveLetter(scopeURL.pathname.substring(1)) | |
| 686 | ) { | |
| 687 | break | |
| 688 | } | |
| 689 | } while (scopeURL.pathname !== '' && scopeURL.pathname !== '/') | |
| 690 | ||
| 691 | return null | |
| 692 | } | |
| 693 | ||
| 694 | exports.file = function* (filename, parentURL, isIndex, opts = {}) { | |
| 695 | if ( | |
| 696 | filename === '.' || | |
| 697 | filename === '..' || | |
| 698 | filename[filename.length - 1] === '/' || | |
| 699 | filename[filename.length - 1] === '\\' | |
| 700 | ) { | |
| 701 | return UNRESOLVED | |
| 702 | } | |
| 703 | ||
| 704 | if (parentURL.protocol === 'file:' && /%2f|%5c/i.test(filename)) { | |
| 705 | throw errors.INVALID_MODULE_SPECIFIER(`Module specifier '${filename}' is invalid`) | |
| 706 | } | |
| 707 | ||
| 708 | const { extensions = [] } = opts | |
| 709 | ||
| 710 | let status = UNRESOLVED | |
| 711 | ||
| 712 | if (!isIndex) { | |
| 713 | if (yield { resolution: new URL(filename, parentURL) }) { | |
| 714 | return RESOLVED | |
| 715 | } | |
| 716 | ||
| 717 | status = YIELDED | |
| 718 | } | |
| 719 | ||
| 720 | for (const ext of extensions) { | |
| 721 | if (filename.endsWith(ext)) continue | |
| 722 | ||
| 723 | if (yield { resolution: new URL(filename + ext, parentURL) }) { | |
| 724 | return RESOLVED | |
| 725 | } | |
| 726 | ||
| 727 | status = YIELDED | |
| 728 | } | |
| 729 | ||
| 730 | return status | |
| 731 | } | |
| 732 | ||
| 733 | exports.directory = function* (dirname, parentURL, opts = {}) { | |
| 734 | let directoryURL | |
| 735 | ||
| 736 | if (dirname[dirname.length - 1] === '/' || dirname[dirname.length - 1] === '\\') { | |
| 737 | directoryURL = new URL(dirname, parentURL) | |
| 738 | } else { | |
| 739 | directoryURL = new URL(dirname + '/', parentURL) | |
| 740 | } | |
| 741 | ||
| 742 | const info = yield { package: new URL('package.json', directoryURL) } | |
| 743 | ||
| 744 | if (info) { | |
| 745 | if (info.exports) { | |
| 746 | return yield* exports.packageExports(directoryURL, '.', info.exports, opts) | |
| 747 | } | |
| 748 | ||
| 749 | if (typeof info.main === 'string' && info.main !== '') { | |
| 750 | const status = yield* exports.file(info.main, directoryURL, false, opts) | |
| 751 | ||
| 752 | if (status === RESOLVED) return status | |
| 753 | ||
| 754 | return yield* exports.directory(info.main, directoryURL, opts) | |
| 755 | } | |
| 756 | } | |
| 757 | ||
| 758 | return yield* exports.file('index', directoryURL, true, opts) | |
| 759 | } | |
| 760 | ||
| 761 | // https://infra.spec.whatwg.org/#ascii-upper-alpha | |
| 762 | function isASCIIUpperAlpha(c) { | |
| 763 | return c >= 0x41 && c <= 0x5a | |
| 764 | } | |
| 765 | ||
| 766 | // https://infra.spec.whatwg.org/#ascii-lower-alpha | |
| 767 | function isASCIILowerAlpha(c) { | |
| 768 | return c >= 0x61 && c <= 0x7a | |
| 769 | } | |
| 770 | ||
| 771 | // https://infra.spec.whatwg.org/#ascii-alpha | |
| 772 | function isASCIIAlpha(c) { | |
| 773 | return isASCIIUpperAlpha(c) || isASCIILowerAlpha(c) | |
| 774 | } | |
| 775 | ||
| 776 | // https://url.spec.whatwg.org/#windows-drive-letter | |
| 777 | exports.isWindowsDriveLetter = function isWindowsDriveLetter(input) { | |
| 778 | return ( | |
| 779 | input.length >= 2 && | |
| 780 | isASCIIAlpha(input.charCodeAt(0)) && | |
| 781 | (input.charCodeAt(1) === 0x3a || input.charCodeAt(1) === 0x7c) | |
| 782 | ) | |
| 783 | } | |
| 784 | ||
| 785 | // https://url.spec.whatwg.org/#start-with-a-windows-drive-letter | |
| 786 | exports.startsWithWindowsDriveLetter = function startsWithWindowsDriveLetter(input) { | |
| 787 | return ( | |
| 788 | input.length >= 2 && | |
| 789 | exports.isWindowsDriveLetter(input) && | |
| 790 | (input.length === 2 || | |
| 791 | input.charCodeAt(2) === 0x2f || | |
| 792 | input.charCodeAt(2) === 0x5c || | |
| 793 | input.charCodeAt(2) === 0x3f || | |
| 794 | input.charCodeAt(2) === 0x23) | |
| 795 | ) | |
| 796 | } |
| … | … | @@ -0,0 +1,11 @@ |
| 1 | declare class ModuleResolveError extends Error { | |
| 2 | readonly code: string | |
| 3 | ||
| 4 | static INVALID_MODULE_SPECIFIER(msg: string): ModuleResolveError | |
| 5 | static INVALID_PACKAGE_TARGET(msg: string): ModuleResolveError | |
| 6 | static PACKAGE_PATH_NOT_EXPORTED(msg: string): ModuleResolveError | |
| 7 | static PACKAGE_IMPORT_NOT_DEFINED(msg: string): ModuleResolveError | |
| 8 | static UNSUPPORTED_ENGINE(msg: string): ModuleResolveError | |
| 9 | } | |
| 10 | ||
| 11 | export = ModuleResolveError |
| … | … | @@ -0,0 +1,50 @@ |
| 1 | module.exports = class ModuleResolveError extends Error { | |
| 2 | constructor(msg, code, fn = ModuleResolveError) { | |
| 3 | super(`${code}: ${msg}`) | |
| 4 | this.code = code | |
| 5 | ||
| 6 | if (Error.captureStackTrace) { | |
| 7 | Error.captureStackTrace(this, fn) | |
| 8 | } | |
| 9 | } | |
| 10 | ||
| 11 | get name() { | |
| 12 | return 'ModuleResolveError' | |
| 13 | } | |
| 14 | ||
| 15 | static INVALID_MODULE_SPECIFIER(msg) { | |
| 16 | return new ModuleResolveError( | |
| 17 | msg, | |
| 18 | 'INVALID_MODULE_SPECIFIER', | |
| 19 | ModuleResolveError.INVALID_MODULE_SPECIFIER | |
| 20 | ) | |
| 21 | } | |
| 22 | ||
| 23 | static INVALID_PACKAGE_TARGET(msg) { | |
| 24 | return new ModuleResolveError( | |
| 25 | msg, | |
| 26 | 'INVALID_PACKAGE_TARGET', | |
| 27 | ModuleResolveError.INVALID_PACKAGE_TARGET | |
| 28 | ) | |
| 29 | } | |
| 30 | ||
| 31 | static PACKAGE_PATH_NOT_EXPORTED(msg) { | |
| 32 | return new ModuleResolveError( | |
| 33 | msg, | |
| 34 | 'PACKAGE_PATH_NOT_EXPORTED', | |
| 35 | ModuleResolveError.PACKAGE_PATH_NOT_EXPORTED | |
| 36 | ) | |
| 37 | } | |
| 38 | ||
| 39 | static PACKAGE_IMPORT_NOT_DEFINED(msg) { | |
| 40 | return new ModuleResolveError( | |
| 41 | msg, | |
| 42 | 'PACKAGE_IMPORT_NOT_DEFINED', | |
| 43 | ModuleResolveError.PACKAGE_IMPORT_NOT_DEFINED | |
| 44 | ) | |
| 45 | } | |
| 46 | ||
| 47 | static UNSUPPORTED_ENGINE(msg) { | |
| 48 | return new ModuleResolveError(msg, 'UNSUPPORTED_ENGINE', ModuleResolveError.UNSUPPORTED_ENGINE) | |
| 49 | } | |
| 50 | } |
| … | … | @@ -0,0 +1,51 @@ |
| 1 | { | |
| 2 | "name": "bare-module-resolve", | |
| 3 | "version": "1.12.1", | |
| 4 | "description": "Low-level module resolution algorithm for Bare", | |
| 5 | "exports": { | |
| 6 | "./package": "./package.json", | |
| 7 | ".": { | |
| 8 | "types": "./index.d.ts", | |
| 9 | "default": "./index.js" | |
| 10 | }, | |
| 11 | "./errors": { | |
| 12 | "types": "./lib/errors.d.ts", | |
| 13 | "default": "./lib/errors.js" | |
| 14 | } | |
| 15 | }, | |
| 16 | "files": [ | |
| 17 | "index.js", | |
| 18 | "index.d.ts", | |
| 19 | "lib" | |
| 20 | ], | |
| 21 | "scripts": { | |
| 22 | "test": "prettier . --check && bare test.js" | |
| 23 | }, | |
| 24 | "repository": { | |
| 25 | "type": "git", | |
| 26 | "url": "git+https://github.com/holepunchto/bare-module-resolve.git" | |
| 27 | }, | |
| 28 | "author": "Holepunch", | |
| 29 | "license": "Apache-2.0", | |
| 30 | "bugs": { | |
| 31 | "url": "https://github.com/holepunchto/bare-module-resolve/issues" | |
| 32 | }, | |
| 33 | "homepage": "https://github.com/holepunchto/bare-module-resolve#readme", | |
| 34 | "dependencies": { | |
| 35 | "bare-semver": "^1.0.0" | |
| 36 | }, | |
| 37 | "devDependencies": { | |
| 38 | "bare-url": "^2.1.3", | |
| 39 | "brittle": "^3.2.1", | |
| 40 | "prettier": "^3.3.3", | |
| 41 | "prettier-config-holepunch": "^2.0.0" | |
| 42 | }, | |
| 43 | "peerDependencies": { | |
| 44 | "bare-url": "*" | |
| 45 | }, | |
| 46 | "peerDependenciesMeta": { | |
| 47 | "bare-url": { | |
| 48 | "optional": true | |
| 49 | } | |
| 50 | } | |
| 51 | } |
| … | … | @@ -0,0 +1,13 @@ |
| 1 | cmake_minimum_required(VERSION 3.25) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | ||
| 5 | project(bare_os C) | |
| 6 | ||
| 7 | add_bare_module(bare_os) | |
| 8 | ||
| 9 | target_sources( | |
| 10 | ${bare_os} | |
| 11 | PRIVATE | |
| 12 | binding.c | |
| 13 | ) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,11 @@ |
| 1 | # bare-os | |
| 2 | ||
| 3 | Operating system utilities for Javascript. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i bare-os | |
| 7 | ``` | |
| 8 | ||
| 9 | ## License | |
| 10 | ||
| 11 | Apache-2.0 |
| … | … | @@ -0,0 +1,1427 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <bare.h> | |
| 3 | #include <js.h> | |
| 4 | #include <stdlib.h> | |
| 5 | #include <utf.h> | |
| 6 | #include <uv.h> | |
| 7 | ||
| 8 | static uv_rwlock_t bare_os_env_lock; | |
| 9 | ||
| 10 | static uv_once_t bare_os_env_lock_guard = UV_ONCE_INIT; | |
| 11 | ||
| 12 | static void | |
| 13 | bare_os__on_env_lock_init(void) { | |
| 14 | int err = uv_rwlock_init(&bare_os_env_lock); | |
| 15 | assert(err == 0); | |
| 16 | } | |
| 17 | ||
| 18 | static js_value_t * | |
| 19 | bare_os_type(js_env_t *env, js_callback_info_t *info) { | |
| 20 | int err; | |
| 21 | ||
| 22 | uv_utsname_t buffer; | |
| 23 | err = uv_os_uname(&buffer); | |
| 24 | if (err < 0) { | |
| 25 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 26 | assert(err == 0); | |
| 27 | ||
| 28 | return NULL; | |
| 29 | } | |
| 30 | ||
| 31 | js_value_t *result; | |
| 32 | err = js_create_string_utf8(env, (utf8_t *) buffer.sysname, -1, &result); | |
| 33 | assert(err == 0); | |
| 34 | ||
| 35 | return result; | |
| 36 | } | |
| 37 | ||
| 38 | static js_value_t * | |
| 39 | bare_os_version(js_env_t *env, js_callback_info_t *info) { | |
| 40 | int err; | |
| 41 | ||
| 42 | uv_utsname_t buffer; | |
| 43 | err = uv_os_uname(&buffer); | |
| 44 | if (err < 0) { | |
| 45 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 46 | assert(err == 0); | |
| 47 | ||
| 48 | return NULL; | |
| 49 | } | |
| 50 | ||
| 51 | js_value_t *result; | |
| 52 | err = js_create_string_utf8(env, (utf8_t *) buffer.version, -1, &result); | |
| 53 | assert(err == 0); | |
| 54 | ||
| 55 | return result; | |
| 56 | } | |
| 57 | ||
| 58 | static js_value_t * | |
| 59 | bare_os_release(js_env_t *env, js_callback_info_t *info) { | |
| 60 | int err; | |
| 61 | ||
| 62 | uv_utsname_t buffer; | |
| 63 | err = uv_os_uname(&buffer); | |
| 64 | if (err < 0) { | |
| 65 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 66 | assert(err == 0); | |
| 67 | ||
| 68 | return NULL; | |
| 69 | } | |
| 70 | ||
| 71 | js_value_t *result; | |
| 72 | err = js_create_string_utf8(env, (utf8_t *) buffer.release, -1, &result); | |
| 73 | assert(err == 0); | |
| 74 | ||
| 75 | return result; | |
| 76 | } | |
| 77 | ||
| 78 | static js_value_t * | |
| 79 | bare_os_machine(js_env_t *env, js_callback_info_t *info) { | |
| 80 | int err; | |
| 81 | ||
| 82 | uv_utsname_t buffer; | |
| 83 | err = uv_os_uname(&buffer); | |
| 84 | if (err < 0) { | |
| 85 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 86 | assert(err == 0); | |
| 87 | ||
| 88 | return NULL; | |
| 89 | } | |
| 90 | ||
| 91 | js_value_t *result; | |
| 92 | err = js_create_string_utf8(env, (utf8_t *) buffer.machine, -1, &result); | |
| 93 | assert(err == 0); | |
| 94 | ||
| 95 | return result; | |
| 96 | } | |
| 97 | ||
| 98 | static js_value_t * | |
| 99 | bare_os_exec_path(js_env_t *env, js_callback_info_t *info) { | |
| 100 | int err; | |
| 101 | ||
| 102 | size_t len = 4096; | |
| 103 | char exec_path[4096]; | |
| 104 | ||
| 105 | err = uv_exepath(exec_path, &len); | |
| 106 | if (err < 0) { | |
| 107 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 108 | assert(err == 0); | |
| 109 | ||
| 110 | return NULL; | |
| 111 | } | |
| 112 | ||
| 113 | js_value_t *result; | |
| 114 | err = js_create_string_utf8(env, (utf8_t *) exec_path, len, &result); | |
| 115 | assert(err == 0); | |
| 116 | ||
| 117 | return result; | |
| 118 | } | |
| 119 | ||
| 120 | static js_value_t * | |
| 121 | bare_os_pid(js_env_t *env, js_callback_info_t *info) { | |
| 122 | int err; | |
| 123 | ||
| 124 | js_value_t *result; | |
| 125 | err = js_create_uint32(env, uv_os_getpid(), &result); | |
| 126 | assert(err == 0); | |
| 127 | ||
| 128 | return result; | |
| 129 | } | |
| 130 | ||
| 131 | static js_value_t * | |
| 132 | bare_os_ppid(js_env_t *env, js_callback_info_t *info) { | |
| 133 | int err; | |
| 134 | ||
| 135 | js_value_t *result; | |
| 136 | err = js_create_uint32(env, uv_os_getppid(), &result); | |
| 137 | assert(err == 0); | |
| 138 | ||
| 139 | return result; | |
| 140 | } | |
| 141 | ||
| 142 | static js_value_t * | |
| 143 | bare_os_cwd(js_env_t *env, js_callback_info_t *info) { | |
| 144 | int err; | |
| 145 | ||
| 146 | size_t len = 4096; | |
| 147 | char cwd[4096]; | |
| 148 | ||
| 149 | err = uv_cwd(cwd, &len); | |
| 150 | if (err < 0) { | |
| 151 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 152 | assert(err == 0); | |
| 153 | ||
| 154 | return NULL; | |
| 155 | } | |
| 156 | ||
| 157 | js_value_t *result; | |
| 158 | err = js_create_string_utf8(env, (utf8_t *) cwd, len, &result); | |
| 159 | assert(err == 0); | |
| 160 | ||
| 161 | return result; | |
| 162 | } | |
| 163 | ||
| 164 | static js_value_t * | |
| 165 | bare_os_chdir(js_env_t *env, js_callback_info_t *info) { | |
| 166 | int err; | |
| 167 | ||
| 168 | size_t argc = 1; | |
| 169 | js_value_t *argv[1]; | |
| 170 | ||
| 171 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 172 | assert(err == 0); | |
| 173 | ||
| 174 | assert(argc == 1); | |
| 175 | ||
| 176 | utf8_t dir[4096]; | |
| 177 | err = js_get_value_string_utf8(env, argv[0], dir, 4096, NULL); | |
| 178 | assert(err == 0); | |
| 179 | ||
| 180 | err = uv_chdir((char *) dir); | |
| 181 | if (err < 0) { | |
| 182 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 183 | assert(err == 0); | |
| 184 | ||
| 185 | return NULL; | |
| 186 | } | |
| 187 | ||
| 188 | return NULL; | |
| 189 | } | |
| 190 | ||
| 191 | static js_value_t * | |
| 192 | bare_os_tmpdir(js_env_t *env, js_callback_info_t *info) { | |
| 193 | int err; | |
| 194 | ||
| 195 | size_t len = 4096; | |
| 196 | char tmpdir[4096]; | |
| 197 | ||
| 198 | err = uv_os_tmpdir(tmpdir, &len); | |
| 199 | if (err < 0) { | |
| 200 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 201 | assert(err == 0); | |
| 202 | ||
| 203 | return NULL; | |
| 204 | } | |
| 205 | ||
| 206 | js_value_t *result; | |
| 207 | err = js_create_string_utf8(env, (utf8_t *) tmpdir, len, &result); | |
| 208 | assert(err == 0); | |
| 209 | ||
| 210 | return result; | |
| 211 | } | |
| 212 | ||
| 213 | static js_value_t * | |
| 214 | bare_os_homedir(js_env_t *env, js_callback_info_t *info) { | |
| 215 | int err; | |
| 216 | ||
| 217 | size_t len = 4096; | |
| 218 | char homedir[4096]; | |
| 219 | ||
| 220 | err = uv_os_homedir(homedir, &len); | |
| 221 | if (err < 0) { | |
| 222 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 223 | assert(err == 0); | |
| 224 | ||
| 225 | return NULL; | |
| 226 | } | |
| 227 | ||
| 228 | js_value_t *result; | |
| 229 | err = js_create_string_utf8(env, (utf8_t *) homedir, len, &result); | |
| 230 | assert(err == 0); | |
| 231 | ||
| 232 | return result; | |
| 233 | } | |
| 234 | ||
| 235 | static js_value_t * | |
| 236 | bare_os_hostname(js_env_t *env, js_callback_info_t *info) { | |
| 237 | int err; | |
| 238 | ||
| 239 | size_t len = UV_MAXHOSTNAMESIZE; | |
| 240 | char hostname[UV_MAXHOSTNAMESIZE]; | |
| 241 | ||
| 242 | err = uv_os_gethostname(hostname, &len); | |
| 243 | if (err < 0) { | |
| 244 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 245 | assert(err == 0); | |
| 246 | ||
| 247 | return NULL; | |
| 248 | } | |
| 249 | ||
| 250 | js_value_t *result; | |
| 251 | err = js_create_string_utf8(env, (utf8_t *) hostname, len, &result); | |
| 252 | assert(err == 0); | |
| 253 | ||
| 254 | return result; | |
| 255 | } | |
| 256 | ||
| 257 | static js_value_t * | |
| 258 | bare_os_user_info(js_env_t *env, js_callback_info_t *info) { | |
| 259 | int err; | |
| 260 | ||
| 261 | uv_passwd_t pwd; | |
| 262 | err = uv_os_get_passwd(&pwd); | |
| 263 | if (err != 0) { | |
| 264 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 265 | assert(err == 0); | |
| 266 | ||
| 267 | return NULL; | |
| 268 | } | |
| 269 | ||
| 270 | js_value_t *result; | |
| 271 | err = js_create_object(env, &result); | |
| 272 | assert(err == 0); | |
| 273 | ||
| 274 | js_value_t *uid; | |
| 275 | err = js_create_int32(env, pwd.uid, &uid); | |
| 276 | assert(err == 0); | |
| 277 | ||
| 278 | err = js_set_named_property(env, result, "uid", uid); | |
| 279 | assert(err == 0); | |
| 280 | ||
| 281 | js_value_t *gid; | |
| 282 | err = js_create_int32(env, pwd.gid, &gid); | |
| 283 | assert(err == 0); | |
| 284 | ||
| 285 | err = js_set_named_property(env, result, "gid", gid); | |
| 286 | assert(err == 0); | |
| 287 | ||
| 288 | js_value_t *username; | |
| 289 | err = js_create_string_utf8(env, (utf8_t *) pwd.username, strlen(pwd.username), &username); | |
| 290 | assert(err == 0); | |
| 291 | ||
| 292 | err = js_set_named_property(env, result, "username", username); | |
| 293 | assert(err == 0); | |
| 294 | ||
| 295 | js_value_t *homedir; | |
| 296 | err = js_create_string_utf8(env, (utf8_t *) pwd.homedir, strlen(pwd.homedir), &homedir); | |
| 297 | assert(err == 0); | |
| 298 | ||
| 299 | err = js_set_named_property(env, result, "homedir", homedir); | |
| 300 | assert(err == 0); | |
| 301 | ||
| 302 | js_value_t *shell; | |
| 303 | ||
| 304 | if (pwd.shell == NULL) { | |
| 305 | err = js_get_null(env, &shell); | |
| 306 | assert(err == 0); | |
| 307 | } else { | |
| 308 | err = js_create_string_utf8(env, (utf8_t *) pwd.shell, -1, &shell); | |
| 309 | assert(err == 0); | |
| 310 | } | |
| 311 | ||
| 312 | err = js_set_named_property(env, result, "shell", shell); | |
| 313 | assert(err == 0); | |
| 314 | ||
| 315 | uv_os_free_passwd(&pwd); | |
| 316 | ||
| 317 | return result; | |
| 318 | } | |
| 319 | ||
| 320 | static int | |
| 321 | bare_os__population_count(uint32_t num) { | |
| 322 | int result = 0; | |
| 323 | ||
| 324 | while (num != 0) { | |
| 325 | result++; | |
| 326 | num &= num - 1; | |
| 327 | } | |
| 328 | ||
| 329 | return result; | |
| 330 | } | |
| 331 | ||
| 332 | static js_value_t * | |
| 333 | bare_os_network_interfaces(js_env_t *env, js_callback_info_t *info) { | |
| 334 | int err; | |
| 335 | ||
| 336 | uv_interface_address_t *addresses; | |
| 337 | int len; | |
| 338 | err = uv_interface_addresses(&addresses, &len); | |
| 339 | if (err < 0) { | |
| 340 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 341 | assert(err == 0); | |
| 342 | ||
| 343 | return NULL; | |
| 344 | } | |
| 345 | ||
| 346 | js_value_t *result; | |
| 347 | err = js_create_array_with_length(env, len, &result); | |
| 348 | assert(err == 0); | |
| 349 | ||
| 350 | for (size_t i = 0, n = len; i < n; i++) { | |
| 351 | uv_interface_address_t address = addresses[i]; | |
| 352 | ||
| 353 | uint32_t family = address.address.address4.sin_family; | |
| 354 | ||
| 355 | if (family != AF_INET && family != AF_INET6) { | |
| 356 | continue; | |
| 357 | } | |
| 358 | ||
| 359 | js_value_t *item; | |
| 360 | err = js_create_object(env, &item); | |
| 361 | assert(err == 0); | |
| 362 | ||
| 363 | js_value_t *val; | |
| 364 | ||
| 365 | err = js_create_string_utf8(env, (utf8_t *) address.name, -1, &val); | |
| 366 | assert(err == 0); | |
| 367 | ||
| 368 | err = js_set_named_property(env, item, "name", val); | |
| 369 | assert(err == 0); | |
| 370 | ||
| 371 | char address_ip[INET6_ADDRSTRLEN]; | |
| 372 | char netmask_ip[INET6_ADDRSTRLEN]; | |
| 373 | char family_string[5]; | |
| 374 | ||
| 375 | char cidr[INET6_ADDRSTRLEN + 8]; | |
| 376 | uint32_t cidr_value = 0; | |
| 377 | ||
| 378 | if (family == AF_INET) { | |
| 379 | err = uv_ip4_name(&address.address.address4, address_ip, sizeof(address_ip)); | |
| 380 | assert(err == 0); | |
| 381 | ||
| 382 | err = uv_ip4_name(&address.netmask.netmask4, netmask_ip, sizeof(netmask_ip)); | |
| 383 | assert(err == 0); | |
| 384 | ||
| 385 | strcpy(family_string, "IPv4"); | |
| 386 | ||
| 387 | cidr_value = bare_os__population_count(address.netmask.netmask4.sin_addr.s_addr); | |
| 388 | } else { | |
| 389 | err = uv_ip6_name(&address.address.address6, address_ip, sizeof(address_ip)); | |
| 390 | assert(err == 0); | |
| 391 | ||
| 392 | err = uv_ip6_name(&address.netmask.netmask6, netmask_ip, sizeof(netmask_ip)); | |
| 393 | assert(err == 0); | |
| 394 | ||
| 395 | strcpy(family_string, "IPv6"); | |
| 396 | ||
| 397 | for (size_t i = 0; i < 16; i++) { | |
| 398 | uint32_t count = bare_os__population_count(address.netmask.netmask6.sin6_addr.s6_addr[i]); | |
| 399 | ||
| 400 | if (count == 0) break; | |
| 401 | ||
| 402 | cidr_value += count; | |
| 403 | } | |
| 404 | } | |
| 405 | ||
| 406 | err = js_create_string_utf8(env, (utf8_t *) address_ip, -1, &val); | |
| 407 | assert(err == 0); | |
| 408 | ||
| 409 | err = js_set_named_property(env, item, "address", val); | |
| 410 | assert(err == 0); | |
| 411 | ||
| 412 | err = js_create_string_utf8(env, (utf8_t *) netmask_ip, -1, &val); | |
| 413 | assert(err == 0); | |
| 414 | ||
| 415 | err = js_set_named_property(env, item, "netmask", val); | |
| 416 | assert(err == 0); | |
| 417 | ||
| 418 | err = js_create_string_utf8(env, (utf8_t *) family_string, -1, &val); | |
| 419 | assert(err == 0); | |
| 420 | ||
| 421 | err = js_set_named_property(env, item, "family", val); | |
| 422 | assert(err == 0); | |
| 423 | ||
| 424 | err = snprintf(cidr, sizeof(cidr), "%s/%d", address_ip, cidr_value); | |
| 425 | assert(err > 0); | |
| 426 | ||
| 427 | err = js_create_string_utf8(env, (utf8_t *) cidr, -1, &val); | |
| 428 | assert(err == 0); | |
| 429 | ||
| 430 | err = js_set_named_property(env, item, "cidr", val); | |
| 431 | assert(err == 0); | |
| 432 | ||
| 433 | char mac[18]; | |
| 434 | err = snprintf( | |
| 435 | mac, | |
| 436 | sizeof(mac), | |
| 437 | "%02x:%02x:%02x:%02x:%02x:%02x", | |
| 438 | (unsigned char) address.phys_addr[0], | |
| 439 | (unsigned char) address.phys_addr[1], | |
| 440 | (unsigned char) address.phys_addr[2], | |
| 441 | (unsigned char) address.phys_addr[3], | |
| 442 | (unsigned char) address.phys_addr[4], | |
| 443 | (unsigned char) address.phys_addr[5] | |
| 444 | ); | |
| 445 | assert(err > 0); | |
| 446 | ||
| 447 | err = js_create_string_utf8(env, (utf8_t *) mac, -1, &val); | |
| 448 | assert(err == 0); | |
| 449 | ||
| 450 | err = js_set_named_property(env, item, "mac", val); | |
| 451 | assert(err == 0); | |
| 452 | ||
| 453 | err = js_get_boolean(env, address.is_internal == 1, &val); | |
| 454 | assert(err == 0); | |
| 455 | ||
| 456 | err = js_set_named_property(env, item, "internal", val); | |
| 457 | assert(err == 0); | |
| 458 | ||
| 459 | if (family == AF_INET6) { | |
| 460 | err = js_create_uint32(env, address.address.address6.sin6_scope_id, &val); | |
| 461 | assert(err == 0); | |
| 462 | ||
| 463 | err = js_set_named_property(env, item, "scopeid", val); | |
| 464 | assert(err == 0); | |
| 465 | } | |
| 466 | ||
| 467 | err = js_set_element(env, result, i, item); | |
| 468 | assert(err == 0); | |
| 469 | } | |
| 470 | ||
| 471 | uv_free_interface_addresses(addresses, len); | |
| 472 | ||
| 473 | return result; | |
| 474 | } | |
| 475 | ||
| 476 | static js_value_t * | |
| 477 | bare_os_kill(js_env_t *env, js_callback_info_t *info) { | |
| 478 | int err; | |
| 479 | ||
| 480 | size_t argc = 2; | |
| 481 | js_value_t *argv[2]; | |
| 482 | ||
| 483 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 484 | assert(err == 0); | |
| 485 | ||
| 486 | assert(argc == 2); | |
| 487 | ||
| 488 | uint32_t pid; | |
| 489 | err = js_get_value_uint32(env, argv[0], &pid); | |
| 490 | assert(err == 0); | |
| 491 | ||
| 492 | uint32_t signum; | |
| 493 | err = js_get_value_uint32(env, argv[1], &signum); | |
| 494 | assert(err == 0); | |
| 495 | ||
| 496 | err = uv_kill(pid, signum); | |
| 497 | if (err < 0) { | |
| 498 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 499 | assert(err == 0); | |
| 500 | ||
| 501 | return NULL; | |
| 502 | } | |
| 503 | ||
| 504 | return NULL; | |
| 505 | } | |
| 506 | ||
| 507 | static js_value_t * | |
| 508 | bare_os_available_parallelism(js_env_t *env, js_callback_info_t *info) { | |
| 509 | int err; | |
| 510 | ||
| 511 | js_value_t *result; | |
| 512 | err = js_create_int64(env, uv_available_parallelism(), &result); | |
| 513 | assert(err == 0); | |
| 514 | ||
| 515 | return result; | |
| 516 | } | |
| 517 | ||
| 518 | static js_value_t * | |
| 519 | bare_os_cpu_usage(js_env_t *env, js_callback_info_t *info) { | |
| 520 | int err; | |
| 521 | ||
| 522 | uv_rusage_t usage; | |
| 523 | err = uv_getrusage(&usage); | |
| 524 | assert(err == 0); | |
| 525 | ||
| 526 | js_value_t *result; | |
| 527 | err = js_create_object(env, &result); | |
| 528 | assert(err == 0); | |
| 529 | ||
| 530 | #define V(name, property) \ | |
| 531 | { \ | |
| 532 | uv_timeval_t time = usage.ru_##property; \ | |
| 533 | \ | |
| 534 | js_value_t *value; \ | |
| 535 | err = js_create_int64(env, time.tv_sec * 1e6 + time.tv_usec, &value); \ | |
| 536 | assert(err == 0); \ | |
| 537 | \ | |
| 538 | err = js_set_named_property(env, result, name, value); \ | |
| 539 | assert(err == 0); \ | |
| 540 | } | |
| 541 | ||
| 542 | V("user", utime) | |
| 543 | V("system", stime) | |
| 544 | #undef V | |
| 545 | ||
| 546 | return result; | |
| 547 | } | |
| 548 | ||
| 549 | static js_value_t * | |
| 550 | bare_os_cpu_usage_thread(js_env_t *env, js_callback_info_t *info) { | |
| 551 | int err; | |
| 552 | ||
| 553 | uv_rusage_t usage; | |
| 554 | err = uv_getrusage_thread(&usage); | |
| 555 | assert(err == 0); | |
| 556 | ||
| 557 | js_value_t *result; | |
| 558 | err = js_create_object(env, &result); | |
| 559 | assert(err == 0); | |
| 560 | ||
| 561 | #define V(name, property) \ | |
| 562 | { \ | |
| 563 | uv_timeval_t time = usage.ru_##property; \ | |
| 564 | \ | |
| 565 | js_value_t *value; \ | |
| 566 | err = js_create_int64(env, time.tv_sec * 1e6 + time.tv_usec, &value); \ | |
| 567 | assert(err == 0); \ | |
| 568 | \ | |
| 569 | err = js_set_named_property(env, result, name, value); \ | |
| 570 | assert(err == 0); \ | |
| 571 | } | |
| 572 | ||
| 573 | V("user", utime) | |
| 574 | V("system", stime) | |
| 575 | #undef V | |
| 576 | ||
| 577 | return result; | |
| 578 | } | |
| 579 | ||
| 580 | static js_value_t * | |
| 581 | bare_os_resource_usage(js_env_t *env, js_callback_info_t *info) { | |
| 582 | int err; | |
| 583 | ||
| 584 | uv_rusage_t usage; | |
| 585 | err = uv_getrusage(&usage); | |
| 586 | assert(err == 0); | |
| 587 | ||
| 588 | js_value_t *result; | |
| 589 | err = js_create_object(env, &result); | |
| 590 | assert(err == 0); | |
| 591 | ||
| 592 | #define V(name, property) \ | |
| 593 | { \ | |
| 594 | uv_timeval_t time = usage.ru_##property; \ | |
| 595 | \ | |
| 596 | js_value_t *value; \ | |
| 597 | err = js_create_int64(env, time.tv_sec * 1e6 + time.tv_usec, &value); \ | |
| 598 | assert(err == 0); \ | |
| 599 | \ | |
| 600 | err = js_set_named_property(env, result, name, value); \ | |
| 601 | assert(err == 0); \ | |
| 602 | } | |
| 603 | ||
| 604 | V("userCPUTime", utime) | |
| 605 | V("systemCPUTime", stime) | |
| 606 | #undef V | |
| 607 | ||
| 608 | #define V(name, property) \ | |
| 609 | { \ | |
| 610 | js_value_t *value; \ | |
| 611 | err = js_create_int64(env, usage.ru_##property, &value); \ | |
| 612 | assert(err == 0); \ | |
| 613 | \ | |
| 614 | err = js_set_named_property(env, result, name, value); \ | |
| 615 | assert(err == 0); \ | |
| 616 | } | |
| 617 | ||
| 618 | V("maxRSS", maxrss) | |
| 619 | V("sharedMemorySize", ixrss) | |
| 620 | V("unsharedDataSize", idrss) | |
| 621 | V("unsharedStackSize", isrss) | |
| 622 | V("minorPageFault", minflt) | |
| 623 | V("majorPageFault", majflt) | |
| 624 | V("swappedOut", nswap) | |
| 625 | V("fsRead", inblock) | |
| 626 | V("fsWrite", oublock) | |
| 627 | V("ipcSent", msgsnd) | |
| 628 | V("ipcReceived", msgrcv) | |
| 629 | V("signalsCount", nsignals) | |
| 630 | V("voluntaryContextSwitches", nvcsw) | |
| 631 | V("involuntaryContextSwitches", nivcsw) | |
| 632 | #undef V | |
| 633 | ||
| 634 | return result; | |
| 635 | } | |
| 636 | ||
| 637 | static js_value_t * | |
| 638 | bare_os_memory_usage(js_env_t *env, js_callback_info_t *info) { | |
| 639 | int err; | |
| 640 | ||
| 641 | js_heap_statistics_t stats = { | |
| 642 | .version = 1, | |
| 643 | ||
| 644 | // Since 0 | |
| 645 | .total_heap_size = -1, | |
| 646 | .used_heap_size = -1, | |
| 647 | ||
| 648 | // Since 1 | |
| 649 | .external_memory = -1, | |
| 650 | }; | |
| 651 | ||
| 652 | err = js_get_heap_statistics(env, &stats); | |
| 653 | assert(err == 0); | |
| 654 | ||
| 655 | js_value_t *result; | |
| 656 | err = js_create_object(env, &result); | |
| 657 | assert(err == 0); | |
| 658 | ||
| 659 | size_t rss; | |
| 660 | err = uv_resident_set_memory(&rss); | |
| 661 | assert(err == 0); | |
| 662 | ||
| 663 | js_value_t *value; | |
| 664 | err = js_create_int64(env, rss, &value); | |
| 665 | assert(err == 0); | |
| 666 | ||
| 667 | err = js_set_named_property(env, result, "rss", value); | |
| 668 | assert(err == 0); | |
| 669 | ||
| 670 | #define V(name, property) \ | |
| 671 | { \ | |
| 672 | if (stats.property != (size_t) -1) { \ | |
| 673 | js_value_t *value; \ | |
| 674 | err = js_create_int64(env, stats.property, &value); \ | |
| 675 | assert(err == 0); \ | |
| 676 | \ | |
| 677 | err = js_set_named_property(env, result, name, value); \ | |
| 678 | assert(err == 0); \ | |
| 679 | } \ | |
| 680 | } | |
| 681 | ||
| 682 | V("heapTotal", total_heap_size) | |
| 683 | V("heapUsed", used_heap_size) | |
| 684 | V("external", external_memory) | |
| 685 | #undef V | |
| 686 | ||
| 687 | return result; | |
| 688 | } | |
| 689 | ||
| 690 | static js_value_t * | |
| 691 | bare_os_freemem(js_env_t *env, js_callback_info_t *info) { | |
| 692 | int err; | |
| 693 | ||
| 694 | js_value_t *result; | |
| 695 | err = js_create_int64(env, uv_get_free_memory(), &result); | |
| 696 | assert(err == 0); | |
| 697 | ||
| 698 | return result; | |
| 699 | } | |
| 700 | ||
| 701 | static js_value_t * | |
| 702 | bare_os_totalmem(js_env_t *env, js_callback_info_t *info) { | |
| 703 | int err; | |
| 704 | ||
| 705 | js_value_t *result; | |
| 706 | err = js_create_int64(env, uv_get_total_memory(), &result); | |
| 707 | assert(err == 0); | |
| 708 | ||
| 709 | return result; | |
| 710 | } | |
| 711 | ||
| 712 | static js_value_t * | |
| 713 | bare_os_available_memory(js_env_t *env, js_callback_info_t *info) { | |
| 714 | int err; | |
| 715 | ||
| 716 | js_value_t *result; | |
| 717 | err = js_create_int64(env, uv_get_available_memory(), &result); | |
| 718 | assert(err == 0); | |
| 719 | ||
| 720 | return result; | |
| 721 | } | |
| 722 | ||
| 723 | static js_value_t * | |
| 724 | bare_os_constrained_memory(js_env_t *env, js_callback_info_t *info) { | |
| 725 | int err; | |
| 726 | ||
| 727 | js_value_t *result; | |
| 728 | err = js_create_int64(env, uv_get_constrained_memory(), &result); | |
| 729 | assert(err == 0); | |
| 730 | ||
| 731 | return result; | |
| 732 | } | |
| 733 | ||
| 734 | static js_value_t * | |
| 735 | bare_os_uptime(js_env_t *env, js_callback_info_t *info) { | |
| 736 | int err; | |
| 737 | ||
| 738 | double uptime; | |
| 739 | err = uv_uptime(&uptime); | |
| 740 | assert(err == 0); | |
| 741 | ||
| 742 | js_value_t *result; | |
| 743 | err = js_create_double(env, uptime, &result); | |
| 744 | assert(err == 0); | |
| 745 | ||
| 746 | return result; | |
| 747 | } | |
| 748 | ||
| 749 | static js_value_t * | |
| 750 | bare_os_loadavg(js_env_t *env, js_callback_info_t *info) { | |
| 751 | int err; | |
| 752 | ||
| 753 | double *data; | |
| 754 | ||
| 755 | js_value_t *arraybuffer; | |
| 756 | err = js_create_arraybuffer(env, sizeof(double) * 3, (void **) &data, &arraybuffer); | |
| 757 | assert(err == 0); | |
| 758 | ||
| 759 | js_value_t *result; | |
| 760 | err = js_create_typedarray(env, js_float64array, 3, arraybuffer, 0, &result); | |
| 761 | assert(err == 0); | |
| 762 | ||
| 763 | uv_loadavg(data); | |
| 764 | ||
| 765 | return result; | |
| 766 | } | |
| 767 | ||
| 768 | static js_value_t * | |
| 769 | bare_os_cpus(js_env_t *env, js_callback_info_t *info) { | |
| 770 | int err; | |
| 771 | ||
| 772 | uv_cpu_info_t *cpus; | |
| 773 | int len; | |
| 774 | err = uv_cpu_info(&cpus, &len); | |
| 775 | if (err < 0) { | |
| 776 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 777 | assert(err == 0); | |
| 778 | ||
| 779 | return NULL; | |
| 780 | } | |
| 781 | ||
| 782 | js_value_t *result; | |
| 783 | err = js_create_array_with_length(env, len, &result); | |
| 784 | assert(err == 0); | |
| 785 | ||
| 786 | for (uint32_t i = 0, n = len; i < n; i++) { | |
| 787 | uv_cpu_info_t cpu = cpus[i]; | |
| 788 | ||
| 789 | js_value_t *item; | |
| 790 | err = js_create_object(env, &item); | |
| 791 | assert(err == 0); | |
| 792 | ||
| 793 | err = js_set_element(env, result, i, item); | |
| 794 | assert(err == 0); | |
| 795 | ||
| 796 | #define V(name, type, ...) \ | |
| 797 | { \ | |
| 798 | js_value_t *val; \ | |
| 799 | err = type(env, ##__VA_ARGS__, &val); \ | |
| 800 | assert(err == 0); \ | |
| 801 | err = js_set_named_property(env, item, name, val); \ | |
| 802 | assert(err == 0); \ | |
| 803 | } | |
| 804 | ||
| 805 | V("model", js_create_string_utf8, (const utf8_t *) cpu.model, -1) | |
| 806 | V("speed", js_create_double, cpu.speed) | |
| 807 | V("times", js_create_object) | |
| 808 | #undef V | |
| 809 | ||
| 810 | js_value_t *times; | |
| 811 | err = js_get_named_property(env, item, "times", ×); | |
| 812 | assert(err == 0); | |
| 813 | ||
| 814 | #define V(name) \ | |
| 815 | { \ | |
| 816 | js_value_t *val; \ | |
| 817 | err = js_create_int64(env, cpu.cpu_times.name, &val); \ | |
| 818 | assert(err == 0); \ | |
| 819 | err = js_set_named_property(env, times, #name, val); \ | |
| 820 | assert(err == 0); \ | |
| 821 | } | |
| 822 | ||
| 823 | V(user) | |
| 824 | V(nice) | |
| 825 | V(sys) | |
| 826 | V(idle) | |
| 827 | V(irq) | |
| 828 | #undef V | |
| 829 | } | |
| 830 | ||
| 831 | uv_free_cpu_info(cpus, len); | |
| 832 | ||
| 833 | return result; | |
| 834 | } | |
| 835 | ||
| 836 | static js_value_t * | |
| 837 | bare_os_get_process_title(js_env_t *env, js_callback_info_t *info) { | |
| 838 | int err; | |
| 839 | ||
| 840 | char title[256]; | |
| 841 | err = uv_get_process_title(title, 256); | |
| 842 | if (err < 0) { | |
| 843 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 844 | assert(err == 0); | |
| 845 | ||
| 846 | return NULL; | |
| 847 | } | |
| 848 | ||
| 849 | js_value_t *result; | |
| 850 | err = js_create_string_utf8(env, (utf8_t *) title, -1, &result); | |
| 851 | assert(err == 0); | |
| 852 | ||
| 853 | return result; | |
| 854 | } | |
| 855 | ||
| 856 | static js_value_t * | |
| 857 | bare_os_set_process_title(js_env_t *env, js_callback_info_t *info) { | |
| 858 | int err; | |
| 859 | ||
| 860 | size_t argc = 1; | |
| 861 | js_value_t *argv[1]; | |
| 862 | ||
| 863 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 864 | assert(err == 0); | |
| 865 | ||
| 866 | assert(argc == 1); | |
| 867 | ||
| 868 | utf8_t data[256]; | |
| 869 | err = js_get_value_string_utf8(env, argv[0], data, 256, NULL); | |
| 870 | assert(err == 0); | |
| 871 | ||
| 872 | err = uv_set_process_title((char *) data); | |
| 873 | assert(err == 0); | |
| 874 | ||
| 875 | return NULL; | |
| 876 | } | |
| 877 | ||
| 878 | static js_value_t * | |
| 879 | bare_os_get_priority(js_env_t *env, js_callback_info_t *info) { | |
| 880 | int err; | |
| 881 | ||
| 882 | size_t argc = 1; | |
| 883 | js_value_t *argv[1]; | |
| 884 | ||
| 885 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 886 | assert(err == 0); | |
| 887 | ||
| 888 | assert(argc == 1); | |
| 889 | ||
| 890 | uv_pid_t pid; | |
| 891 | err = js_get_value_int32(env, argv[0], &pid); | |
| 892 | assert(err == 0); | |
| 893 | ||
| 894 | int priority; | |
| 895 | err = uv_os_getpriority(pid, &priority); | |
| 896 | if (err < 0) { | |
| 897 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 898 | assert(err == 0); | |
| 899 | ||
| 900 | return NULL; | |
| 901 | } | |
| 902 | ||
| 903 | js_value_t *result; | |
| 904 | err = js_create_int32(env, priority, &result); | |
| 905 | assert(err == 0); | |
| 906 | ||
| 907 | return result; | |
| 908 | } | |
| 909 | ||
| 910 | static js_value_t * | |
| 911 | bare_os_set_priority(js_env_t *env, js_callback_info_t *info) { | |
| 912 | int err; | |
| 913 | ||
| 914 | size_t argc = 2; | |
| 915 | js_value_t *argv[2]; | |
| 916 | ||
| 917 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 918 | assert(err == 0); | |
| 919 | ||
| 920 | assert(argc == 2); | |
| 921 | ||
| 922 | uv_pid_t pid; | |
| 923 | err = js_get_value_int32(env, argv[0], &pid); | |
| 924 | assert(err == 0); | |
| 925 | ||
| 926 | int priority; | |
| 927 | err = js_get_value_int32(env, argv[1], &priority); | |
| 928 | assert(err == 0); | |
| 929 | ||
| 930 | err = uv_os_setpriority(pid, priority); | |
| 931 | if (err < 0) { | |
| 932 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 933 | assert(err == 0); | |
| 934 | ||
| 935 | return NULL; | |
| 936 | } | |
| 937 | ||
| 938 | return NULL; | |
| 939 | } | |
| 940 | ||
| 941 | static js_value_t * | |
| 942 | bare_os_get_env_keys(js_env_t *env, js_callback_info_t *info) { | |
| 943 | int err; | |
| 944 | ||
| 945 | uv_env_item_t *items; | |
| 946 | int len; | |
| 947 | ||
| 948 | uv_rwlock_rdlock(&bare_os_env_lock); | |
| 949 | ||
| 950 | err = uv_os_environ(&items, &len); | |
| 951 | ||
| 952 | uv_rwlock_rdunlock(&bare_os_env_lock); | |
| 953 | ||
| 954 | if (err < 0) { | |
| 955 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 956 | assert(err == 0); | |
| 957 | ||
| 958 | return NULL; | |
| 959 | } | |
| 960 | ||
| 961 | js_value_t *result; | |
| 962 | err = js_create_array_with_length(env, len, &result); | |
| 963 | assert(err == 0); | |
| 964 | ||
| 965 | for (int i = 0; i < len; i++) { | |
| 966 | uv_env_item_t *item = &items[i]; | |
| 967 | ||
| 968 | js_value_t *val; | |
| 969 | err = js_create_string_utf8(env, (utf8_t *) item->name, -1, &val); | |
| 970 | assert(err == 0); | |
| 971 | ||
| 972 | err = js_set_element(env, result, i, val); | |
| 973 | assert(err == 0); | |
| 974 | } | |
| 975 | ||
| 976 | uv_os_free_environ(items, len); | |
| 977 | ||
| 978 | return result; | |
| 979 | } | |
| 980 | ||
| 981 | static js_value_t * | |
| 982 | bare_os_get_env(js_env_t *env, js_callback_info_t *info) { | |
| 983 | int err; | |
| 984 | ||
| 985 | size_t argc = 1; | |
| 986 | js_value_t *argv[1]; | |
| 987 | ||
| 988 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 989 | assert(err == 0); | |
| 990 | ||
| 991 | assert(argc == 1); | |
| 992 | ||
| 993 | size_t name_len; | |
| 994 | err = js_get_value_string_utf8(env, argv[0], NULL, 0, &name_len); | |
| 995 | assert(err == 0); | |
| 996 | ||
| 997 | name_len += 1 /* NULL */; | |
| 998 | ||
| 999 | utf8_t *name = malloc(name_len); | |
| 1000 | err = js_get_value_string_utf8(env, argv[0], name, name_len, &name_len); | |
| 1001 | assert(err == 0); | |
| 1002 | ||
| 1003 | uv_rwlock_rdlock(&bare_os_env_lock); | |
| 1004 | ||
| 1005 | size_t value_len = 256; | |
| 1006 | char *value = malloc(value_len); | |
| 1007 | err = uv_os_getenv((char *) name, value, &value_len); | |
| 1008 | ||
| 1009 | js_value_t *result; | |
| 1010 | ||
| 1011 | if (err == UV_ENOENT) { | |
| 1012 | err = js_get_undefined(env, &result); | |
| 1013 | assert(err == 0); | |
| 1014 | } else { | |
| 1015 | if (err == UV_ENOBUFS) { | |
| 1016 | value = realloc(value, value_len); | |
| 1017 | ||
| 1018 | err = uv_os_getenv((char *) name, value, &value_len); | |
| 1019 | assert(err == 0); | |
| 1020 | } else if (err < 0) { | |
| 1021 | uv_rwlock_rdunlock(&bare_os_env_lock); | |
| 1022 | ||
| 1023 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1024 | assert(err == 0); | |
| 1025 | ||
| 1026 | free(name); | |
| 1027 | return NULL; | |
| 1028 | } | |
| 1029 | ||
| 1030 | err = js_create_string_utf8(env, (utf8_t *) value, value_len, &result); | |
| 1031 | assert(err == 0); | |
| 1032 | } | |
| 1033 | ||
| 1034 | uv_rwlock_rdunlock(&bare_os_env_lock); | |
| 1035 | ||
| 1036 | free(name); | |
| 1037 | free(value); | |
| 1038 | ||
| 1039 | return result; | |
| 1040 | } | |
| 1041 | ||
| 1042 | static js_value_t * | |
| 1043 | bare_os_has_env(js_env_t *env, js_callback_info_t *info) { | |
| 1044 | int err; | |
| 1045 | ||
| 1046 | size_t argc = 1; | |
| 1047 | js_value_t *argv[1]; | |
| 1048 | ||
| 1049 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1050 | assert(err == 0); | |
| 1051 | ||
| 1052 | assert(argc == 1); | |
| 1053 | ||
| 1054 | size_t name_len; | |
| 1055 | err = js_get_value_string_utf8(env, argv[0], NULL, 0, &name_len); | |
| 1056 | assert(err == 0); | |
| 1057 | ||
| 1058 | utf8_t *name = malloc(++name_len); | |
| 1059 | err = js_get_value_string_utf8(env, argv[0], name, name_len, &name_len); | |
| 1060 | assert(err == 0); | |
| 1061 | ||
| 1062 | uv_rwlock_rdlock(&bare_os_env_lock); | |
| 1063 | ||
| 1064 | size_t value_len = 1; | |
| 1065 | char value[1]; | |
| 1066 | err = uv_os_getenv((char *) name, value, &value_len); | |
| 1067 | ||
| 1068 | uv_rwlock_rdunlock(&bare_os_env_lock); | |
| 1069 | ||
| 1070 | if (err != 0 && err != UV_ENOENT && err != UV_ENOBUFS) { | |
| 1071 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1072 | assert(err == 0); | |
| 1073 | ||
| 1074 | free(name); | |
| 1075 | return NULL; | |
| 1076 | } | |
| 1077 | ||
| 1078 | free(name); | |
| 1079 | ||
| 1080 | js_value_t *result; | |
| 1081 | err = js_get_boolean(env, err != UV_ENOENT, &result); | |
| 1082 | assert(err == 0); | |
| 1083 | ||
| 1084 | return result; | |
| 1085 | } | |
| 1086 | ||
| 1087 | static js_value_t * | |
| 1088 | bare_os_set_env(js_env_t *env, js_callback_info_t *info) { | |
| 1089 | int err; | |
| 1090 | ||
| 1091 | size_t argc = 2; | |
| 1092 | js_value_t *argv[2]; | |
| 1093 | ||
| 1094 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1095 | assert(err == 0); | |
| 1096 | ||
| 1097 | assert(argc == 2); | |
| 1098 | ||
| 1099 | size_t name_len; | |
| 1100 | err = js_get_value_string_utf8(env, argv[0], NULL, 0, &name_len); | |
| 1101 | assert(err == 0); | |
| 1102 | ||
| 1103 | utf8_t *name = malloc(++name_len); | |
| 1104 | err = js_get_value_string_utf8(env, argv[0], name, name_len, &name_len); | |
| 1105 | assert(err == 0); | |
| 1106 | ||
| 1107 | size_t value_len; | |
| 1108 | err = js_get_value_string_utf8(env, argv[1], NULL, 0, &value_len); | |
| 1109 | assert(err == 0); | |
| 1110 | ||
| 1111 | utf8_t *value = malloc(++value_len); | |
| 1112 | err = js_get_value_string_utf8(env, argv[1], value, value_len, &value_len); | |
| 1113 | assert(err == 0); | |
| 1114 | ||
| 1115 | uv_rwlock_wrlock(&bare_os_env_lock); | |
| 1116 | ||
| 1117 | err = uv_os_setenv((char *) name, (char *) value); | |
| 1118 | ||
| 1119 | uv_rwlock_wrunlock(&bare_os_env_lock); | |
| 1120 | ||
| 1121 | if (err < 0) { | |
| 1122 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1123 | assert(err == 0); | |
| 1124 | ||
| 1125 | free(name); | |
| 1126 | free(value); | |
| 1127 | return NULL; | |
| 1128 | } | |
| 1129 | ||
| 1130 | free(name); | |
| 1131 | free(value); | |
| 1132 | ||
| 1133 | return NULL; | |
| 1134 | } | |
| 1135 | ||
| 1136 | static js_value_t * | |
| 1137 | bare_os_unset_env(js_env_t *env, js_callback_info_t *info) { | |
| 1138 | int err; | |
| 1139 | ||
| 1140 | size_t argc = 1; | |
| 1141 | js_value_t *argv[1]; | |
| 1142 | ||
| 1143 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1144 | assert(err == 0); | |
| 1145 | ||
| 1146 | assert(argc == 1); | |
| 1147 | ||
| 1148 | size_t name_len; | |
| 1149 | err = js_get_value_string_utf8(env, argv[0], NULL, 0, &name_len); | |
| 1150 | assert(err == 0); | |
| 1151 | ||
| 1152 | utf8_t *name = malloc(++name_len); | |
| 1153 | err = js_get_value_string_utf8(env, argv[0], name, name_len, &name_len); | |
| 1154 | assert(err == 0); | |
| 1155 | ||
| 1156 | uv_rwlock_wrlock(&bare_os_env_lock); | |
| 1157 | ||
| 1158 | err = uv_os_unsetenv((char *) name); | |
| 1159 | ||
| 1160 | uv_rwlock_wrunlock(&bare_os_env_lock); | |
| 1161 | ||
| 1162 | if (err < 0) { | |
| 1163 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1164 | assert(err == 0); | |
| 1165 | ||
| 1166 | free(name); | |
| 1167 | return NULL; | |
| 1168 | } | |
| 1169 | ||
| 1170 | free(name); | |
| 1171 | ||
| 1172 | return NULL; | |
| 1173 | } | |
| 1174 | ||
| 1175 | static js_value_t * | |
| 1176 | bare_os_exports(js_env_t *env, js_value_t *exports) { | |
| 1177 | uv_once(&bare_os_env_lock_guard, bare_os__on_env_lock_init); | |
| 1178 | ||
| 1179 | int err; | |
| 1180 | ||
| 1181 | #define V(name, str) \ | |
| 1182 | { \ | |
| 1183 | js_value_t *val; \ | |
| 1184 | err = js_create_string_utf8(env, (utf8_t *) str, -1, &val); \ | |
| 1185 | assert(err == 0); \ | |
| 1186 | err = js_set_named_property(env, exports, name, val); \ | |
| 1187 | assert(err == 0); \ | |
| 1188 | } | |
| 1189 | ||
| 1190 | V("platform", BARE_PLATFORM) | |
| 1191 | V("arch", BARE_ARCH) | |
| 1192 | #undef V | |
| 1193 | ||
| 1194 | #define V(name, fn) \ | |
| 1195 | { \ | |
| 1196 | js_value_t *val; \ | |
| 1197 | err = js_create_function(env, name, -1, fn, NULL, &val); \ | |
| 1198 | assert(err == 0); \ | |
| 1199 | err = js_set_named_property(env, exports, name, val); \ | |
| 1200 | assert(err == 0); \ | |
| 1201 | } | |
| 1202 | ||
| 1203 | V("type", bare_os_type) | |
| 1204 | V("version", bare_os_version) | |
| 1205 | V("release", bare_os_release) | |
| 1206 | V("machine", bare_os_machine) | |
| 1207 | V("execPath", bare_os_exec_path) | |
| 1208 | V("pid", bare_os_pid) | |
| 1209 | V("ppid", bare_os_ppid) | |
| 1210 | V("cwd", bare_os_cwd) | |
| 1211 | V("chdir", bare_os_chdir) | |
| 1212 | V("tmpdir", bare_os_tmpdir) | |
| 1213 | V("homedir", bare_os_homedir) | |
| 1214 | V("hostname", bare_os_hostname) | |
| 1215 | V("kill", bare_os_kill) | |
| 1216 | V("availableParallelism", bare_os_available_parallelism) | |
| 1217 | V("cpuUsage", bare_os_cpu_usage) | |
| 1218 | V("threadCpuUsage", bare_os_cpu_usage_thread) | |
| 1219 | V("resourceUsage", bare_os_resource_usage) | |
| 1220 | V("memoryUsage", bare_os_memory_usage) | |
| 1221 | V("freemem", bare_os_freemem) | |
| 1222 | V("totalmem", bare_os_totalmem) | |
| 1223 | V("availableMemory", bare_os_available_memory) | |
| 1224 | V("constrainedMemory", bare_os_constrained_memory) | |
| 1225 | V("uptime", bare_os_uptime) | |
| 1226 | V("loadavg", bare_os_loadavg) | |
| 1227 | V("cpus", bare_os_cpus) | |
| 1228 | V("getProcessTitle", bare_os_get_process_title) | |
| 1229 | V("setProcessTitle", bare_os_set_process_title) | |
| 1230 | V("getPriority", bare_os_get_priority) | |
| 1231 | V("setPriority", bare_os_set_priority) | |
| 1232 | V("getEnvKeys", bare_os_get_env_keys) | |
| 1233 | V("getEnv", bare_os_get_env) | |
| 1234 | V("hasEnv", bare_os_get_env) | |
| 1235 | V("setEnv", bare_os_set_env) | |
| 1236 | V("unsetEnv", bare_os_unset_env) | |
| 1237 | V("userInfo", bare_os_user_info) | |
| 1238 | V("networkInterfaces", bare_os_network_interfaces) | |
| 1239 | #undef V | |
| 1240 | ||
| 1241 | const union { | |
| 1242 | uint8_t u8[2]; | |
| 1243 | uint16_t u16; | |
| 1244 | } byte_order = {{1, 0}}; | |
| 1245 | ||
| 1246 | js_value_t *is_little_endian; | |
| 1247 | err = js_get_boolean(env, byte_order.u16 == 1, &is_little_endian); | |
| 1248 | assert(err == 0); | |
| 1249 | ||
| 1250 | err = js_set_named_property(env, exports, "isLittleEndian", is_little_endian); | |
| 1251 | assert(err == 0); | |
| 1252 | ||
| 1253 | js_value_t *signals; | |
| 1254 | err = js_create_object(env, &signals); | |
| 1255 | assert(err == 0); | |
| 1256 | ||
| 1257 | err = js_set_named_property(env, exports, "signals", signals); | |
| 1258 | assert(err == 0); | |
| 1259 | ||
| 1260 | #define V(name) \ | |
| 1261 | { \ | |
| 1262 | js_value_t *val; \ | |
| 1263 | err = js_create_uint32(env, name, &val); \ | |
| 1264 | assert(err == 0); \ | |
| 1265 | err = js_set_named_property(env, signals, #name, val); \ | |
| 1266 | assert(err == 0); \ | |
| 1267 | } | |
| 1268 | ||
| 1269 | #ifdef SIGHUP | |
| 1270 | V(SIGHUP); | |
| 1271 | #endif | |
| 1272 | #ifdef SIGINT | |
| 1273 | V(SIGINT); | |
| 1274 | #endif | |
| 1275 | #ifdef SIGQUIT | |
| 1276 | V(SIGQUIT); | |
| 1277 | #endif | |
| 1278 | #ifdef SIGILL | |
| 1279 | V(SIGILL); | |
| 1280 | #endif | |
| 1281 | #ifdef SIGTRAP | |
| 1282 | V(SIGTRAP); | |
| 1283 | #endif | |
| 1284 | #ifdef SIGABRT | |
| 1285 | V(SIGABRT); | |
| 1286 | #endif | |
| 1287 | #ifdef SIGIOT | |
| 1288 | V(SIGIOT); | |
| 1289 | #endif | |
| 1290 | #ifdef SIGBUS | |
| 1291 | V(SIGBUS); | |
| 1292 | #endif | |
| 1293 | #ifdef SIGFPE | |
| 1294 | V(SIGFPE); | |
| 1295 | #endif | |
| 1296 | #ifdef SIGKILL | |
| 1297 | V(SIGKILL); | |
| 1298 | #endif | |
| 1299 | #ifdef SIGUSR1 | |
| 1300 | V(SIGUSR1); | |
| 1301 | #endif | |
| 1302 | #ifdef SIGSEGV | |
| 1303 | V(SIGSEGV); | |
| 1304 | #endif | |
| 1305 | #ifdef SIGUSR2 | |
| 1306 | V(SIGUSR2); | |
| 1307 | #endif | |
| 1308 | #ifdef SIGPIPE | |
| 1309 | V(SIGPIPE); | |
| 1310 | #endif | |
| 1311 | #ifdef SIGALRM | |
| 1312 | V(SIGALRM); | |
| 1313 | #endif | |
| 1314 | #ifdef SIGTERM | |
| 1315 | V(SIGTERM); | |
| 1316 | #endif | |
| 1317 | #ifdef SIGCHLD | |
| 1318 | V(SIGCHLD); | |
| 1319 | #endif | |
| 1320 | #ifdef SIGSTKFLT | |
| 1321 | V(SIGSTKFLT); | |
| 1322 | #endif | |
| 1323 | #ifdef SIGCONT | |
| 1324 | V(SIGCONT); | |
| 1325 | #endif | |
| 1326 | #ifdef SIGSTOP | |
| 1327 | V(SIGSTOP); | |
| 1328 | #endif | |
| 1329 | #ifdef SIGTSTP | |
| 1330 | V(SIGTSTP); | |
| 1331 | #endif | |
| 1332 | #ifdef SIGBREAK | |
| 1333 | V(SIGBREAK); | |
| 1334 | #endif | |
| 1335 | #ifdef SIGTTIN | |
| 1336 | V(SIGTTIN); | |
| 1337 | #endif | |
| 1338 | #ifdef SIGTTOU | |
| 1339 | V(SIGTTOU); | |
| 1340 | #endif | |
| 1341 | #ifdef SIGURG | |
| 1342 | V(SIGURG); | |
| 1343 | #endif | |
| 1344 | #ifdef SIGXCPU | |
| 1345 | V(SIGXCPU); | |
| 1346 | #endif | |
| 1347 | #ifdef SIGXFSZ | |
| 1348 | V(SIGXFSZ); | |
| 1349 | #endif | |
| 1350 | #ifdef SIGVTALRM | |
| 1351 | V(SIGVTALRM); | |
| 1352 | #endif | |
| 1353 | #ifdef SIGPROF | |
| 1354 | V(SIGPROF); | |
| 1355 | #endif | |
| 1356 | #ifdef SIGWINCH | |
| 1357 | V(SIGWINCH); | |
| 1358 | #endif | |
| 1359 | #ifdef SIGIO | |
| 1360 | V(SIGIO); | |
| 1361 | #endif | |
| 1362 | #ifdef SIGPOLL | |
| 1363 | V(SIGPOLL); | |
| 1364 | #endif | |
| 1365 | #ifdef SIGLOST | |
| 1366 | V(SIGLOST); | |
| 1367 | #endif | |
| 1368 | #ifdef SIGPWR | |
| 1369 | V(SIGPWR); | |
| 1370 | #endif | |
| 1371 | #ifdef SIGINFO | |
| 1372 | V(SIGINFO); | |
| 1373 | #endif | |
| 1374 | #ifdef SIGSYS | |
| 1375 | V(SIGSYS); | |
| 1376 | #endif | |
| 1377 | #ifdef SIGUNUSED | |
| 1378 | V(SIGUNUSED); | |
| 1379 | #endif | |
| 1380 | #undef V | |
| 1381 | ||
| 1382 | js_value_t *errnos; | |
| 1383 | err = js_create_object(env, &errnos); | |
| 1384 | assert(err == 0); | |
| 1385 | ||
| 1386 | err = js_set_named_property(env, exports, "errnos", errnos); | |
| 1387 | assert(err == 0); | |
| 1388 | ||
| 1389 | #define V(name, msg) \ | |
| 1390 | { \ | |
| 1391 | js_value_t *val; \ | |
| 1392 | err = js_create_int32(env, UV_##name, &val); \ | |
| 1393 | assert(err == 0); \ | |
| 1394 | err = js_set_named_property(env, errnos, #name, val); \ | |
| 1395 | assert(err == 0); \ | |
| 1396 | } | |
| 1397 | ||
| 1398 | UV_ERRNO_MAP(V); | |
| 1399 | #undef V | |
| 1400 | ||
| 1401 | js_value_t *priority; | |
| 1402 | err = js_create_object(env, &priority); | |
| 1403 | assert(err == 0); | |
| 1404 | ||
| 1405 | err = js_set_named_property(env, exports, "priority", priority); | |
| 1406 | assert(err == 0); | |
| 1407 | ||
| 1408 | #define V(name) \ | |
| 1409 | { \ | |
| 1410 | js_value_t *val; \ | |
| 1411 | err = js_create_int32(env, UV_##name, &val); \ | |
| 1412 | assert(err == 0); \ | |
| 1413 | err = js_set_named_property(env, priority, #name, val); \ | |
| 1414 | assert(err == 0); \ | |
| 1415 | } | |
| 1416 | ||
| 1417 | V(PRIORITY_LOW); | |
| 1418 | V(PRIORITY_BELOW_NORMAL); | |
| 1419 | V(PRIORITY_NORMAL); | |
| 1420 | V(PRIORITY_ABOVE_NORMAL); | |
| 1421 | V(PRIORITY_HIGH); | |
| 1422 | V(PRIORITY_HIGHEST); | |
| 1423 | ||
| 1424 | return exports; | |
| 1425 | } | |
| 1426 | ||
| 1427 | BARE_MODULE(bare_os, bare_os_exports) |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require.addon() |
| … | … | @@ -0,0 +1,143 @@ |
| 1 | export const constants: { | |
| 2 | signals: Record<string, number> | |
| 3 | errnos: Record<string, number> | |
| 4 | priority: Record<string, number> | |
| 5 | } | |
| 6 | ||
| 7 | export const EOL: '\r\n' | '\n' | |
| 8 | ||
| 9 | export const devNull: '\\\\.\\nul' | '/dev/null' | |
| 10 | ||
| 11 | export function platform(): 'android' | 'darwin' | 'ios' | 'linux' | 'win32' | |
| 12 | ||
| 13 | export function arch(): 'arm' | 'arm64' | 'ia32' | 'x64' | |
| 14 | ||
| 15 | export function type(): string | |
| 16 | ||
| 17 | export function version(): string | |
| 18 | ||
| 19 | export function release(): string | |
| 20 | ||
| 21 | export function machine(): string | |
| 22 | ||
| 23 | export function execPath(): string | |
| 24 | ||
| 25 | export function pid(): number | |
| 26 | ||
| 27 | export function ppid(): number | |
| 28 | ||
| 29 | export function cwd(): string | |
| 30 | ||
| 31 | export function chdir(dir: string): string | |
| 32 | ||
| 33 | export function tmpdir(): string | |
| 34 | ||
| 35 | export function homedir(): string | |
| 36 | ||
| 37 | export function hostname(): string | |
| 38 | ||
| 39 | export interface NetworkInterface { | |
| 40 | address: string | |
| 41 | netmask: string | |
| 42 | family: 'IPv4' | 'IPv6' | |
| 43 | cidr: string | |
| 44 | mac: string | |
| 45 | internal: boolean | |
| 46 | scopeid?: number | |
| 47 | } | |
| 48 | ||
| 49 | export function networkInterfaces(): Record<string, NetworkInterface[]> | |
| 50 | ||
| 51 | export function kill(pid: number, signal?: string | number): void | |
| 52 | ||
| 53 | export interface UserInfo { | |
| 54 | uid: number | |
| 55 | gid: number | |
| 56 | username: string | |
| 57 | homedir: string | |
| 58 | shell: string | null | |
| 59 | } | |
| 60 | ||
| 61 | export function userInfo(): UserInfo | |
| 62 | ||
| 63 | export function endianness(): 'LE' | 'BE' | |
| 64 | ||
| 65 | export function availableParallelism(): number | |
| 66 | ||
| 67 | export interface CpuUsage { | |
| 68 | user: number | |
| 69 | system: number | |
| 70 | } | |
| 71 | ||
| 72 | export function cpuUsage(previous?: CpuUsage): CpuUsage | |
| 73 | ||
| 74 | export function threadCpuUsage(previous?: CpuUsage): CpuUsage | |
| 75 | ||
| 76 | export function resourceUsage(): { | |
| 77 | userCPUTime: number | |
| 78 | systemCPUTime: number | |
| 79 | maxRSS: number | |
| 80 | sharedMemorySize: number | |
| 81 | unsharedDataSize: number | |
| 82 | unsharedStackSize: number | |
| 83 | minorPageFault: number | |
| 84 | majorPageFault: number | |
| 85 | swappedOut: number | |
| 86 | fsRead: number | |
| 87 | fsWrite: number | |
| 88 | ipcSent: number | |
| 89 | ipcReceived: number | |
| 90 | signalsCount: number | |
| 91 | voluntaryContextSwitches: number | |
| 92 | involuntaryContextSwitches: number | |
| 93 | } | |
| 94 | ||
| 95 | export function memoryUsage(): { | |
| 96 | rss: number | |
| 97 | heapTotal: number | |
| 98 | heapUsed: number | |
| 99 | external: number | |
| 100 | } | |
| 101 | ||
| 102 | export function freemem(): number | |
| 103 | ||
| 104 | export function totalmem(): number | |
| 105 | ||
| 106 | export function availableMemory(): number | |
| 107 | ||
| 108 | export function constrainedMemory(): number | |
| 109 | ||
| 110 | export function uptime(): number | |
| 111 | ||
| 112 | export function loadavg(): ArrayLike<number> | |
| 113 | ||
| 114 | export function cpus(): { | |
| 115 | model: string | |
| 116 | speed: number | |
| 117 | times: { | |
| 118 | user: number | |
| 119 | nice: number | |
| 120 | sys: number | |
| 121 | idle: number | |
| 122 | irq: number | |
| 123 | } | |
| 124 | }[] | |
| 125 | ||
| 126 | export function getProcessTitle(): string | |
| 127 | ||
| 128 | export function setProcessTitle(title: unknown): void | |
| 129 | ||
| 130 | export function getPriority(pid?: number): number | |
| 131 | ||
| 132 | export function setPriority(priority: number): void | |
| 133 | export function setPriority(pid: number, priority: number): void | |
| 134 | ||
| 135 | export function getEnvKeys(): string[] | |
| 136 | ||
| 137 | export function getEnv(name: string): string | undefined | |
| 138 | ||
| 139 | export function hasEnv(name: string): boolean | |
| 140 | ||
| 141 | export function setEnv(name: string, value: string): void | |
| 142 | ||
| 143 | export function unsetEnv(name: string): void |
| … | … | @@ -0,0 +1,129 @@ |
| 1 | const binding = require('./binding') | |
| 2 | const errors = require('./lib/errors') | |
| 3 | const constants = require('./lib/constants') | |
| 4 | ||
| 5 | exports.constants = constants | |
| 6 | ||
| 7 | exports.EOL = binding.platform === 'win32' ? '\r\n' : '\n' | |
| 8 | ||
| 9 | exports.devNull = binding.platform === 'win32' ? '\\\\.\\nul' : '/dev/null' | |
| 10 | ||
| 11 | exports.platform = function platform() { | |
| 12 | return binding.platform | |
| 13 | } | |
| 14 | ||
| 15 | exports.arch = function arch() { | |
| 16 | return binding.arch | |
| 17 | } | |
| 18 | ||
| 19 | exports.type = binding.type | |
| 20 | exports.version = binding.version | |
| 21 | exports.release = binding.release | |
| 22 | exports.machine = binding.machine | |
| 23 | exports.execPath = binding.execPath | |
| 24 | exports.pid = binding.pid | |
| 25 | exports.ppid = binding.ppid | |
| 26 | exports.cwd = binding.cwd | |
| 27 | exports.chdir = binding.chdir | |
| 28 | exports.tmpdir = binding.tmpdir | |
| 29 | exports.homedir = binding.homedir | |
| 30 | exports.hostname = binding.hostname | |
| 31 | exports.userInfo = binding.userInfo | |
| 32 | ||
| 33 | exports.networkInterfaces = function networkInterfaces() { | |
| 34 | const result = {} | |
| 35 | ||
| 36 | for (const entry of binding.networkInterfaces()) { | |
| 37 | const { name, ...properties } = entry | |
| 38 | ||
| 39 | if (result[name]) result[name].push(properties) | |
| 40 | else result[name] = [properties] | |
| 41 | } | |
| 42 | ||
| 43 | return result | |
| 44 | } | |
| 45 | ||
| 46 | exports.kill = function kill(pid, signal = constants.signals.SIGTERM) { | |
| 47 | if (typeof signal === 'string') { | |
| 48 | if (signal in constants.signals === false) { | |
| 49 | throw errors.UNKNOWN_SIGNAL('Unknown signal: ' + signal) | |
| 50 | } | |
| 51 | ||
| 52 | signal = constants.signals[signal] | |
| 53 | } | |
| 54 | ||
| 55 | binding.kill(pid, signal) | |
| 56 | } | |
| 57 | ||
| 58 | exports.endianness = function endianness() { | |
| 59 | return binding.isLittleEndian ? 'LE' : 'BE' | |
| 60 | } | |
| 61 | ||
| 62 | exports.availableParallelism = binding.availableParallelism | |
| 63 | ||
| 64 | exports.cpuUsage = function cpuUsage(previous) { | |
| 65 | const current = binding.cpuUsage() | |
| 66 | ||
| 67 | if (previous) { | |
| 68 | return { | |
| 69 | user: current.user - previous.user, | |
| 70 | system: current.system - previous.system | |
| 71 | } | |
| 72 | } | |
| 73 | ||
| 74 | return current | |
| 75 | } | |
| 76 | ||
| 77 | exports.threadCpuUsage = function threadCpuUsage(previous) { | |
| 78 | const current = binding.threadCpuUsage() | |
| 79 | ||
| 80 | if (previous) { | |
| 81 | return { | |
| 82 | user: current.user - previous.user, | |
| 83 | system: current.system - previous.system | |
| 84 | } | |
| 85 | } | |
| 86 | ||
| 87 | return current | |
| 88 | } | |
| 89 | ||
| 90 | exports.resourceUsage = binding.resourceUsage | |
| 91 | exports.memoryUsage = binding.memoryUsage | |
| 92 | exports.freemem = binding.freemem | |
| 93 | exports.totalmem = binding.totalmem | |
| 94 | exports.availableMemory = binding.availableMemory | |
| 95 | exports.constrainedMemory = binding.constrainedMemory | |
| 96 | exports.uptime = binding.uptime | |
| 97 | exports.loadavg = binding.loadavg | |
| 98 | exports.cpus = binding.cpus | |
| 99 | ||
| 100 | exports.getProcessTitle = binding.getProcessTitle | |
| 101 | ||
| 102 | exports.setProcessTitle = function setProcessTitle(title) { | |
| 103 | if (typeof title !== 'string') title = title.toString() | |
| 104 | ||
| 105 | if (title.length >= 256) { | |
| 106 | throw errors.TITLE_OVERFLOW('Process title is too long') | |
| 107 | } | |
| 108 | ||
| 109 | binding.setProcessTitle(title) | |
| 110 | } | |
| 111 | ||
| 112 | exports.getPriority = function getPriority(pid = 0) { | |
| 113 | return binding.getPriority(pid) | |
| 114 | } | |
| 115 | ||
| 116 | exports.setPriority = function setPriority(pid, priority) { | |
| 117 | if (priority === undefined) { | |
| 118 | priority = pid | |
| 119 | pid = 0 | |
| 120 | } | |
| 121 | ||
| 122 | binding.setPriority(pid, priority) | |
| 123 | } | |
| 124 | ||
| 125 | exports.getEnvKeys = binding.getEnvKeys | |
| 126 | exports.getEnv = binding.getEnv | |
| 127 | exports.hasEnv = binding.hasEnv | |
| 128 | exports.setEnv = binding.setEnv | |
| 129 | exports.unsetEnv = binding.unsetEnv |
| … | … | @@ -0,0 +1,7 @@ |
| 1 | const binding = require('../binding') | |
| 2 | ||
| 3 | module.exports = { | |
| 4 | signals: binding.signals, | |
| 5 | errnos: binding.errnos, | |
| 6 | priority: binding.priority | |
| 7 | } |
| … | … | @@ -0,0 +1,22 @@ |
| 1 | module.exports = class OSError extends Error { | |
| 2 | constructor(msg, code, fn = OSError) { | |
| 3 | super(`${code}: ${msg}`) | |
| 4 | this.code = code | |
| 5 | ||
| 6 | if (Error.captureStackTrace) { | |
| 7 | Error.captureStackTrace(this, fn) | |
| 8 | } | |
| 9 | } | |
| 10 | ||
| 11 | get name() { | |
| 12 | return 'OSError' | |
| 13 | } | |
| 14 | ||
| 15 | static UNKNOWN_SIGNAL(msg) { | |
| 16 | return new OSError(msg, 'UNKNOWN_SIGNAL', OSError.UNKNOWN_SIGNAL) | |
| 17 | } | |
| 18 | ||
| 19 | static TITLE_OVERFLOW(msg) { | |
| 20 | return new OSError(msg, 'TITLE_OVERFLOW', OSError.TITLE_OVERFLOW) | |
| 21 | } | |
| 22 | } |
| … | … | @@ -0,0 +1,46 @@ |
| 1 | { | |
| 2 | "name": "bare-os", | |
| 3 | "version": "3.8.0", | |
| 4 | "description": "Operating system utilities for Javascript", | |
| 5 | "exports": { | |
| 6 | ".": { | |
| 7 | "types": "./index.d.ts", | |
| 8 | "default": "./index.js" | |
| 9 | }, | |
| 10 | "./package": "./package.json", | |
| 11 | "./constants": "./lib/constants.js", | |
| 12 | "./errors": "./lib/errors.js" | |
| 13 | }, | |
| 14 | "files": [ | |
| 15 | "index.js", | |
| 16 | "index.d.ts", | |
| 17 | "binding.c", | |
| 18 | "binding.js", | |
| 19 | "CMakeLists.txt", | |
| 20 | "lib", | |
| 21 | "prebuilds" | |
| 22 | ], | |
| 23 | "addon": true, | |
| 24 | "scripts": { | |
| 25 | "test": "prettier . --check && bare test.js" | |
| 26 | }, | |
| 27 | "repository": { | |
| 28 | "type": "git", | |
| 29 | "url": "git+https://github.com/holepunchto/bare-os.git" | |
| 30 | }, | |
| 31 | "author": "Holepunch", | |
| 32 | "license": "Apache-2.0", | |
| 33 | "bugs": { | |
| 34 | "url": "https://github.com/holepunchto/bare-os/issues" | |
| 35 | }, | |
| 36 | "homepage": "https://github.com/holepunchto/bare-os#readme", | |
| 37 | "engines": { | |
| 38 | "bare": ">=1.14.0" | |
| 39 | }, | |
| 40 | "devDependencies": { | |
| 41 | "brittle": "^3.1.1", | |
| 42 | "cmake-bare": "^1.1.6", | |
| 43 | "prettier": "^3.4.2", | |
| 44 | "prettier-config-holepunch": "^2.0.0" | |
| 45 | } | |
| 46 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,28 @@ |
| 1 | Copyright 2023 Holepunch Inc | |
| 2 | ||
| 3 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 | you may not use this file except in compliance with the License. | |
| 5 | You may obtain a copy of the License at | |
| 6 | ||
| 7 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | ||
| 9 | Unless required by applicable law or agreed to in writing, software | |
| 10 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 | See the License for the specific language governing permissions and | |
| 13 | limitations under the License. | |
| 14 | ||
| 15 | --- | |
| 16 | ||
| 17 | Copyright Joyent, Inc. and other Node contributors. | |
| 18 | ||
| 19 | Permission is hereby granted, free of charge, to any person obtaining a | |
| 20 | copy of this software and associated documentation files (the | |
| 21 | "Software"), to deal in the Software without restriction, including | |
| 22 | without limitation the rights to use, copy, modify, merge, publish, | |
| 23 | distribute, sublicense, and/or sell copies of the Software, and to permit | |
| 24 | persons to whom the Software is furnished to do so, subject to the | |
| 25 | following conditions: | |
| 26 | ||
| 27 | The above copyright notice and this permission notice shall be included | |
| 28 | in all copies or substantial portions of the Software. |
| … | … | @@ -0,0 +1,19 @@ |
| 1 | # bare-path | |
| 2 | ||
| 3 | Path manipulation library for JavaScript. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i bare-path | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const path = require('bare-path') | |
| 13 | ||
| 14 | path.join('foo', 'bar') // foo/bar on posix, foo\bar on windows | |
| 15 | ``` | |
| 16 | ||
| 17 | ## License | |
| 18 | ||
| 19 | Apache-2.0 |
| … | … | @@ -0,0 +1,11 @@ |
| 1 | /* global Bare */ | |
| 2 | ||
| 3 | // This export SHOULD NOT be shortened in any way as having the full | |
| 4 | // `module.exports = require(...)` statement is crucial for synthesizing | |
| 5 | // ESM exports. | |
| 6 | ||
| 7 | if (Bare.platform === 'win32') { | |
| 8 | module.exports = require('./lib/win32') | |
| 9 | } else { | |
| 10 | module.exports = require('./lib/posix') | |
| 11 | } |
| … | … | @@ -0,0 +1,11 @@ |
| 1 | module.exports = { | |
| 2 | CHAR_UPPERCASE_A: 0x41, | |
| 3 | CHAR_LOWERCASE_A: 0x61, | |
| 4 | CHAR_UPPERCASE_Z: 0x5a, | |
| 5 | CHAR_LOWERCASE_Z: 0x7a, | |
| 6 | CHAR_DOT: 0x2e, | |
| 7 | CHAR_FORWARD_SLASH: 0x2f, | |
| 8 | CHAR_BACKWARD_SLASH: 0x5c, | |
| 9 | CHAR_COLON: 0x3a, | |
| 10 | CHAR_QUESTION_MARK: 0x3f | |
| 11 | } |
| … | … | @@ -0,0 +1,242 @@ |
| 1 | const os = require('bare-os') | |
| 2 | ||
| 3 | const { normalizeString } = require('./shared') | |
| 4 | const { | |
| 5 | CHAR_DOT, | |
| 6 | CHAR_FORWARD_SLASH | |
| 7 | } = require('./constants') | |
| 8 | ||
| 9 | function isPosixPathSeparator (code) { | |
| 10 | return code === CHAR_FORWARD_SLASH | |
| 11 | } | |
| 12 | ||
| 13 | exports.win32 = require('./win32') | |
| 14 | exports.posix = exports | |
| 15 | ||
| 16 | exports.sep = '/' | |
| 17 | exports.delimiter = ':' | |
| 18 | ||
| 19 | exports.resolve = function resolve (...args) { | |
| 20 | let resolvedPath = '' | |
| 21 | let resolvedAbsolute = false | |
| 22 | ||
| 23 | for (let i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) { | |
| 24 | const path = i >= 0 ? args[i] : os.cwd() | |
| 25 | ||
| 26 | if (path.length === 0) { | |
| 27 | continue | |
| 28 | } | |
| 29 | ||
| 30 | resolvedPath = `${path}/${resolvedPath}` | |
| 31 | resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH | |
| 32 | } | |
| 33 | ||
| 34 | resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/', isPosixPathSeparator) | |
| 35 | ||
| 36 | if (resolvedAbsolute) { | |
| 37 | return `/${resolvedPath}` | |
| 38 | } | |
| 39 | ||
| 40 | return resolvedPath.length > 0 ? resolvedPath : '.' | |
| 41 | } | |
| 42 | ||
| 43 | exports.normalize = function normalize (path) { | |
| 44 | if (path.length === 0) return '.' | |
| 45 | ||
| 46 | const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH | |
| 47 | const trailingSeparator = path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH | |
| 48 | ||
| 49 | path = normalizeString(path, !isAbsolute, '/', isPosixPathSeparator) | |
| 50 | ||
| 51 | if (path.length === 0) { | |
| 52 | if (isAbsolute) return '/' | |
| 53 | return trailingSeparator ? './' : '.' | |
| 54 | } | |
| 55 | ||
| 56 | if (trailingSeparator) path += '/' | |
| 57 | ||
| 58 | return isAbsolute ? `/${path}` : path | |
| 59 | } | |
| 60 | ||
| 61 | exports.isAbsolute = function isAbsolute (path) { | |
| 62 | return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH | |
| 63 | } | |
| 64 | ||
| 65 | exports.join = function join (...args) { | |
| 66 | if (args.length === 0) return '.' | |
| 67 | let joined | |
| 68 | for (let i = 0; i < args.length; ++i) { | |
| 69 | const arg = args[i] | |
| 70 | if (arg.length > 0) { | |
| 71 | if (joined === undefined) joined = arg | |
| 72 | else joined += `/${arg}` | |
| 73 | } | |
| 74 | } | |
| 75 | if (joined === undefined) return '.' | |
| 76 | return exports.normalize(joined) | |
| 77 | } | |
| 78 | ||
| 79 | exports.relative = function relative (from, to) { | |
| 80 | if (from === to) return '' | |
| 81 | ||
| 82 | from = exports.resolve(from) | |
| 83 | to = exports.resolve(to) | |
| 84 | ||
| 85 | if (from === to) return '' | |
| 86 | ||
| 87 | const fromStart = 1 | |
| 88 | const fromEnd = from.length | |
| 89 | const fromLen = fromEnd - fromStart | |
| 90 | const toStart = 1 | |
| 91 | const toLen = to.length - toStart | |
| 92 | ||
| 93 | const length = (fromLen < toLen ? fromLen : toLen) | |
| 94 | let lastCommonSep = -1 | |
| 95 | let i = 0 | |
| 96 | for (; i < length; i++) { | |
| 97 | const fromCode = from.charCodeAt(fromStart + i) | |
| 98 | if (fromCode !== to.charCodeAt(toStart + i)) { | |
| 99 | break | |
| 100 | } else if (fromCode === CHAR_FORWARD_SLASH) { | |
| 101 | lastCommonSep = i | |
| 102 | } | |
| 103 | } | |
| 104 | if (i === length) { | |
| 105 | if (toLen > length) { | |
| 106 | if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) { | |
| 107 | return to.substring(toStart + i + 1) | |
| 108 | } | |
| 109 | if (i === 0) { | |
| 110 | return to.substring(toStart + i) | |
| 111 | } | |
| 112 | } else if (fromLen > length) { | |
| 113 | if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { | |
| 114 | lastCommonSep = i | |
| 115 | } else if (i === 0) { | |
| 116 | lastCommonSep = 0 | |
| 117 | } | |
| 118 | } | |
| 119 | } | |
| 120 | ||
| 121 | let out = '' | |
| 122 | for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { | |
| 123 | if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) { | |
| 124 | out += out.length === 0 ? '..' : '/..' | |
| 125 | } | |
| 126 | } | |
| 127 | ||
| 128 | return `${out}${to.substring(toStart + lastCommonSep)}` | |
| 129 | } | |
| 130 | ||
| 131 | exports.toNamespacedPath = function toNamespacedPath (path) { | |
| 132 | return path | |
| 133 | } | |
| 134 | ||
| 135 | exports.dirname = function dirname (path) { | |
| 136 | if (path.length === 0) return '.' | |
| 137 | const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH | |
| 138 | let end = -1 | |
| 139 | let matchedSlash = true | |
| 140 | for (let i = path.length - 1; i >= 1; --i) { | |
| 141 | if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { | |
| 142 | if (!matchedSlash) { | |
| 143 | end = i | |
| 144 | break | |
| 145 | } | |
| 146 | } else { | |
| 147 | matchedSlash = false | |
| 148 | } | |
| 149 | } | |
| 150 | ||
| 151 | if (end === -1) return hasRoot ? '/' : '.' | |
| 152 | if (hasRoot && end === 1) return '//' | |
| 153 | return path.substring(0, end) | |
| 154 | } | |
| 155 | ||
| 156 | exports.basename = function basename (path, suffix) { | |
| 157 | let start = 0 | |
| 158 | let end = -1 | |
| 159 | let matchedSlash = true | |
| 160 | ||
| 161 | if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) { | |
| 162 | if (suffix === path) { return '' } | |
| 163 | let extIdx = suffix.length - 1 | |
| 164 | let firstNonSlashEnd = -1 | |
| 165 | for (let i = path.length - 1; i >= 0; --i) { | |
| 166 | const code = path.charCodeAt(i) | |
| 167 | if (code === CHAR_FORWARD_SLASH) { | |
| 168 | if (!matchedSlash) { | |
| 169 | start = i + 1 | |
| 170 | break | |
| 171 | } | |
| 172 | } else { | |
| 173 | if (firstNonSlashEnd === -1) { | |
| 174 | matchedSlash = false | |
| 175 | firstNonSlashEnd = i + 1 | |
| 176 | } | |
| 177 | if (extIdx >= 0) { | |
| 178 | if (code === suffix.charCodeAt(extIdx)) { | |
| 179 | if (--extIdx === -1) { | |
| 180 | end = i | |
| 181 | } | |
| 182 | } else { | |
| 183 | extIdx = -1 | |
| 184 | end = firstNonSlashEnd | |
| 185 | } | |
| 186 | } | |
| 187 | } | |
| 188 | } | |
| 189 | ||
| 190 | if (start === end) end = firstNonSlashEnd | |
| 191 | else if (end === -1) end = path.length | |
| 192 | return path.substring(start, end) | |
| 193 | } | |
| 194 | ||
| 195 | for (let i = path.length - 1; i >= 0; --i) { | |
| 196 | if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { | |
| 197 | if (!matchedSlash) { | |
| 198 | start = i + 1 | |
| 199 | break | |
| 200 | } | |
| 201 | } else if (end === -1) { | |
| 202 | matchedSlash = false | |
| 203 | end = i + 1 | |
| 204 | } | |
| 205 | } | |
| 206 | ||
| 207 | if (end === -1) return '' | |
| 208 | return path.substring(start, end) | |
| 209 | } | |
| 210 | ||
| 211 | exports.extname = function extname (path) { | |
| 212 | let startDot = -1 | |
| 213 | let startPart = 0 | |
| 214 | let end = -1 | |
| 215 | let matchedSlash = true | |
| 216 | let preDotState = 0 | |
| 217 | for (let i = path.length - 1; i >= 0; --i) { | |
| 218 | const code = path.charCodeAt(i) | |
| 219 | if (code === CHAR_FORWARD_SLASH) { | |
| 220 | if (!matchedSlash) { | |
| 221 | startPart = i + 1 | |
| 222 | break | |
| 223 | } | |
| 224 | continue | |
| 225 | } | |
| 226 | if (end === -1) { | |
| 227 | matchedSlash = false | |
| 228 | end = i + 1 | |
| 229 | } | |
| 230 | if (code === CHAR_DOT) { | |
| 231 | if (startDot === -1) startDot = i | |
| 232 | else if (preDotState !== 1) preDotState = 1 | |
| 233 | } else if (startDot !== -1) { | |
| 234 | preDotState = -1 | |
| 235 | } | |
| 236 | } | |
| 237 | ||
| 238 | if (startDot === -1 || end === -1 || preDotState === 0 || (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)) { | |
| 239 | return '' | |
| 240 | } | |
| 241 | return path.substring(startDot, end) | |
| 242 | } |
| … | … | @@ -0,0 +1,67 @@ |
| 1 | const { | |
| 2 | CHAR_DOT, | |
| 3 | CHAR_FORWARD_SLASH | |
| 4 | } = require('./constants') | |
| 5 | ||
| 6 | exports.normalizeString = function normalizeString (path, allowAboveRoot, separator, isPathSeparator) { | |
| 7 | let res = '' | |
| 8 | let lastSegmentLength = 0 | |
| 9 | let lastSlash = -1 | |
| 10 | let dots = 0 | |
| 11 | let code = 0 | |
| 12 | for (let i = 0; i <= path.length; ++i) { | |
| 13 | if (i < path.length) { | |
| 14 | code = path.charCodeAt(i) | |
| 15 | } else if (isPathSeparator(code)) { | |
| 16 | break | |
| 17 | } else { | |
| 18 | code = CHAR_FORWARD_SLASH | |
| 19 | } | |
| 20 | ||
| 21 | if (isPathSeparator(code)) { | |
| 22 | if (lastSlash === i - 1 || dots === 1) ; | |
| 23 | else if (dots === 2) { | |
| 24 | if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== CHAR_DOT || res.charCodeAt(res.length - 2) !== CHAR_DOT) { | |
| 25 | if (res.length > 2) { | |
| 26 | const lastSlashIndex = res.lastIndexOf(separator) | |
| 27 | if (lastSlashIndex === -1) { | |
| 28 | res = '' | |
| 29 | lastSegmentLength = 0 | |
| 30 | } else { | |
| 31 | res = res.substring(0, lastSlashIndex) | |
| 32 | lastSegmentLength = | |
| 33 | res.length - 1 - res.lastIndexOf(separator) | |
| 34 | } | |
| 35 | lastSlash = i | |
| 36 | dots = 0 | |
| 37 | continue | |
| 38 | } else if (res.length !== 0) { | |
| 39 | res = '' | |
| 40 | lastSegmentLength = 0 | |
| 41 | lastSlash = i | |
| 42 | dots = 0 | |
| 43 | continue | |
| 44 | } | |
| 45 | } | |
| 46 | if (allowAboveRoot) { | |
| 47 | res += res.length > 0 ? `${separator}..` : '..' | |
| 48 | lastSegmentLength = 2 | |
| 49 | } | |
| 50 | } else { | |
| 51 | if (res.length > 0) { | |
| 52 | res += `${separator}${path.substring(lastSlash + 1, i)}` | |
| 53 | } else { | |
| 54 | res = path.substring(lastSlash + 1, i) | |
| 55 | } | |
| 56 | lastSegmentLength = i - lastSlash - 1 | |
| 57 | } | |
| 58 | lastSlash = i | |
| 59 | dots = 0 | |
| 60 | } else if (code === CHAR_DOT && dots !== -1) { | |
| 61 | ++dots | |
| 62 | } else { | |
| 63 | dots = -1 | |
| 64 | } | |
| 65 | } | |
| 66 | return res | |
| 67 | } |
| … | … | @@ -0,0 +1,513 @@ |
| 1 | const os = require('bare-os') | |
| 2 | ||
| 3 | const { normalizeString } = require('./shared') | |
| 4 | const { | |
| 5 | CHAR_UPPERCASE_A, | |
| 6 | CHAR_LOWERCASE_A, | |
| 7 | CHAR_UPPERCASE_Z, | |
| 8 | CHAR_LOWERCASE_Z, | |
| 9 | CHAR_DOT, | |
| 10 | CHAR_FORWARD_SLASH, | |
| 11 | CHAR_BACKWARD_SLASH, | |
| 12 | CHAR_COLON, | |
| 13 | CHAR_QUESTION_MARK | |
| 14 | } = require('./constants') | |
| 15 | ||
| 16 | function isWindowsPathSeparator (code) { | |
| 17 | return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH | |
| 18 | } | |
| 19 | ||
| 20 | function isWindowsDeviceRoot (code) { | |
| 21 | return (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) || | |
| 22 | (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z) | |
| 23 | } | |
| 24 | ||
| 25 | exports.posix = require('./posix') | |
| 26 | exports.win32 = exports | |
| 27 | ||
| 28 | exports.sep = '\\' | |
| 29 | exports.delimiter = ';' | |
| 30 | ||
| 31 | exports.resolve = function resolve (...args) { | |
| 32 | let resolvedDevice = '' | |
| 33 | let resolvedTail = '' | |
| 34 | let resolvedAbsolute = false | |
| 35 | ||
| 36 | for (let i = args.length - 1; i >= -1; i--) { | |
| 37 | let path | |
| 38 | if (i >= 0) { | |
| 39 | path = args[i] | |
| 40 | ||
| 41 | if (path.length === 0) continue | |
| 42 | } else if (resolvedDevice.length === 0) { | |
| 43 | path = os.cwd() | |
| 44 | } else { | |
| 45 | path = os.getEnv(`=${resolvedDevice}`) || os.cwd() | |
| 46 | ||
| 47 | if (path === undefined || (path.substring(0, 2).toLowerCase() !== resolvedDevice.toLowerCase() && path.charCodeAt(2) === CHAR_BACKWARD_SLASH)) { | |
| 48 | path = `${resolvedDevice}\\` | |
| 49 | } | |
| 50 | } | |
| 51 | ||
| 52 | const len = path.length | |
| 53 | let rootEnd = 0 | |
| 54 | let device = '' | |
| 55 | let isAbsolute = false | |
| 56 | const code = path.charCodeAt(0) | |
| 57 | ||
| 58 | if (len === 1) { | |
| 59 | if (isWindowsPathSeparator(code)) { | |
| 60 | rootEnd = 1 | |
| 61 | isAbsolute = true | |
| 62 | } | |
| 63 | } else if (isWindowsPathSeparator(code)) { | |
| 64 | isAbsolute = true | |
| 65 | ||
| 66 | if (isWindowsPathSeparator(path.charCodeAt(1))) { | |
| 67 | let j = 2 | |
| 68 | let last = j | |
| 69 | while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) { | |
| 70 | j++ | |
| 71 | } | |
| 72 | if (j < len && j !== last) { | |
| 73 | const firstPart = path.substring(last, j) | |
| 74 | last = j | |
| 75 | while (j < len && isWindowsPathSeparator(path.charCodeAt(j))) { | |
| 76 | j++ | |
| 77 | } | |
| 78 | if (j < len && j !== last) { | |
| 79 | last = j | |
| 80 | while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) { | |
| 81 | j++ | |
| 82 | } | |
| 83 | if (j === len || j !== last) { | |
| 84 | device = `\\\\${firstPart}\\${path.substring(last, j)}` | |
| 85 | rootEnd = j | |
| 86 | } | |
| 87 | } | |
| 88 | } | |
| 89 | } else { | |
| 90 | rootEnd = 1 | |
| 91 | } | |
| 92 | } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { | |
| 93 | device = path.substring(0, 2) | |
| 94 | rootEnd = 2 | |
| 95 | if (len > 2 && isWindowsPathSeparator(path.charCodeAt(2))) { | |
| 96 | isAbsolute = true | |
| 97 | rootEnd = 3 | |
| 98 | } | |
| 99 | } | |
| 100 | ||
| 101 | if (device.length > 0) { | |
| 102 | if (resolvedDevice.length > 0) { | |
| 103 | if (device.toLowerCase() !== resolvedDevice.toLowerCase()) { continue } | |
| 104 | } else { | |
| 105 | resolvedDevice = device | |
| 106 | } | |
| 107 | } | |
| 108 | ||
| 109 | if (resolvedAbsolute) { | |
| 110 | if (resolvedDevice.length > 0) { break } | |
| 111 | } else { | |
| 112 | resolvedTail = `${path.substring(rootEnd)}\\${resolvedTail}` | |
| 113 | resolvedAbsolute = isAbsolute | |
| 114 | if (isAbsolute && resolvedDevice.length > 0) { | |
| 115 | break | |
| 116 | } | |
| 117 | } | |
| 118 | } | |
| 119 | ||
| 120 | resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\', isWindowsPathSeparator) | |
| 121 | ||
| 122 | return resolvedAbsolute ? `${resolvedDevice}\\${resolvedTail}` : `${resolvedDevice}${resolvedTail}` || '.' | |
| 123 | } | |
| 124 | ||
| 125 | exports.normalize = function normalize (path) { | |
| 126 | const len = path.length | |
| 127 | if (len === 0) return '.' | |
| 128 | let rootEnd = 0 | |
| 129 | let device | |
| 130 | let isAbsolute = false | |
| 131 | const code = path.charCodeAt(0) | |
| 132 | ||
| 133 | if (len === 1) { | |
| 134 | return code === CHAR_FORWARD_SLASH ? '\\' : path | |
| 135 | } | |
| 136 | ||
| 137 | if (isWindowsPathSeparator(code)) { | |
| 138 | isAbsolute = true | |
| 139 | ||
| 140 | if (isWindowsPathSeparator(path.charCodeAt(1))) { | |
| 141 | let j = 2 | |
| 142 | let last = j | |
| 143 | while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) { | |
| 144 | j++ | |
| 145 | } | |
| 146 | if (j < len && j !== last) { | |
| 147 | const firstPart = path.substring(last, j) | |
| 148 | last = j | |
| 149 | while (j < len && isWindowsPathSeparator(path.charCodeAt(j))) { | |
| 150 | j++ | |
| 151 | } | |
| 152 | if (j < len && j !== last) { | |
| 153 | last = j | |
| 154 | while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) { | |
| 155 | j++ | |
| 156 | } | |
| 157 | if (j === len) { | |
| 158 | return `\\\\${firstPart}\\${path.substring(last)}\\` | |
| 159 | } | |
| 160 | if (j !== last) { | |
| 161 | device = `\\\\${firstPart}\\${path.substring(last, j)}` | |
| 162 | rootEnd = j | |
| 163 | } | |
| 164 | } | |
| 165 | } | |
| 166 | } else { | |
| 167 | rootEnd = 1 | |
| 168 | } | |
| 169 | } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { | |
| 170 | device = path.substring(0, 2) | |
| 171 | rootEnd = 2 | |
| 172 | if (len > 2 && isWindowsPathSeparator(path.charCodeAt(2))) { | |
| 173 | isAbsolute = true | |
| 174 | rootEnd = 3 | |
| 175 | } | |
| 176 | } | |
| 177 | ||
| 178 | let tail = rootEnd < len ? normalizeString(path.substring(rootEnd), !isAbsolute, '\\', isWindowsPathSeparator) : '' | |
| 179 | if (tail.length === 0 && !isAbsolute) { | |
| 180 | tail = '.' | |
| 181 | } | |
| 182 | if (tail.length > 0 && isWindowsPathSeparator(path.charCodeAt(len - 1))) { | |
| 183 | tail += '\\' | |
| 184 | } | |
| 185 | if (device === undefined) { | |
| 186 | return isAbsolute ? `\\${tail}` : tail | |
| 187 | } | |
| 188 | return isAbsolute ? `${device}\\${tail}` : `${device}${tail}` | |
| 189 | } | |
| 190 | ||
| 191 | exports.isAbsolute = function isAbsolute (path) { | |
| 192 | const len = path.length | |
| 193 | if (len === 0) return false | |
| 194 | ||
| 195 | const code = path.charCodeAt(0) | |
| 196 | ||
| 197 | return isWindowsPathSeparator(code) || (len > 2 && isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON && isWindowsPathSeparator(path.charCodeAt(2))) | |
| 198 | } | |
| 199 | ||
| 200 | exports.join = function join (...args) { | |
| 201 | if (args.length === 0) return '.' | |
| 202 | ||
| 203 | let joined | |
| 204 | let firstPart | |
| 205 | for (let i = 0; i < args.length; ++i) { | |
| 206 | const arg = args[i] | |
| 207 | if (arg.length > 0) { | |
| 208 | if (joined === undefined) joined = firstPart = arg | |
| 209 | else joined += `\\${arg}` | |
| 210 | } | |
| 211 | } | |
| 212 | ||
| 213 | if (joined === undefined) return '.' | |
| 214 | ||
| 215 | let needsReplace = true | |
| 216 | let slashCount = 0 | |
| 217 | if (isWindowsPathSeparator(firstPart.charCodeAt(0))) { | |
| 218 | ++slashCount | |
| 219 | const firstLen = firstPart.length | |
| 220 | if (firstLen > 1 && isWindowsPathSeparator(firstPart.charCodeAt(1))) { | |
| 221 | ++slashCount | |
| 222 | if (firstLen > 2) { | |
| 223 | if (isWindowsPathSeparator(firstPart.charCodeAt(2))) { | |
| 224 | ++slashCount | |
| 225 | } else { | |
| 226 | needsReplace = false | |
| 227 | } | |
| 228 | } | |
| 229 | } | |
| 230 | } | |
| 231 | if (needsReplace) { | |
| 232 | while (slashCount < joined.length && isWindowsPathSeparator(joined.charCodeAt(slashCount))) { | |
| 233 | slashCount++ | |
| 234 | } | |
| 235 | ||
| 236 | if (slashCount >= 2) { | |
| 237 | joined = `\\${joined.substring(slashCount)}` | |
| 238 | } | |
| 239 | } | |
| 240 | ||
| 241 | return exports.normalize(joined) | |
| 242 | } | |
| 243 | ||
| 244 | exports.relative = function relative (from, to) { | |
| 245 | if (from === to) return '' | |
| 246 | ||
| 247 | const fromOrig = exports.resolve(from) | |
| 248 | const toOrig = exports.resolve(to) | |
| 249 | ||
| 250 | if (fromOrig === toOrig) return '' | |
| 251 | ||
| 252 | from = fromOrig.toLowerCase() | |
| 253 | to = toOrig.toLowerCase() | |
| 254 | ||
| 255 | if (from === to) return '' | |
| 256 | ||
| 257 | let fromStart = 0 | |
| 258 | while (fromStart < from.length && from.charCodeAt(fromStart) === CHAR_BACKWARD_SLASH) { | |
| 259 | fromStart++ | |
| 260 | } | |
| 261 | let fromEnd = from.length | |
| 262 | while (fromEnd - 1 > fromStart && from.charCodeAt(fromEnd - 1) === CHAR_BACKWARD_SLASH) { | |
| 263 | fromEnd-- | |
| 264 | } | |
| 265 | const fromLen = fromEnd - fromStart | |
| 266 | ||
| 267 | let toStart = 0 | |
| 268 | while (toStart < to.length && to.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) { | |
| 269 | toStart++ | |
| 270 | } | |
| 271 | let toEnd = to.length | |
| 272 | while (toEnd - 1 > toStart && to.charCodeAt(toEnd - 1) === CHAR_BACKWARD_SLASH) { | |
| 273 | toEnd-- | |
| 274 | } | |
| 275 | const toLen = toEnd - toStart | |
| 276 | ||
| 277 | const length = fromLen < toLen ? fromLen : toLen | |
| 278 | let lastCommonSep = -1 | |
| 279 | let i = 0 | |
| 280 | for (; i < length; i++) { | |
| 281 | const fromCode = from.charCodeAt(fromStart + i) | |
| 282 | if (fromCode !== to.charCodeAt(toStart + i)) { | |
| 283 | break | |
| 284 | } else if (fromCode === CHAR_BACKWARD_SLASH) { | |
| 285 | lastCommonSep = i | |
| 286 | } | |
| 287 | } | |
| 288 | ||
| 289 | if (i !== length) { | |
| 290 | if (lastCommonSep === -1) return toOrig | |
| 291 | } else { | |
| 292 | if (toLen > length) { | |
| 293 | if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) { | |
| 294 | return toOrig.substring(toStart + i + 1) | |
| 295 | } | |
| 296 | if (i === 2) { | |
| 297 | return toOrig.substring(toStart + i) | |
| 298 | } | |
| 299 | } | |
| 300 | if (fromLen > length) { | |
| 301 | if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) { | |
| 302 | lastCommonSep = i | |
| 303 | } else if (i === 2) { | |
| 304 | lastCommonSep = 3 | |
| 305 | } | |
| 306 | } | |
| 307 | if (lastCommonSep === -1) lastCommonSep = 0 | |
| 308 | } | |
| 309 | ||
| 310 | let out = '' | |
| 311 | for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { | |
| 312 | if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) { | |
| 313 | out += out.length === 0 ? '..' : '\\..' | |
| 314 | } | |
| 315 | } | |
| 316 | ||
| 317 | toStart += lastCommonSep | |
| 318 | ||
| 319 | if (out.length > 0) { | |
| 320 | return `${out}${toOrig.substring(toStart, toEnd)}` | |
| 321 | } | |
| 322 | if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) { | |
| 323 | ++toStart | |
| 324 | } | |
| 325 | return toOrig.substring(toStart, toEnd) | |
| 326 | } | |
| 327 | ||
| 328 | exports.toNamespacedPath = function toNamespacedPath (path) { | |
| 329 | if (path.length === 0) return path | |
| 330 | ||
| 331 | const resolvedPath = exports.resolve(path) | |
| 332 | ||
| 333 | if (resolvedPath.length <= 2) return path | |
| 334 | ||
| 335 | if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) { | |
| 336 | if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) { | |
| 337 | const code = resolvedPath.charCodeAt(2) | |
| 338 | if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) { | |
| 339 | return `\\\\?\\UNC\\${resolvedPath.substring(2)}` | |
| 340 | } | |
| 341 | } | |
| 342 | } else if ( | |
| 343 | isWindowsDeviceRoot(resolvedPath.charCodeAt(0)) && | |
| 344 | resolvedPath.charCodeAt(1) === CHAR_COLON && | |
| 345 | resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH | |
| 346 | ) { | |
| 347 | return `\\\\?\\${resolvedPath}` | |
| 348 | } | |
| 349 | ||
| 350 | return path | |
| 351 | } | |
| 352 | ||
| 353 | exports.dirname = function dirname (path) { | |
| 354 | const len = path.length | |
| 355 | if (len === 0) return '.' | |
| 356 | let rootEnd = -1 | |
| 357 | let offset = 0 | |
| 358 | const code = path.charCodeAt(0) | |
| 359 | ||
| 360 | if (len === 1) { | |
| 361 | return isWindowsPathSeparator(code) ? path : '.' | |
| 362 | } | |
| 363 | ||
| 364 | if (isWindowsPathSeparator(code)) { | |
| 365 | rootEnd = offset = 1 | |
| 366 | ||
| 367 | if (isWindowsPathSeparator(path.charCodeAt(1))) { | |
| 368 | let j = 2 | |
| 369 | let last = j | |
| 370 | while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) { | |
| 371 | j++ | |
| 372 | } | |
| 373 | if (j < len && j !== last) { | |
| 374 | last = j | |
| 375 | while (j < len && isWindowsPathSeparator(path.charCodeAt(j))) { | |
| 376 | j++ | |
| 377 | } | |
| 378 | if (j < len && j !== last) { | |
| 379 | last = j | |
| 380 | while (j < len && !isWindowsPathSeparator(path.charCodeAt(j))) { | |
| 381 | j++ | |
| 382 | } | |
| 383 | if (j === len) { | |
| 384 | return path | |
| 385 | } | |
| 386 | if (j !== last) { | |
| 387 | rootEnd = offset = j + 1 | |
| 388 | } | |
| 389 | } | |
| 390 | } | |
| 391 | } | |
| 392 | } else if (isWindowsDeviceRoot(code) && path.charCodeAt(1) === CHAR_COLON) { | |
| 393 | rootEnd = len > 2 && isWindowsPathSeparator(path.charCodeAt(2)) ? 3 : 2 | |
| 394 | offset = rootEnd | |
| 395 | } | |
| 396 | ||
| 397 | let end = -1 | |
| 398 | let matchedSlash = true | |
| 399 | for (let i = len - 1; i >= offset; --i) { | |
| 400 | if (isWindowsPathSeparator(path.charCodeAt(i))) { | |
| 401 | if (!matchedSlash) { | |
| 402 | end = i | |
| 403 | break | |
| 404 | } | |
| 405 | } else { | |
| 406 | matchedSlash = false | |
| 407 | } | |
| 408 | } | |
| 409 | ||
| 410 | if (end === -1) { | |
| 411 | if (rootEnd === -1) return '.' | |
| 412 | ||
| 413 | end = rootEnd | |
| 414 | } | |
| 415 | return path.substring(0, end) | |
| 416 | } | |
| 417 | ||
| 418 | exports.basename = function basename (path, suffix) { | |
| 419 | let start = 0 | |
| 420 | let end = -1 | |
| 421 | let matchedSlash = true | |
| 422 | ||
| 423 | if (path.length >= 2 && isWindowsDeviceRoot(path.charCodeAt(0)) && path.charCodeAt(1) === CHAR_COLON) { | |
| 424 | start = 2 | |
| 425 | } | |
| 426 | ||
| 427 | if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) { | |
| 428 | if (suffix === path) return '' | |
| 429 | let extIdx = suffix.length - 1 | |
| 430 | let firstNonSlashEnd = -1 | |
| 431 | for (let i = path.length - 1; i >= start; --i) { | |
| 432 | const code = path.charCodeAt(i) | |
| 433 | if (isWindowsPathSeparator(code)) { | |
| 434 | if (!matchedSlash) { | |
| 435 | start = i + 1 | |
| 436 | break | |
| 437 | } | |
| 438 | } else { | |
| 439 | if (firstNonSlashEnd === -1) { | |
| 440 | matchedSlash = false | |
| 441 | firstNonSlashEnd = i + 1 | |
| 442 | } | |
| 443 | if (extIdx >= 0) { | |
| 444 | if (code === suffix.charCodeAt(extIdx)) { | |
| 445 | if (--extIdx === -1) { | |
| 446 | end = i | |
| 447 | } | |
| 448 | } else { | |
| 449 | extIdx = -1 | |
| 450 | end = firstNonSlashEnd | |
| 451 | } | |
| 452 | } | |
| 453 | } | |
| 454 | } | |
| 455 | ||
| 456 | if (start === end) end = firstNonSlashEnd | |
| 457 | else if (end === -1) end = path.length | |
| 458 | return path.substring(start, end) | |
| 459 | } | |
| 460 | for (let i = path.length - 1; i >= start; --i) { | |
| 461 | if (isWindowsPathSeparator(path.charCodeAt(i))) { | |
| 462 | if (!matchedSlash) { | |
| 463 | start = i + 1 | |
| 464 | break | |
| 465 | } | |
| 466 | } else if (end === -1) { | |
| 467 | matchedSlash = false | |
| 468 | end = i + 1 | |
| 469 | } | |
| 470 | } | |
| 471 | ||
| 472 | if (end === -1) return '' | |
| 473 | return path.substring(start, end) | |
| 474 | } | |
| 475 | ||
| 476 | exports.extname = function extname (path) { | |
| 477 | let start = 0 | |
| 478 | let startDot = -1 | |
| 479 | let startPart = 0 | |
| 480 | let end = -1 | |
| 481 | let matchedSlash = true | |
| 482 | let preDotState = 0 | |
| 483 | ||
| 484 | if (path.length >= 2 && path.charCodeAt(1) === CHAR_COLON && isWindowsDeviceRoot(path.charCodeAt(0))) { | |
| 485 | start = startPart = 2 | |
| 486 | } | |
| 487 | ||
| 488 | for (let i = path.length - 1; i >= start; --i) { | |
| 489 | const code = path.charCodeAt(i) | |
| 490 | if (isWindowsPathSeparator(code)) { | |
| 491 | if (!matchedSlash) { | |
| 492 | startPart = i + 1 | |
| 493 | break | |
| 494 | } | |
| 495 | continue | |
| 496 | } | |
| 497 | if (end === -1) { | |
| 498 | matchedSlash = false | |
| 499 | end = i + 1 | |
| 500 | } | |
| 501 | if (code === CHAR_DOT) { | |
| 502 | if (startDot === -1) startDot = i | |
| 503 | else if (preDotState !== 1) preDotState = 1 | |
| 504 | } else if (startDot !== -1) { | |
| 505 | preDotState = -1 | |
| 506 | } | |
| 507 | } | |
| 508 | ||
| 509 | if (startDot === -1 || end === -1 || preDotState === 0 || (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)) { | |
| 510 | return '' | |
| 511 | } | |
| 512 | return path.substring(startDot, end) | |
| 513 | } |
| … | … | @@ -0,0 +1,36 @@ |
| 1 | { | |
| 2 | "name": "bare-path", | |
| 3 | "version": "3.0.0", | |
| 4 | "description": "Path manipulation library for JavaScript", | |
| 5 | "exports": { | |
| 6 | ".": "./index.js", | |
| 7 | "./package": "./package.json", | |
| 8 | "./posix": "./lib/posix.js", | |
| 9 | "./win32": "./lib/win32.js" | |
| 10 | }, | |
| 11 | "files": [ | |
| 12 | "index.js", | |
| 13 | "lib", | |
| 14 | "NOTICE" | |
| 15 | ], | |
| 16 | "scripts": { | |
| 17 | "test": "standard && bare test.js" | |
| 18 | }, | |
| 19 | "repository": { | |
| 20 | "type": "git", | |
| 21 | "url": "git+https://github.com/holepunchto/bare-path.git" | |
| 22 | }, | |
| 23 | "author": "Holepunch", | |
| 24 | "license": "Apache-2.0", | |
| 25 | "bugs": { | |
| 26 | "url": "https://github.com/holepunchto/bare-path/issues" | |
| 27 | }, | |
| 28 | "homepage": "https://github.com/holepunchto/bare-path#readme", | |
| 29 | "dependencies": { | |
| 30 | "bare-os": "^3.0.1" | |
| 31 | }, | |
| 32 | "devDependencies": { | |
| 33 | "brittle": "^3.3.2", | |
| 34 | "standard": "^17.0.0" | |
| 35 | } | |
| 36 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,19 @@ |
| 1 | # bare-semver | |
| 2 | ||
| 3 | Minimal semantic versioning library for Bare. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i bare-semver | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const { Version } = require('bare-semver') | |
| 13 | ||
| 14 | Version.parse('1.2.3') | |
| 15 | ``` | |
| 16 | ||
| 17 | ## License | |
| 18 | ||
| 19 | Apache-2.0 |
| … | … | @@ -0,0 +1,61 @@ |
| 1 | export const constants: { EQ: 1; LT: 2; LTE: 3; GT: 4; GTE: 5 } | |
| 2 | ||
| 3 | declare class SemVerError extends Error { | |
| 4 | static INVALID_VERSION(msg: string, fn?: Function): SemVerError | |
| 5 | ||
| 6 | static INVALID_RANGE(msg: string, fn?: Function): SemVerError | |
| 7 | } | |
| 8 | ||
| 9 | export { SemVerError as errors } | |
| 10 | ||
| 11 | export function satisfies(version: Version, range: Range): boolean | |
| 12 | ||
| 13 | export class Version { | |
| 14 | constructor( | |
| 15 | major: number, | |
| 16 | minor: number, | |
| 17 | patch: number, | |
| 18 | opts?: { prerelease?: string[]; build?: string[] } | |
| 19 | ) | |
| 20 | ||
| 21 | major: number | |
| 22 | minor: number | |
| 23 | patch: number | |
| 24 | prerealease: string[] | |
| 25 | build: string[] | |
| 26 | ||
| 27 | compare(version: Version): boolean | |
| 28 | ||
| 29 | toString(): string | |
| 30 | } | |
| 31 | ||
| 32 | export namespace Version { | |
| 33 | export function parse(input: string): Version | |
| 34 | ||
| 35 | export function compare(a: Version, b: Version): number | |
| 36 | } | |
| 37 | ||
| 38 | export class Comparator { | |
| 39 | constructor(operator: number, version: Version) | |
| 40 | ||
| 41 | operator: number | |
| 42 | version: Version | |
| 43 | ||
| 44 | test(version: Version): boolean | |
| 45 | ||
| 46 | toString(): string | |
| 47 | } | |
| 48 | ||
| 49 | export class Range { | |
| 50 | constructor(comparators?: Comparator[][]) | |
| 51 | ||
| 52 | comparators: Comparator[][] | |
| 53 | ||
| 54 | test(version: Version): boolean | |
| 55 | ||
| 56 | toString(): string | |
| 57 | } | |
| 58 | ||
| 59 | export namespace Range { | |
| 60 | export function parse(input: string): Range | |
| 61 | } |
| … | … | @@ -0,0 +1,13 @@ |
| 1 | exports.constants = require('./lib/constants') | |
| 2 | exports.errors = require('./lib/errors') | |
| 3 | ||
| 4 | const Version = (exports.Version = require('./lib/version')) | |
| 5 | const Range = (exports.Range = require('./lib/range')) | |
| 6 | exports.Comparator = require('./lib/comparator') | |
| 7 | ||
| 8 | exports.satisfies = function satisfies(version, range) { | |
| 9 | if (typeof version === 'string') version = Version.parse(version) | |
| 10 | if (typeof range === 'string') range = Range.parse(range) | |
| 11 | ||
| 12 | return range.test(version) | |
| 13 | } |
| … | … | @@ -0,0 +1,37 @@ |
| 1 | const constants = require('./constants') | |
| 2 | ||
| 3 | const symbols = { | |
| 4 | [constants.EQ]: '=', | |
| 5 | [constants.LT]: '<', | |
| 6 | [constants.LTE]: '<=', | |
| 7 | [constants.GT]: '>', | |
| 8 | [constants.GTE]: '>=' | |
| 9 | } | |
| 10 | ||
| 11 | module.exports = class Comparator { | |
| 12 | constructor(operator, version) { | |
| 13 | this.operator = operator | |
| 14 | this.version = version | |
| 15 | } | |
| 16 | ||
| 17 | test(version) { | |
| 18 | const result = version.compare(this.version) | |
| 19 | ||
| 20 | switch (this.operator) { | |
| 21 | case constants.LT: | |
| 22 | return result < 0 | |
| 23 | case constants.LTE: | |
| 24 | return result <= 0 | |
| 25 | case constants.GT: | |
| 26 | return result > 0 | |
| 27 | case constants.GTE: | |
| 28 | return result >= 0 | |
| 29 | default: | |
| 30 | return result === 0 | |
| 31 | } | |
| 32 | } | |
| 33 | ||
| 34 | toString() { | |
| 35 | return symbols[this.operator] + this.version | |
| 36 | } | |
| 37 | } |
| … | … | @@ -0,0 +1,7 @@ |
| 1 | module.exports = { | |
| 2 | EQ: 1, | |
| 3 | LT: 2, | |
| 4 | LTE: 3, | |
| 5 | GT: 4, | |
| 6 | GTE: 5 | |
| 7 | } |
| … | … | @@ -0,0 +1,22 @@ |
| 1 | module.exports = class SemVerError extends Error { | |
| 2 | constructor(msg, code, fn = SemVerError) { | |
| 3 | super(`${code}: ${msg}`) | |
| 4 | this.code = code | |
| 5 | ||
| 6 | if (Error.captureStackTrace) { | |
| 7 | Error.captureStackTrace(this, fn) | |
| 8 | } | |
| 9 | } | |
| 10 | ||
| 11 | get name() { | |
| 12 | return 'SemVerError' | |
| 13 | } | |
| 14 | ||
| 15 | static INVALID_VERSION(msg, fn = SemVerError.INVALID_VERSION) { | |
| 16 | return new SemVerError(msg, 'INVALID_VERSION', fn) | |
| 17 | } | |
| 18 | ||
| 19 | static INVALID_RANGE(msg, fn = SemVerError.INVALID_RANGE) { | |
| 20 | return new SemVerError(msg, 'INVALID_RANGE', fn) | |
| 21 | } | |
| 22 | } |
| … | … | @@ -0,0 +1,121 @@ |
| 1 | const constants = require('./constants') | |
| 2 | const errors = require('./errors') | |
| 3 | const Version = require('./version') | |
| 4 | const Comparator = require('./comparator') | |
| 5 | ||
| 6 | class Range { | |
| 7 | constructor(comparators = []) { | |
| 8 | this.comparators = comparators | |
| 9 | } | |
| 10 | ||
| 11 | test(version) { | |
| 12 | for (const set of this.comparators) { | |
| 13 | let matches = true | |
| 14 | ||
| 15 | for (const comparator of set) { | |
| 16 | if (comparator.test(version)) continue | |
| 17 | matches = false | |
| 18 | break | |
| 19 | } | |
| 20 | ||
| 21 | if (matches) return true | |
| 22 | } | |
| 23 | ||
| 24 | return false | |
| 25 | } | |
| 26 | ||
| 27 | toString() { | |
| 28 | let result = '' | |
| 29 | let first = true | |
| 30 | ||
| 31 | for (const set of this.comparators) { | |
| 32 | if (first) first = false | |
| 33 | else result += ' || ' | |
| 34 | ||
| 35 | result += set.join(' ') | |
| 36 | } | |
| 37 | ||
| 38 | return result | |
| 39 | } | |
| 40 | } | |
| 41 | ||
| 42 | module.exports = exports = Range | |
| 43 | ||
| 44 | exports.parse = function parse(input, state = { position: 0, partial: false }) { | |
| 45 | let i = state.position | |
| 46 | let c | |
| 47 | ||
| 48 | const unexpected = (expected) => { | |
| 49 | let msg | |
| 50 | ||
| 51 | if (i >= input.length) { | |
| 52 | msg = `Unexpected end of input in '${input}'` | |
| 53 | } else { | |
| 54 | msg = `Unexpected token '${input[i]}' in '${input}' at position ${i}` | |
| 55 | } | |
| 56 | ||
| 57 | if (expected) msg += `, ${expected}` | |
| 58 | ||
| 59 | throw errors.INVALID_VERSION(msg, unexpected) | |
| 60 | } | |
| 61 | ||
| 62 | const comparators = [] | |
| 63 | ||
| 64 | while (i < input.length) { | |
| 65 | const set = [] | |
| 66 | ||
| 67 | while (i < input.length) { | |
| 68 | c = input[i] | |
| 69 | ||
| 70 | let operator = constants.EQ | |
| 71 | ||
| 72 | if (c === '<') { | |
| 73 | operator = constants.LT | |
| 74 | c = input[++i] | |
| 75 | ||
| 76 | if (c === '=') { | |
| 77 | operator = constants.LTE | |
| 78 | c = input[++i] | |
| 79 | } | |
| 80 | } else if (c === '>') { | |
| 81 | operator = constants.GT | |
| 82 | c = input[++i] | |
| 83 | ||
| 84 | if (c === '=') { | |
| 85 | operator = constants.GTE | |
| 86 | c = input[++i] | |
| 87 | } | |
| 88 | } else if (c === '=') { | |
| 89 | c = input[++i] | |
| 90 | } | |
| 91 | ||
| 92 | const state = { position: i, partial: true, range: true } | |
| 93 | ||
| 94 | set.push(new Comparator(operator, Version.parse(input, state))) | |
| 95 | ||
| 96 | c = input[(i = state.position)] | |
| 97 | ||
| 98 | while (c === ' ') c = input[++i] | |
| 99 | ||
| 100 | if (c === '|' && input[i + 1] === '|') { | |
| 101 | c = input[(i += 2)] | |
| 102 | ||
| 103 | while (c === ' ') c = input[++i] | |
| 104 | ||
| 105 | break | |
| 106 | } | |
| 107 | ||
| 108 | if (c && c !== '<' && c !== '>') unexpected("expected '||', '<', or '>'") | |
| 109 | } | |
| 110 | ||
| 111 | if (set.length) comparators.push(set) | |
| 112 | } | |
| 113 | ||
| 114 | if (i < input.length && state.partial === false) { | |
| 115 | unexpected('expected end of input') | |
| 116 | } | |
| 117 | ||
| 118 | state.position = i | |
| 119 | ||
| 120 | return new Range(comparators) | |
| 121 | } |
| … | … | @@ -0,0 +1,208 @@ |
| 1 | const errors = require('./errors') | |
| 2 | ||
| 3 | class Version { | |
| 4 | constructor(major, minor, patch, opts = {}) { | |
| 5 | const { prerelease = [], build = [] } = opts | |
| 6 | ||
| 7 | this.major = major | |
| 8 | this.minor = minor | |
| 9 | this.patch = patch | |
| 10 | this.prerelease = prerelease | |
| 11 | this.build = build | |
| 12 | } | |
| 13 | ||
| 14 | compare(version) { | |
| 15 | return exports.compare(this, version) | |
| 16 | } | |
| 17 | ||
| 18 | toString() { | |
| 19 | let result = `${this.major}.${this.minor}.${this.patch}` | |
| 20 | ||
| 21 | if (this.prerelease.length) { | |
| 22 | result += '-' + this.prerelease.join('.') | |
| 23 | } | |
| 24 | ||
| 25 | if (this.build.length) { | |
| 26 | result += '+' + this.build.join('.') | |
| 27 | } | |
| 28 | ||
| 29 | return result | |
| 30 | } | |
| 31 | } | |
| 32 | ||
| 33 | module.exports = exports = Version | |
| 34 | ||
| 35 | exports.parse = function parse( | |
| 36 | input, | |
| 37 | state = { position: 0, partial: false, range: false } | |
| 38 | ) { | |
| 39 | let i = state.position | |
| 40 | let c | |
| 41 | ||
| 42 | const unexpected = (expected) => { | |
| 43 | let msg | |
| 44 | ||
| 45 | if (i >= input.length) { | |
| 46 | msg = `Unexpected end of input in '${input}'` | |
| 47 | } else { | |
| 48 | msg = `Unexpected token '${input[i]}' in '${input}' at position ${i}` | |
| 49 | } | |
| 50 | ||
| 51 | if (expected) msg += `, ${expected}` | |
| 52 | ||
| 53 | throw errors.INVALID_VERSION(msg, unexpected) | |
| 54 | } | |
| 55 | ||
| 56 | const components = [0, 0, 0] | |
| 57 | ||
| 58 | let k = 0 | |
| 59 | ||
| 60 | while (k < 3) { | |
| 61 | c = input[i] | |
| 62 | ||
| 63 | if (k > 0) { | |
| 64 | if (c === '.') c = input[++i] | |
| 65 | else if (state.range) break | |
| 66 | else unexpected("expected '.'") | |
| 67 | } | |
| 68 | ||
| 69 | if (c === '0') { | |
| 70 | i++ | |
| 71 | k++ | |
| 72 | } else if (c >= '1' && c <= '9') { | |
| 73 | let j = 0 | |
| 74 | do c = input[i + ++j] | |
| 75 | while (c >= '0' && c <= '9') | |
| 76 | ||
| 77 | components[k++] = parseInt(input.substring(i, i + j)) | |
| 78 | ||
| 79 | i += j | |
| 80 | } else unexpected('expected /[0-9]/') | |
| 81 | } | |
| 82 | ||
| 83 | const prerelease = [] | |
| 84 | ||
| 85 | if (k === 3 && input[i] === '-') { | |
| 86 | i++ | |
| 87 | ||
| 88 | while (true) { | |
| 89 | c = input[i] | |
| 90 | ||
| 91 | let tag = '' | |
| 92 | let j = 0 | |
| 93 | ||
| 94 | while (c >= '0' && c <= '9') c = input[i + ++j] | |
| 95 | ||
| 96 | let isNumeric = false | |
| 97 | ||
| 98 | if (j) { | |
| 99 | tag += input.substring(i, i + j) | |
| 100 | ||
| 101 | c = input[(i += j)] | |
| 102 | ||
| 103 | isNumeric = tag[0] !== '0' || tag.length === 1 | |
| 104 | } | |
| 105 | ||
| 106 | j = 0 | |
| 107 | ||
| 108 | while ( | |
| 109 | (c >= '0' && c <= '9') || | |
| 110 | (c >= 'a' && c <= 'z') || | |
| 111 | (c >= 'A' && c <= 'Z') || | |
| 112 | c === '-' | |
| 113 | ) | |
| 114 | c = input[i + ++j] | |
| 115 | ||
| 116 | if (j) { | |
| 117 | tag += input.substring(i, i + j) | |
| 118 | ||
| 119 | c = input[(i += j)] | |
| 120 | } else if (!isNumeric) unexpected('expected /[a-zA-Z-]/') | |
| 121 | ||
| 122 | prerelease.push(tag) | |
| 123 | ||
| 124 | if (c === '.') c = input[++i] | |
| 125 | else break | |
| 126 | } | |
| 127 | } | |
| 128 | ||
| 129 | const build = [] | |
| 130 | ||
| 131 | if (k === 3 && input[i] === '+') { | |
| 132 | i++ | |
| 133 | ||
| 134 | while (true) { | |
| 135 | c = input[i] | |
| 136 | ||
| 137 | let tag = '' | |
| 138 | let j = 0 | |
| 139 | ||
| 140 | while ( | |
| 141 | (c >= '0' && c <= '9') || | |
| 142 | (c >= 'a' && c <= 'z') || | |
| 143 | (c >= 'A' && c <= 'Z') || | |
| 144 | c === '-' | |
| 145 | ) | |
| 146 | c = input[i + ++j] | |
| 147 | ||
| 148 | if (j) { | |
| 149 | tag += input.substring(i, i + j) | |
| 150 | ||
| 151 | c = input[(i += j)] | |
| 152 | } else unexpected('expected /[0-9a-zA-Z-]/') | |
| 153 | ||
| 154 | build.push(tag) | |
| 155 | ||
| 156 | if (c === '.') c = input[++i] | |
| 157 | else break | |
| 158 | } | |
| 159 | } | |
| 160 | ||
| 161 | if (i < input.length && state.partial === false) { | |
| 162 | unexpected('expected end of input') | |
| 163 | } | |
| 164 | ||
| 165 | state.position = i | |
| 166 | ||
| 167 | return new Version(...components, { prerelease, build }) | |
| 168 | } | |
| 169 | ||
| 170 | const integer = /^[0-9]+$/ | |
| 171 | ||
| 172 | exports.compare = function compare(a, b) { | |
| 173 | if (a.major > b.major) return 1 | |
| 174 | if (a.major < b.major) return -1 | |
| 175 | ||
| 176 | if (a.minor > b.minor) return 1 | |
| 177 | if (a.minor < b.minor) return -1 | |
| 178 | ||
| 179 | if (a.patch > b.patch) return 1 | |
| 180 | if (a.patch < b.patch) return -1 | |
| 181 | ||
| 182 | if (a.prerelease.length === 0) return b.prerelease.length === 0 ? 0 : 1 | |
| 183 | if (b.prerelease.length === 0) return -1 | |
| 184 | ||
| 185 | let i = 0 | |
| 186 | do { | |
| 187 | let x = a.prerelease[i] | |
| 188 | let y = b.prerelease[i] | |
| 189 | ||
| 190 | if (x === undefined) return y === undefined ? 0 : -1 | |
| 191 | if (y === undefined) return 1 | |
| 192 | ||
| 193 | if (x === y) continue | |
| 194 | ||
| 195 | const xInt = integer.test(x) | |
| 196 | const yInt = integer.test(y) | |
| 197 | ||
| 198 | if (xInt && yInt) { | |
| 199 | x = +x | |
| 200 | y = +y | |
| 201 | } else { | |
| 202 | if (xInt) return -1 | |
| 203 | if (yInt) return 1 | |
| 204 | } | |
| 205 | ||
| 206 | return x > y ? 1 : -1 | |
| 207 | } while (++i) | |
| 208 | } |
| … | … | @@ -0,0 +1,40 @@ |
| 1 | { | |
| 2 | "name": "bare-semver", | |
| 3 | "version": "1.0.2", | |
| 4 | "description": "Minimal semantic versioning library for Bare", | |
| 5 | "exports": { | |
| 6 | ".": { | |
| 7 | "types": "./index.d.ts", | |
| 8 | "default": "./index.js" | |
| 9 | }, | |
| 10 | "./package": "./package.json", | |
| 11 | "./constants": "./lib/constants.js", | |
| 12 | "./errors": "./lib/errors.js", | |
| 13 | "./version": "./lib/version.js", | |
| 14 | "./range": "./lib/range.js", | |
| 15 | "./comparator": "./lib/comparator.js" | |
| 16 | }, | |
| 17 | "files": [ | |
| 18 | "index.js", | |
| 19 | "index.d.ts", | |
| 20 | "lib" | |
| 21 | ], | |
| 22 | "scripts": { | |
| 23 | "test": "prettier . --check && bare test.js" | |
| 24 | }, | |
| 25 | "repository": { | |
| 26 | "type": "git", | |
| 27 | "url": "git+https://github.com/holepunchto/bare-semver.git" | |
| 28 | }, | |
| 29 | "author": "Holepunch", | |
| 30 | "license": "Apache-2.0", | |
| 31 | "bugs": { | |
| 32 | "url": "https://github.com/holepunchto/bare-semver/issues" | |
| 33 | }, | |
| 34 | "homepage": "https://github.com/holepunchto/bare-semver#readme", | |
| 35 | "devDependencies": { | |
| 36 | "brittle": "^3.2.1", | |
| 37 | "prettier": "^3.4.2", | |
| 38 | "prettier-config-standard": "^7.0.0" | |
| 39 | } | |
| 40 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,15 @@ |
| 1 | # bare-stream | |
| 2 | ||
| 3 | Streaming data for JavaScript. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i bare-stream | |
| 7 | ``` | |
| 8 | ||
| 9 | ## API | |
| 10 | ||
| 11 | See <https://github.com/mafintosh/streamx>. | |
| 12 | ||
| 13 | ## License | |
| 14 | ||
| 15 | Apache-2.0 |
| … | … | @@ -0,0 +1,12 @@ |
| 1 | const stream = require('./web') | |
| 2 | ||
| 3 | global.ReadableStream = stream.ReadableStream | |
| 4 | global.ReadableStreamDefaultController = stream.ReadableStreamDefaultController | |
| 5 | global.ReadableStreamDefaultReader = stream.ReadableStreamDefaultReader | |
| 6 | ||
| 7 | global.CountQueuingStrategy = stream.CountQueuingStrategy | |
| 8 | global.ByteLengthQueuingStrategy = stream.ByteLengthQueuingStrategy | |
| 9 | ||
| 10 | global.WritableStream = stream.WritableStream | |
| 11 | global.WritableStreamDefaultController = stream.WritableStreamDefaultController | |
| 12 | global.WritableStreamDefaultReader = stream.WritableStreamDefaultReader |
| … | … | @@ -0,0 +1,188 @@ |
| 1 | import EventEmitter, { EventMap } from 'bare-events' | |
| 2 | import Buffer, { BufferEncoding } from 'bare-buffer' | |
| 3 | ||
| 4 | type StreamEncoding = BufferEncoding | 'buffer' | |
| 5 | ||
| 6 | interface StreamCallback { | |
| 7 | (err: Error | null): void | |
| 8 | } | |
| 9 | ||
| 10 | interface StreamEvents extends EventMap { | |
| 11 | close: [] | |
| 12 | error: [err: Error] | |
| 13 | } | |
| 14 | ||
| 15 | interface StreamOptions<S extends Stream = Stream> { | |
| 16 | eagerOpen?: boolean | |
| 17 | signal?: AbortSignal | |
| 18 | open?(this: S, cb: StreamCallback): void | |
| 19 | predestroy?(this: S): void | |
| 20 | destroy?(this: S, err: Error | null, cb: StreamCallback): void | |
| 21 | } | |
| 22 | ||
| 23 | interface Stream<M extends StreamEvents = StreamEvents> extends EventEmitter<M> { | |
| 24 | _open(cb: StreamCallback): void | |
| 25 | _predestroy(): void | |
| 26 | _destroy(err: Error | null, cb: StreamCallback): void | |
| 27 | ||
| 28 | readonly readable: boolean | |
| 29 | readonly writable: boolean | |
| 30 | readonly destroyed: boolean | |
| 31 | readonly destroying: boolean | |
| 32 | ||
| 33 | destroy(err?: Error | null): void | |
| 34 | } | |
| 35 | ||
| 36 | declare class Stream {} | |
| 37 | ||
| 38 | interface ReadableEvents extends StreamEvents { | |
| 39 | data: [data: unknown] | |
| 40 | end: [] | |
| 41 | readable: [] | |
| 42 | piping: [dest: Writable] | |
| 43 | } | |
| 44 | ||
| 45 | interface ReadableOptions<S extends Readable = Readable> extends StreamOptions<S> { | |
| 46 | encoding?: BufferEncoding | |
| 47 | highWaterMark?: number | |
| 48 | read?(this: S, size: number): void | |
| 49 | } | |
| 50 | ||
| 51 | interface Readable<M extends ReadableEvents = ReadableEvents> | |
| 52 | extends Stream<M>, AsyncIterable<unknown> { | |
| 53 | _read(size: number): void | |
| 54 | ||
| 55 | push(data: unknown | null, encoding?: BufferEncoding): boolean | |
| 56 | unshift(data: unknown | null, encoding?: BufferEncoding): boolean | |
| 57 | read(): unknown | null | |
| 58 | ||
| 59 | resume(): this | |
| 60 | pause(): this | |
| 61 | ||
| 62 | pipe<S extends Writable>(dest: S, cb?: StreamCallback): S | |
| 63 | ||
| 64 | setEncoding(encoding: BufferEncoding): void | |
| 65 | } | |
| 66 | ||
| 67 | declare class Readable<M extends ReadableEvents = ReadableEvents> extends Stream<M> { | |
| 68 | constructor(opts?: ReadableOptions) | |
| 69 | ||
| 70 | static from(data: unknown | unknown[] | AsyncIterable<unknown>, opts?: ReadableOptions): Readable | |
| 71 | ||
| 72 | static isBackpressured(rs: Readable): boolean | |
| 73 | ||
| 74 | static isPaused(rs: Readable): boolean | |
| 75 | } | |
| 76 | ||
| 77 | interface WritableEvents extends StreamEvents { | |
| 78 | drain: [] | |
| 79 | finish: [] | |
| 80 | pipe: [src: Readable] | |
| 81 | } | |
| 82 | ||
| 83 | interface WritableOptions<S extends Writable = Writable> extends StreamOptions<S> { | |
| 84 | write?(this: S, data: unknown, encoding: StreamEncoding, cb: StreamCallback): void | |
| 85 | writev?(this: S, batch: { chunk: unknown; encoding: StreamEncoding }[], cb: StreamCallback): void | |
| 86 | final?(this: S, cb: StreamCallback): void | |
| 87 | } | |
| 88 | ||
| 89 | interface Writable<M extends WritableEvents = WritableEvents> extends Stream<M> { | |
| 90 | _write(data: unknown, encoding: StreamEncoding, cb: StreamCallback): void | |
| 91 | _writev(batch: { chunk: unknown; encoding: StreamEncoding }[], cb: StreamCallback): void | |
| 92 | _final(cb: StreamCallback): void | |
| 93 | ||
| 94 | readonly destroyed: boolean | |
| 95 | ||
| 96 | write(data: unknown, encoding?: BufferEncoding, cb?: StreamCallback): boolean | |
| 97 | write(data: unknown, cb?: StreamCallback): boolean | |
| 98 | ||
| 99 | end(cb?: StreamCallback): this | |
| 100 | end(data: unknown, encoding?: BufferEncoding, cb?: StreamCallback): this | |
| 101 | end(data: unknown, cb?: StreamCallback): this | |
| 102 | ||
| 103 | cork(): void | |
| 104 | uncork(): void | |
| 105 | } | |
| 106 | ||
| 107 | declare class Writable<M extends WritableEvents = WritableEvents> extends Stream<M> { | |
| 108 | constructor(opts?: WritableOptions) | |
| 109 | ||
| 110 | static isBackpressured(ws: Writable): boolean | |
| 111 | ||
| 112 | static drained(ws: Writable): Promise<boolean> | |
| 113 | } | |
| 114 | ||
| 115 | interface DuplexEvents extends ReadableEvents, WritableEvents {} | |
| 116 | ||
| 117 | interface DuplexOptions<S extends Duplex = Duplex> extends ReadableOptions<S>, WritableOptions<S> {} | |
| 118 | ||
| 119 | interface Duplex<M extends DuplexEvents = DuplexEvents> extends Readable<M>, Writable<M> {} | |
| 120 | ||
| 121 | declare class Duplex<M extends DuplexEvents = DuplexEvents> extends Stream<M> { | |
| 122 | constructor(opts?: DuplexOptions) | |
| 123 | } | |
| 124 | ||
| 125 | interface TransformEvents extends DuplexEvents {} | |
| 126 | ||
| 127 | interface TransformOptions<S extends Transform = Transform> extends DuplexOptions<S> { | |
| 128 | transform?(this: S, data: unknown, encoding: StreamEncoding, cb: StreamCallback): void | |
| 129 | flush?(this: S, cb: StreamCallback): void | |
| 130 | } | |
| 131 | ||
| 132 | interface Transform<M extends TransformEvents = TransformEvents> extends Duplex<M> { | |
| 133 | _transform(data: unknown, encoding: StreamEncoding, cb: StreamCallback): void | |
| 134 | _flush(cb: StreamCallback): void | |
| 135 | } | |
| 136 | ||
| 137 | declare class Transform<M extends TransformEvents = TransformEvents> extends Duplex<M> { | |
| 138 | constructor(opts?: TransformOptions) | |
| 139 | } | |
| 140 | ||
| 141 | type Pipeline<S extends Writable> = [src: Readable, ...transforms: Duplex[], dest: S] | |
| 142 | ||
| 143 | declare namespace Stream { | |
| 144 | export { | |
| 145 | Stream, | |
| 146 | StreamEvents, | |
| 147 | StreamOptions, | |
| 148 | Readable, | |
| 149 | ReadableEvents, | |
| 150 | ReadableOptions, | |
| 151 | Writable, | |
| 152 | WritableEvents, | |
| 153 | WritableOptions, | |
| 154 | Duplex, | |
| 155 | DuplexEvents, | |
| 156 | DuplexOptions, | |
| 157 | Transform, | |
| 158 | TransformEvents, | |
| 159 | TransformOptions, | |
| 160 | Transform as PassThrough | |
| 161 | } | |
| 162 | ||
| 163 | export function pipeline<S extends Writable>(streams: Pipeline<S>, cb?: StreamCallback): S | |
| 164 | ||
| 165 | export function pipeline<S extends Writable>(...args: Pipeline<S>): S | |
| 166 | ||
| 167 | export function pipeline<S extends Writable>(...args: [...Pipeline<S>, cb: StreamCallback]): S | |
| 168 | ||
| 169 | export function finished( | |
| 170 | stream: Stream, | |
| 171 | opts: { cleanup?: boolean }, | |
| 172 | cb: StreamCallback | |
| 173 | ): () => void | |
| 174 | ||
| 175 | export function finished(stream: Stream, cb: StreamCallback): () => void | |
| 176 | ||
| 177 | export function isStream(stream: unknown): stream is Stream | |
| 178 | ||
| 179 | export function isEnded(stream: Stream): boolean | |
| 180 | ||
| 181 | export function isFinished(stream: Stream): boolean | |
| 182 | ||
| 183 | export function isDisturbed(stream: Stream): boolean | |
| 184 | ||
| 185 | export function getStreamError(stream: Stream, opts?: { all?: boolean }): Error | null | |
| 186 | } | |
| 187 | ||
| 188 | export = Stream |
| … | … | @@ -0,0 +1,351 @@ |
| 1 | const stream = require('streamx') | |
| 2 | ||
| 3 | const defaultEncoding = 'utf8' | |
| 4 | ||
| 5 | module.exports = exports = stream.Stream | |
| 6 | ||
| 7 | exports.pipeline = stream.pipeline | |
| 8 | ||
| 9 | exports.isStream = stream.isStream | |
| 10 | exports.isEnding = stream.isEnding | |
| 11 | exports.isEnded = stream.isEnded | |
| 12 | exports.isFinishing = stream.isFinishing | |
| 13 | exports.isFinished = stream.isFinished | |
| 14 | exports.isDisturbed = stream.isDisturbed | |
| 15 | ||
| 16 | exports.getStreamError = stream.getStreamError | |
| 17 | ||
| 18 | exports.Stream = exports | |
| 19 | ||
| 20 | exports.Readable = class Readable extends stream.Readable { | |
| 21 | constructor(opts = {}) { | |
| 22 | super({ | |
| 23 | ...opts, | |
| 24 | byteLength: null, | |
| 25 | byteLengthReadable: null, | |
| 26 | map: null, | |
| 27 | mapReadable: null | |
| 28 | }) | |
| 29 | ||
| 30 | if (this._construct) this._open = this._construct | |
| 31 | ||
| 32 | if (this._read !== stream.Readable.prototype._read) { | |
| 33 | this._read = read.bind(this, this._read) | |
| 34 | } | |
| 35 | ||
| 36 | if (this._destroy !== stream.Stream.prototype._destroy) { | |
| 37 | this._destroy = destroy.bind(this, this._destroy) | |
| 38 | } | |
| 39 | } | |
| 40 | ||
| 41 | push(chunk, encoding) { | |
| 42 | if (typeof chunk === 'string') { | |
| 43 | chunk = Buffer.from(chunk, encoding || defaultEncoding) | |
| 44 | } | |
| 45 | ||
| 46 | return super.push(chunk) | |
| 47 | } | |
| 48 | ||
| 49 | unshift(chunk, encoding) { | |
| 50 | if (typeof chunk === 'string') { | |
| 51 | chunk = Buffer.from(chunk, encoding || defaultEncoding) | |
| 52 | } | |
| 53 | ||
| 54 | super.unshift(chunk) | |
| 55 | } | |
| 56 | } | |
| 57 | ||
| 58 | exports.Writable = class Writable extends stream.Writable { | |
| 59 | constructor(opts = {}) { | |
| 60 | super({ | |
| 61 | ...opts, | |
| 62 | byteLength: null, | |
| 63 | byteLengthWritable, | |
| 64 | map: null, | |
| 65 | mapWritable: null | |
| 66 | }) | |
| 67 | ||
| 68 | if (this._construct) this._open = this._construct | |
| 69 | ||
| 70 | if (this._write !== stream.Writable.prototype._write) { | |
| 71 | this._write = write.bind(this, this._write) | |
| 72 | } | |
| 73 | ||
| 74 | if (this._destroy !== stream.Stream.prototype._destroy) { | |
| 75 | this._destroy = destroy.bind(this, this._destroy) | |
| 76 | } | |
| 77 | } | |
| 78 | ||
| 79 | write(chunk, encoding, cb) { | |
| 80 | if (typeof encoding === 'function') { | |
| 81 | cb = encoding | |
| 82 | encoding = null | |
| 83 | } | |
| 84 | ||
| 85 | if (typeof chunk === 'string') { | |
| 86 | encoding = encoding || defaultEncoding | |
| 87 | chunk = Buffer.from(chunk, encoding) | |
| 88 | } else { | |
| 89 | encoding = 'buffer' | |
| 90 | } | |
| 91 | ||
| 92 | const result = super.write({ chunk, encoding }) | |
| 93 | ||
| 94 | if (cb) stream.Writable.drained(this).then(() => cb(null), cb) | |
| 95 | ||
| 96 | return result | |
| 97 | } | |
| 98 | ||
| 99 | end(chunk, encoding, cb) { | |
| 100 | if (typeof chunk === 'function') { | |
| 101 | cb = chunk | |
| 102 | chunk = null | |
| 103 | } else if (typeof encoding === 'function') { | |
| 104 | cb = encoding | |
| 105 | encoding = null | |
| 106 | } | |
| 107 | ||
| 108 | if (typeof chunk === 'string') { | |
| 109 | encoding = encoding || defaultEncoding | |
| 110 | chunk = Buffer.from(chunk, encoding || defaultEncoding) | |
| 111 | } else { | |
| 112 | encoding = 'buffer' | |
| 113 | } | |
| 114 | ||
| 115 | const result = | |
| 116 | chunk !== undefined && chunk !== null ? super.end({ chunk, encoding }) : super.end() | |
| 117 | ||
| 118 | if (cb) this.once('finish', () => cb(null)) | |
| 119 | ||
| 120 | return result | |
| 121 | } | |
| 122 | } | |
| 123 | ||
| 124 | exports.Duplex = class Duplex extends stream.Duplex { | |
| 125 | constructor(opts = {}) { | |
| 126 | super({ | |
| 127 | ...opts, | |
| 128 | byteLength: null, | |
| 129 | byteLengthReadable: null, | |
| 130 | byteLengthWritable, | |
| 131 | map: null, | |
| 132 | mapReadable: null, | |
| 133 | mapWritable: null | |
| 134 | }) | |
| 135 | ||
| 136 | if (this._construct) this._open = this._construct | |
| 137 | ||
| 138 | if (this._read !== stream.Readable.prototype._read) { | |
| 139 | this._read = read.bind(this, this._read) | |
| 140 | } | |
| 141 | ||
| 142 | if (this._write !== stream.Duplex.prototype._write) { | |
| 143 | this._write = write.bind(this, this._write) | |
| 144 | } | |
| 145 | ||
| 146 | if (this._destroy !== stream.Stream.prototype._destroy) { | |
| 147 | this._destroy = destroy.bind(this, this._destroy) | |
| 148 | } | |
| 149 | } | |
| 150 | ||
| 151 | push(chunk, encoding) { | |
| 152 | if (typeof chunk === 'string') { | |
| 153 | chunk = Buffer.from(chunk, encoding || defaultEncoding) | |
| 154 | } | |
| 155 | ||
| 156 | return super.push(chunk) | |
| 157 | } | |
| 158 | ||
| 159 | unshift(chunk, encoding) { | |
| 160 | if (typeof chunk === 'string') { | |
| 161 | chunk = Buffer.from(chunk, encoding || defaultEncoding) | |
| 162 | } | |
| 163 | ||
| 164 | super.unshift(chunk) | |
| 165 | } | |
| 166 | ||
| 167 | write(chunk, encoding, cb) { | |
| 168 | if (typeof encoding === 'function') { | |
| 169 | cb = encoding | |
| 170 | encoding = null | |
| 171 | } | |
| 172 | ||
| 173 | if (typeof chunk === 'string') { | |
| 174 | encoding = encoding || defaultEncoding | |
| 175 | chunk = Buffer.from(chunk, encoding) | |
| 176 | } else { | |
| 177 | encoding = 'buffer' | |
| 178 | } | |
| 179 | ||
| 180 | const result = super.write({ chunk, encoding }) | |
| 181 | ||
| 182 | if (cb) stream.Writable.drained(this).then(() => cb(null), cb) | |
| 183 | ||
| 184 | return result | |
| 185 | } | |
| 186 | ||
| 187 | end(chunk, encoding, cb) { | |
| 188 | if (typeof chunk === 'function') { | |
| 189 | cb = chunk | |
| 190 | chunk = null | |
| 191 | } else if (typeof encoding === 'function') { | |
| 192 | cb = encoding | |
| 193 | encoding = null | |
| 194 | } | |
| 195 | ||
| 196 | if (typeof chunk === 'string') { | |
| 197 | encoding = encoding || defaultEncoding | |
| 198 | chunk = Buffer.from(chunk, encoding) | |
| 199 | } else { | |
| 200 | encoding = 'buffer' | |
| 201 | } | |
| 202 | ||
| 203 | const result = | |
| 204 | chunk !== undefined && chunk !== null ? super.end({ chunk, encoding }) : super.end() | |
| 205 | ||
| 206 | if (cb) this.once('finish', () => cb(null)) | |
| 207 | ||
| 208 | return result | |
| 209 | } | |
| 210 | } | |
| 211 | ||
| 212 | exports.Transform = class Transform extends stream.Transform { | |
| 213 | constructor(opts = {}) { | |
| 214 | super({ | |
| 215 | ...opts, | |
| 216 | byteLength: null, | |
| 217 | byteLengthReadable: null, | |
| 218 | byteLengthWritable, | |
| 219 | map: null, | |
| 220 | mapReadable: null, | |
| 221 | mapWritable: null | |
| 222 | }) | |
| 223 | ||
| 224 | if (this._transform !== stream.Transform.prototype._transform) { | |
| 225 | this._transform = transform.bind(this, this._transform) | |
| 226 | } else { | |
| 227 | this._transform = passthrough | |
| 228 | } | |
| 229 | } | |
| 230 | ||
| 231 | push(chunk, encoding) { | |
| 232 | if (typeof chunk === 'string') { | |
| 233 | chunk = Buffer.from(chunk, encoding || defaultEncoding) | |
| 234 | } | |
| 235 | ||
| 236 | return super.push(chunk) | |
| 237 | } | |
| 238 | ||
| 239 | unshift(chunk, encoding) { | |
| 240 | if (typeof chunk === 'string') { | |
| 241 | chunk = Buffer.from(chunk, encoding || defaultEncoding) | |
| 242 | } | |
| 243 | ||
| 244 | super.unshift(chunk) | |
| 245 | } | |
| 246 | ||
| 247 | write(chunk, encoding, cb) { | |
| 248 | if (typeof encoding === 'function') { | |
| 249 | cb = encoding | |
| 250 | encoding = null | |
| 251 | } | |
| 252 | ||
| 253 | if (typeof chunk === 'string') { | |
| 254 | encoding = encoding || defaultEncoding | |
| 255 | chunk = Buffer.from(chunk, encoding) | |
| 256 | } else { | |
| 257 | encoding = 'buffer' | |
| 258 | } | |
| 259 | ||
| 260 | const result = super.write({ chunk, encoding }) | |
| 261 | ||
| 262 | if (cb) stream.Writable.drained(this).then(() => cb(null), cb) | |
| 263 | ||
| 264 | return result | |
| 265 | } | |
| 266 | ||
| 267 | end(chunk, encoding, cb) { | |
| 268 | if (typeof chunk === 'function') { | |
| 269 | cb = chunk | |
| 270 | chunk = null | |
| 271 | } else if (typeof encoding === 'function') { | |
| 272 | cb = encoding | |
| 273 | encoding = null | |
| 274 | } | |
| 275 | ||
| 276 | if (typeof chunk === 'string') { | |
| 277 | encoding = encoding || defaultEncoding | |
| 278 | chunk = Buffer.from(chunk, encoding) | |
| 279 | } else { | |
| 280 | encoding = 'buffer' | |
| 281 | } | |
| 282 | ||
| 283 | const result = | |
| 284 | chunk !== undefined && chunk !== null ? super.end({ chunk, encoding }) : super.end() | |
| 285 | ||
| 286 | if (cb) this.once('finish', () => cb(null)) | |
| 287 | ||
| 288 | return result | |
| 289 | } | |
| 290 | } | |
| 291 | ||
| 292 | exports.PassThrough = class PassThrough extends exports.Transform {} | |
| 293 | ||
| 294 | exports.finished = function finished(stream, opts, cb) { | |
| 295 | if (typeof opts === 'function') { | |
| 296 | cb = opts | |
| 297 | opts = {} | |
| 298 | } | |
| 299 | ||
| 300 | if (!opts) opts = {} | |
| 301 | ||
| 302 | const { cleanup = false } = opts | |
| 303 | ||
| 304 | const done = () => { | |
| 305 | cb(exports.getStreamError(stream, { all: true })) | |
| 306 | ||
| 307 | if (cleanup) detach() | |
| 308 | } | |
| 309 | ||
| 310 | const detach = () => { | |
| 311 | stream.off('close', done) | |
| 312 | stream.off('error', noop) | |
| 313 | } | |
| 314 | ||
| 315 | if (stream.destroyed) { | |
| 316 | done() | |
| 317 | } else { | |
| 318 | stream.on('close', done) | |
| 319 | stream.on('error', noop) | |
| 320 | } | |
| 321 | ||
| 322 | return detach | |
| 323 | } | |
| 324 | ||
| 325 | function read(read, cb) { | |
| 326 | read.call(this, 65536) | |
| 327 | ||
| 328 | cb(null) | |
| 329 | } | |
| 330 | ||
| 331 | function write(write, data, cb) { | |
| 332 | write.call(this, data.chunk, data.encoding, cb) | |
| 333 | } | |
| 334 | ||
| 335 | function transform(transform, data, cb) { | |
| 336 | transform.call(this, data.chunk, data.encoding, cb) | |
| 337 | } | |
| 338 | ||
| 339 | function destroy(destroy, cb) { | |
| 340 | destroy.call(this, exports.getStreamError(this), cb) | |
| 341 | } | |
| 342 | ||
| 343 | function passthrough(data, cb) { | |
| 344 | cb(null, data.chunk) | |
| 345 | } | |
| 346 | ||
| 347 | function byteLengthWritable(data) { | |
| 348 | return data.chunk.byteLength | |
| 349 | } | |
| 350 | ||
| 351 | function noop() {} |
| … | … | @@ -0,0 +1,62 @@ |
| 1 | { | |
| 2 | "name": "bare-stream", | |
| 3 | "version": "2.10.0", | |
| 4 | "description": "Streaming data for JavaScript", | |
| 5 | "exports": { | |
| 6 | ".": { | |
| 7 | "types": "./index.d.ts", | |
| 8 | "default": "./index.js" | |
| 9 | }, | |
| 10 | "./package": "./package.json", | |
| 11 | "./promises": "./promises.js", | |
| 12 | "./web": { | |
| 13 | "types": "./web.d.ts", | |
| 14 | "default": "./web.js" | |
| 15 | }, | |
| 16 | "./global": "./global.js" | |
| 17 | }, | |
| 18 | "files": [ | |
| 19 | "index.js", | |
| 20 | "index.d.ts", | |
| 21 | "promises.js", | |
| 22 | "web.js", | |
| 23 | "web.d.ts", | |
| 24 | "global.js" | |
| 25 | ], | |
| 26 | "scripts": { | |
| 27 | "test": "prettier . --check && bare test.js" | |
| 28 | }, | |
| 29 | "repository": { | |
| 30 | "type": "git", | |
| 31 | "url": "git+https://github.com/holepunchto/bare-stream.git" | |
| 32 | }, | |
| 33 | "author": "Holepunch", | |
| 34 | "license": "Apache-2.0", | |
| 35 | "bugs": { | |
| 36 | "url": "https://github.com/holepunchto/bare-stream/issues" | |
| 37 | }, | |
| 38 | "homepage": "https://github.com/holepunchto/bare-stream#readme", | |
| 39 | "dependencies": { | |
| 40 | "streamx": "^2.25.0", | |
| 41 | "teex": "^1.0.1" | |
| 42 | }, | |
| 43 | "devDependencies": { | |
| 44 | "bare-buffer": "^3.0.0", | |
| 45 | "bare-events": "^2.5.4", | |
| 46 | "brittle": "^3.5.2", | |
| 47 | "prettier": "^3.3.3", | |
| 48 | "prettier-config-holepunch": "^2.0.0" | |
| 49 | }, | |
| 50 | "peerDependencies": { | |
| 51 | "bare-buffer": "*", | |
| 52 | "bare-events": "*" | |
| 53 | }, | |
| 54 | "peerDependenciesMeta": { | |
| 55 | "bare-buffer": { | |
| 56 | "optional": true | |
| 57 | }, | |
| 58 | "bare-events": { | |
| 59 | "optional": true | |
| 60 | } | |
| 61 | } | |
| 62 | } |
| … | … | @@ -0,0 +1,3 @@ |
| 1 | const stream = require('streamx') | |
| 2 | ||
| 3 | exports.pipeline = stream.pipelinePromise |
| … | … | @@ -0,0 +1,119 @@ |
| 1 | export interface ReadableStreamDefaultReader { | |
| 2 | readonly closed: Promise<void> | |
| 3 | ||
| 4 | read(): Promise<{ value: unknown; done: boolean }> | |
| 5 | releaseLock(): void | |
| 6 | cancel(reason?: unknown): Promise<void> | |
| 7 | } | |
| 8 | ||
| 9 | export class ReadableStreamDefaultReader { | |
| 10 | constructor(stream: ReadableStream) | |
| 11 | } | |
| 12 | ||
| 13 | export interface ReadableStreamDefaultController { | |
| 14 | readonly desiredSize: number | |
| 15 | ||
| 16 | enqueue(data: unknown): void | |
| 17 | close(): void | |
| 18 | error(error?: unknown): void | |
| 19 | } | |
| 20 | ||
| 21 | export class ReadableStreamDefaultController { | |
| 22 | constructor(stream: ReadableStream) | |
| 23 | } | |
| 24 | ||
| 25 | export interface UnderlyingSource<S extends ReadableStream = ReadableStream> { | |
| 26 | start?(this: S, controller: ReadableStreamDefaultController): void | |
| 27 | pull?(this: S, controller: ReadableStreamDefaultController): void | |
| 28 | cancel?(this: S, reason?: unknown): void | |
| 29 | } | |
| 30 | ||
| 31 | export interface CustomQueuingStrategy { | |
| 32 | highWaterMark?: number | |
| 33 | size?: (chunk: unknown) => number | |
| 34 | } | |
| 35 | ||
| 36 | export interface ReadableStream extends AsyncIterable<unknown> { | |
| 37 | readonly locked: boolean | |
| 38 | ||
| 39 | getReader(): ReadableStreamDefaultReader | |
| 40 | cancel(reason?: unknown): Promise<void> | |
| 41 | tee(): [ReadableStream, ReadableStream] | |
| 42 | pipeTo(destination: WritableStream): Promise<void> | |
| 43 | } | |
| 44 | ||
| 45 | export class ReadableStream { | |
| 46 | constructor(underlyingSource?: UnderlyingSource, queuingStrategy?: CustomQueuingStrategy) | |
| 47 | ||
| 48 | static from(iterable: unknown | unknown[] | AsyncIterable<unknown>): ReadableStream | |
| 49 | } | |
| 50 | ||
| 51 | export interface QueuingStrategyOptions { | |
| 52 | highWaterMark?: number | |
| 53 | } | |
| 54 | ||
| 55 | interface QueuingStrategy { | |
| 56 | readonly highWaterMark: number | |
| 57 | ||
| 58 | size(chunk: unknown): number | |
| 59 | } | |
| 60 | ||
| 61 | declare class QueuingStrategy { | |
| 62 | constructor(opts?: QueuingStrategyOptions) | |
| 63 | } | |
| 64 | ||
| 65 | export { type QueuingStrategy } | |
| 66 | ||
| 67 | export class CountQueuingStrategy extends QueuingStrategy {} | |
| 68 | ||
| 69 | export class ByteLengthQueuingStrategy extends QueuingStrategy {} | |
| 70 | ||
| 71 | export function isReadableStream(value: unknown): value is ReadableStream | |
| 72 | ||
| 73 | export function isReadableStreamErrored(stream: ReadableStream): boolean | |
| 74 | ||
| 75 | export function isReadableStreamDisturbed(stream: ReadableStream): boolean | |
| 76 | ||
| 77 | export interface WritableStreamDefaultWriter { | |
| 78 | readonly desiredSize: number | |
| 79 | readonly closed: Promise<void> | |
| 80 | readonly ready: Promise<void> | |
| 81 | ||
| 82 | write(chunk: unknown): Promise<void> | |
| 83 | releaseLock(): void | |
| 84 | close(): Promise<void> | |
| 85 | abort(reason?: unknown): Promise<void> | |
| 86 | } | |
| 87 | ||
| 88 | export class WritableStreamDefaultWriter { | |
| 89 | constructor(stream: WritableStream) | |
| 90 | } | |
| 91 | ||
| 92 | export interface WritableStreamDefaultController { | |
| 93 | error(err?: unknown): void | |
| 94 | } | |
| 95 | ||
| 96 | export class WritableStreamDefaultController { | |
| 97 | constructor(stream: WritableStream) | |
| 98 | } | |
| 99 | ||
| 100 | export interface UnderlyingSink<S extends WritableStream = WritableStream> { | |
| 101 | start?(this: S, controller: WritableStreamDefaultController): void | |
| 102 | write?(this: S, chunk: unknown, controller: WritableStreamDefaultController): void | |
| 103 | close?(this: S): void | |
| 104 | abort?(this: S, reason?: unknown): void | |
| 105 | } | |
| 106 | ||
| 107 | export interface WritableStream extends AsyncIterable<unknown> { | |
| 108 | readonly locked: boolean | |
| 109 | ||
| 110 | getWriter(): WritableStreamDefaultWriter | |
| 111 | abort(reason?: unknown): Promise<void> | |
| 112 | close(): Promise<void> | |
| 113 | } | |
| 114 | ||
| 115 | export class WritableStream { | |
| 116 | constructor(underlyingSink?: UnderlyingSink, queuingStrategy?: CustomQueuingStrategy) | |
| 117 | } | |
| 118 | ||
| 119 | export function isWritableStream(value: unknown): value is WritableStream |
| … | … | @@ -0,0 +1,488 @@ |
| 1 | const { Readable, Writable, getStreamError, isStreamx, isDisturbed } = require('streamx') | |
| 2 | const tee = require('teex') | |
| 3 | ||
| 4 | const readableKind = Symbol.for('bare.stream.readable.kind') | |
| 5 | const writableKind = Symbol.for('bare.stream.writable.kind') | |
| 6 | ||
| 7 | // https://streams.spec.whatwg.org/#readablestreamdefaultreader | |
| 8 | exports.ReadableStreamDefaultReader = class ReadableStreamDefaultReader { | |
| 9 | constructor(stream) { | |
| 10 | this._stream = stream | |
| 11 | this._stream._stream.once('close', onclose).once('error', onerror) | |
| 12 | ||
| 13 | const closed = Promise.withResolvers() | |
| 14 | ||
| 15 | // Avoid unhandled exceptions | |
| 16 | closed.promise.catch(noop) | |
| 17 | ||
| 18 | this._closed = closed | |
| 19 | ||
| 20 | function onclose() { | |
| 21 | closed.resolve() | |
| 22 | } | |
| 23 | ||
| 24 | function onerror(err) { | |
| 25 | closed.reject(err) | |
| 26 | } | |
| 27 | } | |
| 28 | ||
| 29 | get closed() { | |
| 30 | return this._closed.promise | |
| 31 | } | |
| 32 | ||
| 33 | read() { | |
| 34 | const stream = this._stream._stream | |
| 35 | ||
| 36 | return new Promise((resolve, reject) => { | |
| 37 | const err = getStreamError(stream) | |
| 38 | ||
| 39 | if (err) return reject(err) | |
| 40 | ||
| 41 | if (stream.destroyed) { | |
| 42 | return resolve({ value: undefined, done: true }) | |
| 43 | } | |
| 44 | ||
| 45 | const value = stream.read() | |
| 46 | ||
| 47 | if (value !== null) { | |
| 48 | return resolve({ value, done: false }) | |
| 49 | } | |
| 50 | ||
| 51 | stream.once('readable', onreadable).once('close', onclose).once('error', onerror) | |
| 52 | ||
| 53 | function onreadable() { | |
| 54 | const value = stream.read() | |
| 55 | ||
| 56 | ondone(null, value === null ? { value: undefined, done: true } : { value, done: false }) | |
| 57 | } | |
| 58 | ||
| 59 | function onclose() { | |
| 60 | ondone(null, { value: undefined, done: true }) | |
| 61 | } | |
| 62 | ||
| 63 | function onerror(err) { | |
| 64 | ondone(err, null) | |
| 65 | } | |
| 66 | ||
| 67 | function ondone(err, value) { | |
| 68 | stream.off('readable', onreadable).off('close', onclose).off('error', onerror) | |
| 69 | ||
| 70 | if (err) reject(err) | |
| 71 | else resolve(value) | |
| 72 | } | |
| 73 | }) | |
| 74 | } | |
| 75 | ||
| 76 | releaseLock() { | |
| 77 | this._closed.reject(new TypeError('Reader was released')) | |
| 78 | this._stream._releaseLock() | |
| 79 | this._stream = null | |
| 80 | } | |
| 81 | ||
| 82 | cancel(reason = new TypeError('Stream was cancelled')) { | |
| 83 | const stream = this._stream._stream | |
| 84 | ||
| 85 | if (stream.destroyed) return Promise.resolve() | |
| 86 | ||
| 87 | return new Promise((resolve) => | |
| 88 | stream.once('close', resolve).once('error', noop).destroy(reason) | |
| 89 | ) | |
| 90 | } | |
| 91 | } | |
| 92 | ||
| 93 | // https://streams.spec.whatwg.org/#readablestreamdefaultcontroller | |
| 94 | exports.ReadableStreamDefaultController = class ReadableStreamDefaultController { | |
| 95 | constructor(stream) { | |
| 96 | this._stream = stream | |
| 97 | } | |
| 98 | ||
| 99 | get desiredSize() { | |
| 100 | const stream = this._stream._stream | |
| 101 | ||
| 102 | return stream._readableState.highWaterMark - stream._readableState.buffered | |
| 103 | } | |
| 104 | ||
| 105 | enqueue(data) { | |
| 106 | this._stream._stream.push(data) | |
| 107 | } | |
| 108 | ||
| 109 | close() { | |
| 110 | this._stream._stream.push(null) | |
| 111 | } | |
| 112 | ||
| 113 | error(err) { | |
| 114 | this._stream._stream.destroy(err) | |
| 115 | } | |
| 116 | } | |
| 117 | ||
| 118 | // https://streams.spec.whatwg.org/#readablestream | |
| 119 | class ReadableStream { | |
| 120 | static get [readableKind]() { | |
| 121 | return 0 // Compatibility version | |
| 122 | } | |
| 123 | ||
| 124 | static from(iterable) { | |
| 125 | return new ReadableStream(Readable.from(iterable)) | |
| 126 | } | |
| 127 | ||
| 128 | constructor(underlyingSource = {}, queuingStrategy) { | |
| 129 | if (isStreamx(underlyingSource)) { | |
| 130 | this._stream = underlyingSource | |
| 131 | } else { | |
| 132 | if (queuingStrategy === undefined) { | |
| 133 | queuingStrategy = new exports.CountQueuingStrategy() | |
| 134 | } | |
| 135 | ||
| 136 | const { start, pull, cancel } = underlyingSource | |
| 137 | const { highWaterMark = 1, size = defaultSize } = queuingStrategy | |
| 138 | ||
| 139 | this._stream = new Readable({ highWaterMark, byteLength: size }) | |
| 140 | ||
| 141 | const controller = new exports.ReadableStreamDefaultController(this) | |
| 142 | ||
| 143 | if (start) { | |
| 144 | this._stream._open = this._open.bind(this, start.call(this, controller)) | |
| 145 | } | |
| 146 | ||
| 147 | if (pull) { | |
| 148 | this._stream._read = this._read.bind(this, pull.bind(this, controller)) | |
| 149 | } | |
| 150 | ||
| 151 | if (cancel) { | |
| 152 | this._stream.once('error', cancel) | |
| 153 | } | |
| 154 | } | |
| 155 | ||
| 156 | this._reader = null | |
| 157 | } | |
| 158 | ||
| 159 | get [readableKind]() { | |
| 160 | return ReadableStream[readableKind] | |
| 161 | } | |
| 162 | ||
| 163 | get locked() { | |
| 164 | return this._reader !== null | |
| 165 | } | |
| 166 | ||
| 167 | getReader() { | |
| 168 | if (this.locked) throw new TypeError('Stream is locked') | |
| 169 | ||
| 170 | this._reader = new exports.ReadableStreamDefaultReader(this) | |
| 171 | ||
| 172 | return this._reader | |
| 173 | } | |
| 174 | ||
| 175 | cancel(reason = new TypeError('Stream was cancelled')) { | |
| 176 | const stream = this._stream | |
| 177 | ||
| 178 | if (stream.destroyed) return Promise.resolve() | |
| 179 | ||
| 180 | if (this.locked) return Promise.reject(new TypeError('Stream is locked')) | |
| 181 | ||
| 182 | return new Promise((resolve) => | |
| 183 | stream.once('close', resolve).once('error', noop).destroy(reason) | |
| 184 | ) | |
| 185 | } | |
| 186 | ||
| 187 | tee() { | |
| 188 | const [a, b] = tee(this._stream) | |
| 189 | ||
| 190 | return [new ReadableStream(a), new ReadableStream(b)] | |
| 191 | } | |
| 192 | ||
| 193 | pipeTo(destination) { | |
| 194 | return new Promise((resolve, reject) => | |
| 195 | this._stream.pipe(destination._stream, (err) => { | |
| 196 | err ? reject(err) : resolve() | |
| 197 | }) | |
| 198 | ) | |
| 199 | } | |
| 200 | ||
| 201 | [Symbol.asyncIterator]() { | |
| 202 | return this._stream[Symbol.asyncIterator]() | |
| 203 | } | |
| 204 | ||
| 205 | _releaseLock() { | |
| 206 | this._reader = null | |
| 207 | } | |
| 208 | ||
| 209 | async _open(starting, cb) { | |
| 210 | try { | |
| 211 | await starting | |
| 212 | ||
| 213 | cb(null) | |
| 214 | } catch (err) { | |
| 215 | cb(err) | |
| 216 | } | |
| 217 | } | |
| 218 | ||
| 219 | async _read(pull, cb) { | |
| 220 | try { | |
| 221 | await pull() | |
| 222 | ||
| 223 | cb(null) | |
| 224 | } catch (err) { | |
| 225 | cb(err) | |
| 226 | } | |
| 227 | } | |
| 228 | } | |
| 229 | ||
| 230 | function defaultSize() { | |
| 231 | return 1 | |
| 232 | } | |
| 233 | ||
| 234 | exports.ReadableStream = ReadableStream | |
| 235 | ||
| 236 | // https://streams.spec.whatwg.org/#countqueuingstrategy | |
| 237 | exports.CountQueuingStrategy = class CountQueuingStrategy { | |
| 238 | constructor(opts = {}) { | |
| 239 | const { highWaterMark = 1 } = opts | |
| 240 | ||
| 241 | this.highWaterMark = highWaterMark | |
| 242 | } | |
| 243 | ||
| 244 | size(chunk) { | |
| 245 | return 1 | |
| 246 | } | |
| 247 | } | |
| 248 | ||
| 249 | // https://streams.spec.whatwg.org/#bytelengthqueuingstrategy | |
| 250 | exports.ByteLengthQueuingStrategy = class ByteLengthQueuingStrategy { | |
| 251 | constructor(opts = {}) { | |
| 252 | const { highWaterMark = 16384 } = opts | |
| 253 | ||
| 254 | this.highWaterMark = highWaterMark | |
| 255 | } | |
| 256 | ||
| 257 | size(chunk) { | |
| 258 | return chunk.byteLength | |
| 259 | } | |
| 260 | } | |
| 261 | ||
| 262 | exports.isReadableStream = function isReadableStream(value) { | |
| 263 | if (value instanceof ReadableStream) return true | |
| 264 | ||
| 265 | return ( | |
| 266 | typeof value === 'object' && | |
| 267 | value !== null && | |
| 268 | value[readableKind] === ReadableStream[readableKind] | |
| 269 | ) | |
| 270 | } | |
| 271 | ||
| 272 | // https://streams.spec.whatwg.org/#readablestream-errored | |
| 273 | exports.isReadableStreamErrored = function isReadableStreamErrored(stream) { | |
| 274 | return getStreamError(stream._stream) !== null | |
| 275 | } | |
| 276 | ||
| 277 | // https://streams.spec.whatwg.org/#is-readable-stream-disturbed | |
| 278 | exports.isReadableStreamDisturbed = function isReadableStreamDisturbed(stream) { | |
| 279 | return isDisturbed(stream._stream) | |
| 280 | } | |
| 281 | ||
| 282 | // https://streams.spec.whatwg.org/#writablestreamdefaultwriter | |
| 283 | exports.WritableStreamDefaultWriter = class WritableStreamDefaultWriter { | |
| 284 | constructor(stream) { | |
| 285 | this._stream = stream | |
| 286 | this._stream._stream.once('close', onclose).once('error', onerror) | |
| 287 | ||
| 288 | const closed = Promise.withResolvers() | |
| 289 | ||
| 290 | // Avoid unhandled exceptions | |
| 291 | closed.promise.catch(noop) | |
| 292 | ||
| 293 | this._closed = closed | |
| 294 | ||
| 295 | function onclose() { | |
| 296 | closed.resolve() | |
| 297 | } | |
| 298 | ||
| 299 | function onerror(err) { | |
| 300 | closed.reject(err) | |
| 301 | } | |
| 302 | } | |
| 303 | ||
| 304 | get desiredSize() { | |
| 305 | const stream = this._stream._stream | |
| 306 | ||
| 307 | return stream._writableState.highWaterMark - stream._writableState.buffered | |
| 308 | } | |
| 309 | ||
| 310 | get closed() { | |
| 311 | return this._closed.promise | |
| 312 | } | |
| 313 | ||
| 314 | get ready() { | |
| 315 | const stream = this._stream._stream | |
| 316 | ||
| 317 | if (getStreamError(stream)) return Promise.reject() | |
| 318 | ||
| 319 | return Writable.drained(stream).then() | |
| 320 | } | |
| 321 | ||
| 322 | async write(chunk) { | |
| 323 | const stream = this._stream._stream | |
| 324 | ||
| 325 | const err = getStreamError(stream) | |
| 326 | ||
| 327 | if (err) return Promise.reject(err) | |
| 328 | ||
| 329 | stream.write(chunk) | |
| 330 | ||
| 331 | await Writable.drained(stream) | |
| 332 | } | |
| 333 | ||
| 334 | releaseLock() { | |
| 335 | this._closed.reject(new TypeError('Writer was released')) | |
| 336 | this._stream._releaseLock() | |
| 337 | this._stream = null | |
| 338 | } | |
| 339 | ||
| 340 | close() { | |
| 341 | const stream = this._stream._stream | |
| 342 | ||
| 343 | if (stream.destroyed) return Promise.resolve() | |
| 344 | ||
| 345 | return new Promise((resolve) => stream.once('close', resolve).end()) | |
| 346 | } | |
| 347 | ||
| 348 | abort(reason = new TypeError('Stream was aborted')) { | |
| 349 | const stream = this._stream._stream | |
| 350 | ||
| 351 | if (stream.destroyed) return Promise.resolve() | |
| 352 | ||
| 353 | return new Promise((resolve) => stream.once('close', resolve).destroy(reason)) | |
| 354 | } | |
| 355 | } | |
| 356 | ||
| 357 | // https://streams.spec.whatwg.org/#writablestreamdefaultcontroller | |
| 358 | exports.WritableStreamDefaultController = class WritableStreamDefaultController { | |
| 359 | constructor(stream) { | |
| 360 | this._stream = stream | |
| 361 | } | |
| 362 | ||
| 363 | error(err) { | |
| 364 | this._stream._stream.destroy(err) | |
| 365 | } | |
| 366 | } | |
| 367 | ||
| 368 | // https://streams.spec.whatwg.org/#writablestream | |
| 369 | class WritableStream { | |
| 370 | static get [writableKind]() { | |
| 371 | return 0 // Compatibility version | |
| 372 | } | |
| 373 | ||
| 374 | constructor(underlyingSink = {}, queuingStrategy = {}) { | |
| 375 | if (isStreamx(underlyingSink)) { | |
| 376 | this._stream = underlyingSink | |
| 377 | } else { | |
| 378 | if (queuingStrategy === undefined) { | |
| 379 | queuingStrategy = new exports.CountQueuingStrategy() | |
| 380 | } | |
| 381 | ||
| 382 | const { start, write, close, abort } = underlyingSink | |
| 383 | const { highWaterMark = 1, size = defaultSize } = queuingStrategy | |
| 384 | ||
| 385 | this._stream = new Writable({ highWaterMark, byteLength: size }) | |
| 386 | ||
| 387 | this._controller = new exports.WritableStreamDefaultController(this) | |
| 388 | ||
| 389 | if (start) { | |
| 390 | this._stream._open = this._open.bind(this, start.call(this, this._controller)) | |
| 391 | } | |
| 392 | ||
| 393 | if (write) { | |
| 394 | this._stream._write = this._write.bind(this, write) | |
| 395 | } | |
| 396 | ||
| 397 | if (close) { | |
| 398 | this._stream._destroy = this._destroy.bind(this, close.call(this)) | |
| 399 | } | |
| 400 | ||
| 401 | if (abort) { | |
| 402 | this._stream.once('error', abort) | |
| 403 | } | |
| 404 | } | |
| 405 | ||
| 406 | this._writer = null | |
| 407 | } | |
| 408 | ||
| 409 | get [writableKind]() { | |
| 410 | return WritableStream[writableKind] | |
| 411 | } | |
| 412 | ||
| 413 | get locked() { | |
| 414 | return this._writer !== null | |
| 415 | } | |
| 416 | ||
| 417 | getWriter() { | |
| 418 | if (this.locked) throw new TypeError('Stream is locked') | |
| 419 | ||
| 420 | this._writer = new exports.WritableStreamDefaultWriter(this) | |
| 421 | ||
| 422 | return this._writer | |
| 423 | } | |
| 424 | ||
| 425 | abort(reason = new TypeError('Stream was aborted')) { | |
| 426 | if (this._stream.destroyed) return Promise.resolve() | |
| 427 | ||
| 428 | if (this.locked) return Promise.reject(new TypeError('Stream is locked')) | |
| 429 | ||
| 430 | return new Promise((resolve) => this._stream.once('close', resolve).destroy(reason)) | |
| 431 | } | |
| 432 | ||
| 433 | close() { | |
| 434 | if (this._stream.destroyed) return Promise.resolve() | |
| 435 | ||
| 436 | if (this.locked) return Promise.reject(new TypeError('Stream is locked')) | |
| 437 | ||
| 438 | return new Promise((resolve) => this._stream.once('close', resolve).end()) | |
| 439 | } | |
| 440 | ||
| 441 | _releaseLock() { | |
| 442 | this._writer = null | |
| 443 | } | |
| 444 | ||
| 445 | async _open(starting, cb) { | |
| 446 | try { | |
| 447 | await starting | |
| 448 | ||
| 449 | cb(null) | |
| 450 | } catch (err) { | |
| 451 | cb(err) | |
| 452 | } | |
| 453 | } | |
| 454 | ||
| 455 | async _write(write, data, cb) { | |
| 456 | try { | |
| 457 | await write(data, this._controller) | |
| 458 | ||
| 459 | cb(null) | |
| 460 | } catch (err) { | |
| 461 | cb(err) | |
| 462 | } | |
| 463 | } | |
| 464 | ||
| 465 | async _destroy(closing, cb) { | |
| 466 | try { | |
| 467 | await closing | |
| 468 | ||
| 469 | cb(null) | |
| 470 | } catch (err) { | |
| 471 | cb(err) | |
| 472 | } | |
| 473 | } | |
| 474 | } | |
| 475 | ||
| 476 | exports.WritableStream = WritableStream | |
| 477 | ||
| 478 | exports.isWritableStream = function isWritableStream(value) { | |
| 479 | if (value instanceof WritableStream) return true | |
| 480 | ||
| 481 | return ( | |
| 482 | typeof value === 'object' && | |
| 483 | value !== null && | |
| 484 | value[writableKind] === WritableStream[writableKind] | |
| 485 | ) | |
| 486 | } | |
| 487 | ||
| 488 | function noop() {} |
| … | … | @@ -0,0 +1,13 @@ |
| 1 | cmake_minimum_required(VERSION 3.25) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | ||
| 5 | project(bare_type C) | |
| 6 | ||
| 7 | add_bare_module(bare_type) | |
| 8 | ||
| 9 | target_sources( | |
| 10 | ${bare_type} | |
| 11 | PRIVATE | |
| 12 | binding.c | |
| 13 | ) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | # bare-type | |
| 2 | ||
| 3 | Cross-realm type predicates for Bare. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i bare-type | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const type = require('bare-type') | |
| 13 | ||
| 14 | if (type(123).isNumber()) { | |
| 15 | console.log(123, 'is a number') | |
| 16 | } | |
| 17 | ``` | |
| 18 | ||
| 19 | ## License | |
| 20 | ||
| 21 | Apache-2.0 |
| … | … | @@ -0,0 +1,315 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <bare.h> | |
| 3 | #include <js.h> | |
| 4 | #include <stdint.h> | |
| 5 | #include <utf.h> | |
| 6 | ||
| 7 | typedef enum { | |
| 8 | // Base types | |
| 9 | bare_type_undefined = js_undefined, | |
| 10 | bare_type_null = js_null, | |
| 11 | bare_type_boolean = js_boolean, | |
| 12 | bare_type_number = js_number, | |
| 13 | bare_type_string = js_string, | |
| 14 | bare_type_symbol = js_symbol, | |
| 15 | bare_type_object = js_object, | |
| 16 | bare_type_function = js_function, | |
| 17 | bare_type_external = js_external, | |
| 18 | bare_type_bigint = js_bigint, | |
| 19 | ||
| 20 | // Number types | |
| 21 | bare_type_int32 = 1 << 8, | |
| 22 | bare_type_uint32 = 1 << 9, | |
| 23 | ||
| 24 | // Object types | |
| 25 | bare_type_array = 1 << 8, | |
| 26 | bare_type_arguments = 2 << 8, | |
| 27 | bare_type_date = 3 << 8, | |
| 28 | bare_type_regexp = 4 << 8, | |
| 29 | bare_type_error = 5 << 8, | |
| 30 | bare_type_promise = 6 << 8, | |
| 31 | bare_type_proxy = 7 << 8, | |
| 32 | bare_type_generator = 8 << 8, | |
| 33 | bare_type_map = 9 << 8, | |
| 34 | bare_type_set = 10 << 8, | |
| 35 | bare_type_weak_map = 11 << 8, | |
| 36 | bare_type_weak_set = 12 << 8, | |
| 37 | bare_type_weak_ref = 13 << 8, | |
| 38 | bare_type_arraybuffer = 14 << 8, | |
| 39 | bare_type_sharedarraybuffer = 15 << 8, | |
| 40 | bare_type_typedarray = 16 << 8, | |
| 41 | bare_type_dataview = 17 << 8, | |
| 42 | bare_type_module_namespace = 18 << 8, | |
| 43 | ||
| 44 | // TypedArray types | |
| 45 | bare_type_int8array = 1 << 16, | |
| 46 | bare_type_uint8array = 2 << 16, | |
| 47 | bare_type_uint8clampedarray = 3 << 16, | |
| 48 | bare_type_int16array = 4 << 16, | |
| 49 | bare_type_uint16array = 5 << 16, | |
| 50 | bare_type_int32array = 6 << 16, | |
| 51 | bare_type_uint32array = 7 << 16, | |
| 52 | bare_type_float16array = 12 << 16, | |
| 53 | bare_type_float32array = 8 << 16, | |
| 54 | bare_type_float64array = 9 << 16, | |
| 55 | bare_type_bigint64array = 10 << 16, | |
| 56 | bare_type_biguint64array = 11 << 16, | |
| 57 | ||
| 58 | // Function types | |
| 59 | bare_type_async_function = 1 << 8, | |
| 60 | bare_type_generator_function = 1 << 9, | |
| 61 | } bare_type_t; | |
| 62 | ||
| 63 | static js_value_t * | |
| 64 | bare_type(js_env_t *env, js_callback_info_t *info) { | |
| 65 | int err; | |
| 66 | ||
| 67 | size_t argc = 1; | |
| 68 | js_value_t *argv[1]; | |
| 69 | ||
| 70 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 71 | assert(err == 0); | |
| 72 | ||
| 73 | assert(argc == 1); | |
| 74 | ||
| 75 | js_value_t *value = argv[0]; | |
| 76 | ||
| 77 | uint32_t type; | |
| 78 | err = js_typeof(env, value, (js_value_type_t *) &type); | |
| 79 | assert(err == 0); | |
| 80 | ||
| 81 | switch (type) { | |
| 82 | case bare_type_number: { | |
| 83 | #define V(t) \ | |
| 84 | bool is_##t; \ | |
| 85 | err = js_is_##t(env, value, &is_##t); \ | |
| 86 | assert(err == 0); \ | |
| 87 | if (is_##t) type |= bare_type_##t; | |
| 88 | ||
| 89 | V(int32) | |
| 90 | V(uint32) | |
| 91 | #undef V | |
| 92 | ||
| 93 | break; | |
| 94 | } | |
| 95 | ||
| 96 | case bare_type_object: { | |
| 97 | #define V(t) \ | |
| 98 | bool is_##t; \ | |
| 99 | err = js_is_##t(env, value, &is_##t); \ | |
| 100 | assert(err == 0); \ | |
| 101 | if (is_##t) { \ | |
| 102 | type |= bare_type_##t; \ | |
| 103 | break; \ | |
| 104 | } | |
| 105 | ||
| 106 | V(array) | |
| 107 | V(arguments) | |
| 108 | V(date) | |
| 109 | V(regexp) | |
| 110 | V(error) | |
| 111 | V(promise) | |
| 112 | V(proxy) | |
| 113 | V(generator) | |
| 114 | V(map) | |
| 115 | V(set) | |
| 116 | V(weak_map) | |
| 117 | V(weak_set) | |
| 118 | V(weak_ref) | |
| 119 | V(arraybuffer) | |
| 120 | V(sharedarraybuffer) | |
| 121 | V(typedarray) | |
| 122 | V(dataview) | |
| 123 | V(module_namespace) | |
| 124 | #undef V | |
| 125 | ||
| 126 | break; | |
| 127 | } | |
| 128 | ||
| 129 | case bare_type_function: { | |
| 130 | #define V(t) \ | |
| 131 | bool is_##t; \ | |
| 132 | err = js_is_##t(env, value, &is_##t); \ | |
| 133 | assert(err == 0); \ | |
| 134 | if (is_##t) type |= bare_type_##t; | |
| 135 | ||
| 136 | V(async_function) | |
| 137 | V(generator_function) | |
| 138 | #undef V | |
| 139 | ||
| 140 | break; | |
| 141 | } | |
| 142 | } | |
| 143 | ||
| 144 | switch (type) { | |
| 145 | case bare_type_object | bare_type_typedarray: { | |
| 146 | #define V(t) \ | |
| 147 | bool is_##t; \ | |
| 148 | err = js_is_##t(env, value, &is_##t); \ | |
| 149 | assert(err == 0); \ | |
| 150 | if (is_##t) { \ | |
| 151 | type |= bare_type_##t; \ | |
| 152 | break; \ | |
| 153 | } | |
| 154 | ||
| 155 | V(int8array) | |
| 156 | V(uint8array) | |
| 157 | V(uint8clampedarray) | |
| 158 | V(int16array) | |
| 159 | V(uint16array) | |
| 160 | V(int32array) | |
| 161 | V(uint32array) | |
| 162 | V(float16array) | |
| 163 | V(float32array) | |
| 164 | V(float64array) | |
| 165 | V(bigint64array) | |
| 166 | V(biguint64array) | |
| 167 | #undef V | |
| 168 | ||
| 169 | break; | |
| 170 | } | |
| 171 | } | |
| 172 | ||
| 173 | js_value_t *result; | |
| 174 | err = js_create_uint32(env, type, &result); | |
| 175 | assert(err == 0); | |
| 176 | ||
| 177 | return result; | |
| 178 | } | |
| 179 | ||
| 180 | static js_value_t * | |
| 181 | bare_type_add_tag(js_env_t *env, js_callback_info_t *info) { | |
| 182 | int err; | |
| 183 | ||
| 184 | size_t argc = 2; | |
| 185 | js_value_t *argv[2]; | |
| 186 | ||
| 187 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 188 | assert(err == 0); | |
| 189 | ||
| 190 | assert(argc == 2); | |
| 191 | ||
| 192 | js_type_tag_t *tag; | |
| 193 | err = js_get_typedarray_info(env, argv[1], NULL, (void **) &tag, NULL, NULL, NULL); | |
| 194 | assert(err == 0); | |
| 195 | ||
| 196 | js_add_type_tag(env, argv[0], tag); | |
| 197 | ||
| 198 | return NULL; | |
| 199 | } | |
| 200 | ||
| 201 | static js_value_t * | |
| 202 | bare_type_check_tag(js_env_t *env, js_callback_info_t *info) { | |
| 203 | int err; | |
| 204 | ||
| 205 | size_t argc = 2; | |
| 206 | js_value_t *argv[2]; | |
| 207 | ||
| 208 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 209 | assert(err == 0); | |
| 210 | ||
| 211 | assert(argc == 2); | |
| 212 | ||
| 213 | js_type_tag_t *tag; | |
| 214 | err = js_get_typedarray_info(env, argv[1], NULL, (void **) &tag, NULL, NULL, NULL); | |
| 215 | assert(err == 0); | |
| 216 | ||
| 217 | bool matches; | |
| 218 | err = js_check_type_tag(env, argv[0], tag, &matches); | |
| 219 | assert(err == 0); | |
| 220 | ||
| 221 | js_value_t *result; | |
| 222 | err = js_get_boolean(env, matches, &result); | |
| 223 | assert(err == 0); | |
| 224 | ||
| 225 | return result; | |
| 226 | } | |
| 227 | ||
| 228 | static js_value_t * | |
| 229 | bare_type_exports(js_env_t *env, js_value_t *exports) { | |
| 230 | int err; | |
| 231 | ||
| 232 | #define V(name, fn) \ | |
| 233 | { \ | |
| 234 | js_value_t *val; \ | |
| 235 | err = js_create_function(env, name, -1, fn, NULL, &val); \ | |
| 236 | assert(err == 0); \ | |
| 237 | err = js_set_named_property(env, exports, name, val); \ | |
| 238 | assert(err == 0); \ | |
| 239 | } | |
| 240 | ||
| 241 | V("type", bare_type) | |
| 242 | V("addTag", bare_type_add_tag) | |
| 243 | V("checkTag", bare_type_check_tag) | |
| 244 | #undef V | |
| 245 | ||
| 246 | js_value_t *constants; | |
| 247 | err = js_create_object(env, &constants); | |
| 248 | assert(err == 0); | |
| 249 | ||
| 250 | err = js_set_named_property(env, exports, "constants", constants); | |
| 251 | assert(err == 0); | |
| 252 | ||
| 253 | #define V(name, n) \ | |
| 254 | { \ | |
| 255 | js_value_t *val; \ | |
| 256 | err = js_create_uint32(env, n, &val); \ | |
| 257 | assert(err == 0); \ | |
| 258 | err = js_set_named_property(env, constants, name, val); \ | |
| 259 | assert(err == 0); \ | |
| 260 | } | |
| 261 | ||
| 262 | V("UNDEFINED", bare_type_undefined) | |
| 263 | V("NULL", bare_type_null) | |
| 264 | V("BOOLEAN", bare_type_boolean) | |
| 265 | V("NUMBER", bare_type_number) | |
| 266 | V("STRING", bare_type_string) | |
| 267 | V("SYMBOL", bare_type_symbol) | |
| 268 | V("OBJECT", bare_type_object) | |
| 269 | V("FUNCTION", bare_type_function) | |
| 270 | V("EXTERNAL", bare_type_external) | |
| 271 | V("BIGINT", bare_type_bigint) | |
| 272 | ||
| 273 | V("INT32", bare_type_int32) | |
| 274 | V("UINT32", bare_type_uint32) | |
| 275 | ||
| 276 | V("ARRAY", bare_type_array) | |
| 277 | V("ARGUMENTS", bare_type_arguments) | |
| 278 | V("DATE", bare_type_date) | |
| 279 | V("REGEXP", bare_type_regexp) | |
| 280 | V("ERROR", bare_type_error) | |
| 281 | V("PROMISE", bare_type_promise) | |
| 282 | V("PROXY", bare_type_proxy) | |
| 283 | V("GENERATOR", bare_type_generator) | |
| 284 | V("MAP", bare_type_map) | |
| 285 | V("SET", bare_type_set) | |
| 286 | V("WEAK_MAP", bare_type_weak_map) | |
| 287 | V("WEAK_SET", bare_type_weak_set) | |
| 288 | V("WEAK_REF", bare_type_weak_ref) | |
| 289 | V("ARRAYBUFFER", bare_type_arraybuffer) | |
| 290 | V("SHAREDARRAYBUFFER", bare_type_sharedarraybuffer) | |
| 291 | V("TYPEDARRAY", bare_type_typedarray) | |
| 292 | V("DATAVIEW", bare_type_dataview) | |
| 293 | V("MODULE_NAMESPACE", bare_type_module_namespace) | |
| 294 | ||
| 295 | V("INT8ARRAY", bare_type_int8array) | |
| 296 | V("UINT8ARRAY", bare_type_uint8array) | |
| 297 | V("UINT8CLAMPEDARRAY", bare_type_uint8clampedarray) | |
| 298 | V("INT16ARRAY", bare_type_int16array) | |
| 299 | V("UINT16ARRAY", bare_type_uint16array) | |
| 300 | V("INT32ARRAY", bare_type_int32array) | |
| 301 | V("UINT32ARRAY", bare_type_uint32array) | |
| 302 | V("FLOAT16ARRAY", bare_type_float16array) | |
| 303 | V("FLOAT32ARRAY", bare_type_float32array) | |
| 304 | V("FLOAT64ARRAY", bare_type_float64array) | |
| 305 | V("BIGINT64ARRAY", bare_type_bigint64array) | |
| 306 | V("BIGUINT64ARRAY", bare_type_biguint64array) | |
| 307 | ||
| 308 | V("ASYNC_FUNCTION", bare_type_async_function) | |
| 309 | V("GENERATOR_FUNCTION", bare_type_generator_function) | |
| 310 | #undef V | |
| 311 | ||
| 312 | return exports; | |
| 313 | } | |
| 314 | ||
| 315 | BARE_MODULE(bare_type, bare_type_exports) |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require.addon() |
| … | … | @@ -0,0 +1,62 @@ |
| 1 | interface Type { | |
| 2 | isUndefined(): boolean | |
| 3 | isNull(): boolean | |
| 4 | isBoolean(): boolean | |
| 5 | isNumber(): boolean | |
| 6 | isInt32(): boolean | |
| 7 | isUint32(): boolean | |
| 8 | isString(): boolean | |
| 9 | isSymbol(): boolean | |
| 10 | isObject(): boolean | |
| 11 | isArray(): boolean | |
| 12 | isArguments(): boolean | |
| 13 | isDate(): boolean | |
| 14 | isRegExp(): boolean | |
| 15 | isError(): boolean | |
| 16 | isPromise(): boolean | |
| 17 | isProxy(): boolean | |
| 18 | isGenerator(): boolean | |
| 19 | isMap(): boolean | |
| 20 | isSet(): boolean | |
| 21 | isWeakMap(): boolean | |
| 22 | isWeakSet(): boolean | |
| 23 | isWeakRef(): boolean | |
| 24 | isArrayBuffer(): boolean | |
| 25 | isSharedArrayBuffer(): boolean | |
| 26 | isTypedArray(): boolean | |
| 27 | isInt8Array(): boolean | |
| 28 | isUint8Array(): boolean | |
| 29 | isUint8ClampedArray(): boolean | |
| 30 | isInt16Array(): boolean | |
| 31 | isUint16Array(): boolean | |
| 32 | isInt32Array(): boolean | |
| 33 | isUint32Array(): boolean | |
| 34 | isFloat16Array(): boolean | |
| 35 | isFloat32Array(): boolean | |
| 36 | isFloat64Array(): boolean | |
| 37 | isBigInt64Array(): boolean | |
| 38 | isBigUint64Array(): boolean | |
| 39 | isDataView(): boolean | |
| 40 | isModuleNamespace(): boolean | |
| 41 | isFunction(): boolean | |
| 42 | isAsyncFunction(): boolean | |
| 43 | isGeneratorFunction(): boolean | |
| 44 | isExternal(): boolean | |
| 45 | isBigInt(): boolean | |
| 46 | } | |
| 47 | ||
| 48 | declare class Type { | |
| 49 | constructor(type: number) | |
| 50 | } | |
| 51 | ||
| 52 | declare function type(value: unknown): Type | |
| 53 | ||
| 54 | declare namespace type { | |
| 55 | export function createTag(...components: number[]): Uint32Array | |
| 56 | ||
| 57 | export function addTag(object: object, tag: Uint32Array): void | |
| 58 | ||
| 59 | export function checkTag(object: object, tag: Uint32Array): boolean | |
| 60 | } | |
| 61 | ||
| 62 | export = type |
| … | … | @@ -0,0 +1,228 @@ |
| 1 | const binding = require('./binding') | |
| 2 | ||
| 3 | const t = binding.constants | |
| 4 | ||
| 5 | class Type { | |
| 6 | constructor(type) { | |
| 7 | this._type = type | |
| 8 | } | |
| 9 | ||
| 10 | isUndefined() { | |
| 11 | return this._type === t.UNDEFINED | |
| 12 | } | |
| 13 | ||
| 14 | isNull() { | |
| 15 | return this._type === t.NULL | |
| 16 | } | |
| 17 | ||
| 18 | isBoolean() { | |
| 19 | return this._type === t.BOOLEAN | |
| 20 | } | |
| 21 | ||
| 22 | isNumber() { | |
| 23 | return (this._type & 0xff) === t.NUMBER | |
| 24 | } | |
| 25 | ||
| 26 | isInt32() { | |
| 27 | return (this._type & (0xff | t.INT32)) === (t.NUMBER | t.INT32) | |
| 28 | } | |
| 29 | ||
| 30 | isUint32() { | |
| 31 | return (this._type & (0xff | t.UINT32)) === (t.NUMBER | t.UINT32) | |
| 32 | } | |
| 33 | ||
| 34 | isString() { | |
| 35 | return this._type === t.STRING | |
| 36 | } | |
| 37 | ||
| 38 | isSymbol() { | |
| 39 | return this._type === t.SYMBOL | |
| 40 | } | |
| 41 | ||
| 42 | isObject() { | |
| 43 | return (this._type & 0xff) === t.OBJECT | |
| 44 | } | |
| 45 | ||
| 46 | isArray() { | |
| 47 | return this._type === (t.OBJECT | t.ARRAY) | |
| 48 | } | |
| 49 | ||
| 50 | isArguments() { | |
| 51 | return this._type === (t.OBJECT | t.ARGUMENTS) | |
| 52 | } | |
| 53 | ||
| 54 | isDate() { | |
| 55 | return this._type === (t.OBJECT | t.DATE) | |
| 56 | } | |
| 57 | ||
| 58 | isRegExp() { | |
| 59 | return this._type === (t.OBJECT | t.REGEXP) | |
| 60 | } | |
| 61 | ||
| 62 | isError() { | |
| 63 | return this._type === (t.OBJECT | t.ERROR) | |
| 64 | } | |
| 65 | ||
| 66 | isPromise() { | |
| 67 | return this._type === (t.OBJECT | t.PROMISE) | |
| 68 | } | |
| 69 | ||
| 70 | isProxy() { | |
| 71 | return this._type === (t.OBJECT | t.PROXY) | |
| 72 | } | |
| 73 | ||
| 74 | isGenerator() { | |
| 75 | return this._type === (t.OBJECT | t.GENERATOR) | |
| 76 | } | |
| 77 | ||
| 78 | isMap() { | |
| 79 | return this._type === (t.OBJECT | t.MAP) | |
| 80 | } | |
| 81 | ||
| 82 | isSet() { | |
| 83 | return this._type === (t.OBJECT | t.SET) | |
| 84 | } | |
| 85 | ||
| 86 | isWeakMap() { | |
| 87 | return this._type === (t.OBJECT | t.WEAK_MAP) | |
| 88 | } | |
| 89 | ||
| 90 | isWeakSet() { | |
| 91 | return this._type === (t.OBJECT | t.WEAK_SET) | |
| 92 | } | |
| 93 | ||
| 94 | isWeakRef() { | |
| 95 | return this._type === (t.OBJECT | t.WEAK_REF) | |
| 96 | } | |
| 97 | ||
| 98 | isArrayBuffer() { | |
| 99 | return this._type === (t.OBJECT | t.ARRAYBUFFER) | |
| 100 | } | |
| 101 | ||
| 102 | isSharedArrayBuffer() { | |
| 103 | return this._type === (t.OBJECT | t.SHAREDARRAYBUFFER) | |
| 104 | } | |
| 105 | ||
| 106 | isTypedArray() { | |
| 107 | return (this._type & 0xffff) === (t.OBJECT | t.TYPEDARRAY) | |
| 108 | } | |
| 109 | ||
| 110 | isInt8Array() { | |
| 111 | return this._type === (t.OBJECT | t.TYPEDARRAY | t.INT8ARRAY) | |
| 112 | } | |
| 113 | ||
| 114 | isUint8Array() { | |
| 115 | return this._type === (t.OBJECT | t.TYPEDARRAY | t.UINT8ARRAY) | |
| 116 | } | |
| 117 | ||
| 118 | isUint8ClampedArray() { | |
| 119 | return this._type === (t.OBJECT | t.TYPEDARRAY | t.UINT8CLAMPEDARRAY) | |
| 120 | } | |
| 121 | ||
| 122 | isInt16Array() { | |
| 123 | return this._type === (t.OBJECT | t.TYPEDARRAY | t.INT16ARRAY) | |
| 124 | } | |
| 125 | ||
| 126 | isUint16Array() { | |
| 127 | return this._type === (t.OBJECT | t.TYPEDARRAY | t.UINT16ARRAY) | |
| 128 | } | |
| 129 | ||
| 130 | isInt32Array() { | |
| 131 | return this._type === (t.OBJECT | t.TYPEDARRAY | t.INT32ARRAY) | |
| 132 | } | |
| 133 | ||
| 134 | isUint32Array() { | |
| 135 | return this._type === (t.OBJECT | t.TYPEDARRAY | t.UINT32ARRAY) | |
| 136 | } | |
| 137 | ||
| 138 | isFloat16Array() { | |
| 139 | return this._type === (t.OBJECT | t.TYPEDARRAY | t.FLOAT16ARRAY) | |
| 140 | } | |
| 141 | ||
| 142 | isFloat32Array() { | |
| 143 | return this._type === (t.OBJECT | t.TYPEDARRAY | t.FLOAT32ARRAY) | |
| 144 | } | |
| 145 | ||
| 146 | isFloat64Array() { | |
| 147 | return this._type === (t.OBJECT | t.TYPEDARRAY | t.FLOAT64ARRAY) | |
| 148 | } | |
| 149 | ||
| 150 | isBigInt64Array() { | |
| 151 | return this._type === (t.OBJECT | t.TYPEDARRAY | t.BIGINT64ARRAY) | |
| 152 | } | |
| 153 | ||
| 154 | isBigUint64Array() { | |
| 155 | return this._type === (t.OBJECT | t.TYPEDARRAY | t.BIGUINT64ARRAY) | |
| 156 | } | |
| 157 | ||
| 158 | isDataView() { | |
| 159 | return this._type === (t.OBJECT | t.DATAVIEW) | |
| 160 | } | |
| 161 | ||
| 162 | isModuleNamespace() { | |
| 163 | return this._type === (t.OBJECT | t.MODULE_NAMESPACE) | |
| 164 | } | |
| 165 | ||
| 166 | isFunction() { | |
| 167 | return (this._type & 0xff) === t.FUNCTION | |
| 168 | } | |
| 169 | ||
| 170 | isAsyncFunction() { | |
| 171 | return ( | |
| 172 | (this._type & (0xff | t.ASYNC_FUNCTION)) === | |
| 173 | (t.FUNCTION | t.ASYNC_FUNCTION) | |
| 174 | ) | |
| 175 | } | |
| 176 | ||
| 177 | isGeneratorFunction() { | |
| 178 | return ( | |
| 179 | (this._type & (0xff | t.GENERATOR_FUNCTION)) === | |
| 180 | (t.FUNCTION | t.GENERATOR_FUNCTION) | |
| 181 | ) | |
| 182 | } | |
| 183 | ||
| 184 | isExternal() { | |
| 185 | return this._type === t.EXTERNAL | |
| 186 | } | |
| 187 | ||
| 188 | isBigInt() { | |
| 189 | return this._type === t.BIGINT | |
| 190 | } | |
| 191 | } | |
| 192 | ||
| 193 | module.exports = exports = function type(value) { | |
| 194 | switch (typeof value) { | |
| 195 | case 'undefined': | |
| 196 | return new Type(t.UNDEFINED) | |
| 197 | case 'boolean': | |
| 198 | return new Type(t.BOOLEAN) | |
| 199 | case 'number': | |
| 200 | return new Type( | |
| 201 | Number.isSafeInteger(value) ? binding.type(value) : t.NUMBER | |
| 202 | ) | |
| 203 | case 'string': | |
| 204 | return new Type(t.STRING) | |
| 205 | case 'symbol': | |
| 206 | return new Type(t.SYMBOL) | |
| 207 | case 'object': | |
| 208 | return new Type(value === null ? t.NULL : binding.type(value)) | |
| 209 | case 'function': | |
| 210 | return new Type(binding.type(value)) | |
| 211 | case 'bigint': | |
| 212 | return new Type(t.BIGINT) | |
| 213 | } | |
| 214 | } | |
| 215 | ||
| 216 | exports.createTag = function createTag(...components) { | |
| 217 | const tag = new Uint32Array(4) | |
| 218 | for (let i = 0; i < 4; i++) tag[i] = components[i] || 0 | |
| 219 | return tag | |
| 220 | } | |
| 221 | ||
| 222 | exports.addTag = function addTag(object, tag) { | |
| 223 | binding.addTag(object, tag) | |
| 224 | } | |
| 225 | ||
| 226 | exports.checkTag = function checkTag(object, tag) { | |
| 227 | return binding.checkTag(object, tag) | |
| 228 | } |
| … | … | @@ -0,0 +1,43 @@ |
| 1 | { | |
| 2 | "name": "bare-type", | |
| 3 | "version": "1.1.0", | |
| 4 | "description": "Cross-realm type predicates for Bare", | |
| 5 | "exports": { | |
| 6 | ".": { | |
| 7 | "types": "./index.d.ts", | |
| 8 | "default": "./index.js" | |
| 9 | }, | |
| 10 | "./package": "./package.json" | |
| 11 | }, | |
| 12 | "files": [ | |
| 13 | "index.js", | |
| 14 | "index.d.ts", | |
| 15 | "binding.c", | |
| 16 | "binding.js", | |
| 17 | "CMakeLists.txt", | |
| 18 | "prebuilds" | |
| 19 | ], | |
| 20 | "addon": true, | |
| 21 | "scripts": { | |
| 22 | "test": "prettier . --check && bare test.js" | |
| 23 | }, | |
| 24 | "repository": { | |
| 25 | "type": "git", | |
| 26 | "url": "git+https://github.com/holepunchto/bare-type.git" | |
| 27 | }, | |
| 28 | "author": "Holepunch", | |
| 29 | "license": "Apache-2.0", | |
| 30 | "bugs": { | |
| 31 | "url": "https://github.com/holepunchto/bare-type/issues" | |
| 32 | }, | |
| 33 | "homepage": "https://github.com/holepunchto/bare-type#readme", | |
| 34 | "engines": { | |
| 35 | "bare": ">=1.2.0" | |
| 36 | }, | |
| 37 | "devDependencies": { | |
| 38 | "brittle": "^3.4.0", | |
| 39 | "cmake-bare": "^1.1.6", | |
| 40 | "prettier": "^3.4.1", | |
| 41 | "prettier-config-standard": "^7.0.0" | |
| 42 | } | |
| 43 | } |
| … | … | @@ -0,0 +1,27 @@ |
| 1 | cmake_minimum_required(VERSION 4.0) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) | |
| 5 | ||
| 6 | project(bare_url C) | |
| 7 | ||
| 8 | fetch_package("github:holepunchto/libutf#6b1a36f") | |
| 9 | fetch_package("github:holepunchto/liburl#03f1488") | |
| 10 | ||
| 11 | add_bare_module(bare_url) | |
| 12 | ||
| 13 | target_sources( | |
| 14 | ${bare_url} | |
| 15 | PRIVATE | |
| 16 | binding.c | |
| 17 | ) | |
| 18 | ||
| 19 | target_link_libraries( | |
| 20 | ${bare_url} | |
| 21 | PRIVATE | |
| 22 | $<TARGET_OBJECTS:utf> | |
| 23 | $<TARGET_OBJECTS:url> | |
| 24 | PUBLIC | |
| 25 | utf | |
| 26 | url | |
| 27 | ) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,19 @@ |
| 1 | # bare-url | |
| 2 | ||
| 3 | WHATWG URL implementation for JavaScript. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i bare-url | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const url = require('bare-url') | |
| 13 | ||
| 14 | const p = url.fileURLToPath('file:///foo') // --> /foo | |
| 15 | ``` | |
| 16 | ||
| 17 | ## License | |
| 18 | ||
| 19 | Apache-2.0 |
| … | … | @@ -0,0 +1,188 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <bare.h> | |
| 3 | #include <js.h> | |
| 4 | #include <stddef.h> | |
| 5 | #include <stdlib.h> | |
| 6 | #include <string.h> | |
| 7 | #include <url.h> | |
| 8 | #include <utf.h> | |
| 9 | #include <utf/string.h> | |
| 10 | ||
| 11 | static js_type_tag_t bare_url__tag = {0x6f1fd92f476698b0, 0x93b59c349143ee50}; | |
| 12 | ||
| 13 | static js_value_t * | |
| 14 | bare_url_parse (js_env_t *env, js_callback_info_t *info) { | |
| 15 | int err; | |
| 16 | ||
| 17 | size_t argc = 4; | |
| 18 | js_value_t *argv[4]; | |
| 19 | ||
| 20 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 21 | assert(err == 0); | |
| 22 | ||
| 23 | assert(argc == 4); | |
| 24 | ||
| 25 | bool should_throw; | |
| 26 | err = js_get_value_bool(env, argv[3], &should_throw); | |
| 27 | assert(err == 0); | |
| 28 | ||
| 29 | bool has_base; | |
| 30 | err = js_is_string(env, argv[1], &has_base); | |
| 31 | assert(err == 0); | |
| 32 | ||
| 33 | url_t base; | |
| 34 | url_init(&base); | |
| 35 | ||
| 36 | if (has_base) { | |
| 37 | size_t len; | |
| 38 | err = js_get_value_string_utf8(env, argv[1], NULL, 0, &len); | |
| 39 | assert(err == 0); | |
| 40 | ||
| 41 | utf8_t *input = malloc(len); | |
| 42 | err = js_get_value_string_utf8(env, argv[1], input, len, NULL); | |
| 43 | assert(err == 0); | |
| 44 | ||
| 45 | err = url_parse(&base, input, len, NULL); | |
| 46 | ||
| 47 | free(input); | |
| 48 | ||
| 49 | if (err < 0) { | |
| 50 | url_destroy(&base); | |
| 51 | ||
| 52 | if (should_throw) js_throw_error(env, NULL, "Invalid base URL"); | |
| 53 | ||
| 54 | return NULL; | |
| 55 | } | |
| 56 | } | |
| 57 | ||
| 58 | size_t len; | |
| 59 | err = js_get_value_string_utf8(env, argv[0], NULL, 0, &len); | |
| 60 | assert(err == 0); | |
| 61 | ||
| 62 | utf8_t *input = malloc(len); | |
| 63 | err = js_get_value_string_utf8(env, argv[0], input, len, NULL); | |
| 64 | assert(err == 0); | |
| 65 | ||
| 66 | js_value_t *handle; | |
| 67 | ||
| 68 | url_t url; | |
| 69 | url_init(&url); | |
| 70 | ||
| 71 | err = url_parse(&url, input, len, has_base ? &base : NULL); | |
| 72 | ||
| 73 | free(input); | |
| 74 | ||
| 75 | if (err < 0) { | |
| 76 | url_destroy(&base); | |
| 77 | url_destroy(&url); | |
| 78 | ||
| 79 | if (should_throw) js_throw_error(env, NULL, "Invalid URL"); | |
| 80 | ||
| 81 | return NULL; | |
| 82 | } | |
| 83 | ||
| 84 | js_value_t *href; | |
| 85 | err = js_create_string_utf8(env, url.href.data, url.href.len, &href); | |
| 86 | assert(err == 0); | |
| 87 | ||
| 88 | uint32_t *components; | |
| 89 | err = js_get_typedarray_info(env, argv[2], NULL, (void **) &components, NULL, NULL, NULL); | |
| 90 | assert(err == 0); | |
| 91 | ||
| 92 | memcpy(components, &url.components, sizeof(url.components)); | |
| 93 | ||
| 94 | url_destroy(&base); | |
| 95 | url_destroy(&url); | |
| 96 | ||
| 97 | return href; | |
| 98 | } | |
| 99 | ||
| 100 | static js_value_t * | |
| 101 | bare_url_can_parse (js_env_t *env, js_callback_info_t *info) { | |
| 102 | int err; | |
| 103 | ||
| 104 | size_t argc = 2; | |
| 105 | js_value_t *argv[2]; | |
| 106 | ||
| 107 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 108 | assert(err == 0); | |
| 109 | ||
| 110 | assert(argc == 2); | |
| 111 | ||
| 112 | bool has_base; | |
| 113 | err = js_is_string(env, argv[1], &has_base); | |
| 114 | assert(err == 0); | |
| 115 | ||
| 116 | url_t base; | |
| 117 | url_init(&base); | |
| 118 | ||
| 119 | if (has_base) { | |
| 120 | size_t len; | |
| 121 | err = js_get_value_string_utf8(env, argv[1], NULL, 0, &len); | |
| 122 | assert(err == 0); | |
| 123 | ||
| 124 | utf8_t *input = malloc(len); | |
| 125 | err = js_get_value_string_utf8(env, argv[1], input, len, NULL); | |
| 126 | assert(err == 0); | |
| 127 | ||
| 128 | err = url_parse(&base, input, len, NULL); | |
| 129 | ||
| 130 | free(input); | |
| 131 | ||
| 132 | if (err < 0) { | |
| 133 | url_destroy(&base); | |
| 134 | ||
| 135 | js_value_t *result; | |
| 136 | err = js_get_boolean(env, false, &result); | |
| 137 | assert(err == 0); | |
| 138 | ||
| 139 | return result; | |
| 140 | } | |
| 141 | } | |
| 142 | ||
| 143 | size_t len; | |
| 144 | err = js_get_value_string_utf8(env, argv[0], NULL, 0, &len); | |
| 145 | assert(err == 0); | |
| 146 | ||
| 147 | utf8_t *input = malloc(len); | |
| 148 | err = js_get_value_string_utf8(env, argv[0], input, len, NULL); | |
| 149 | assert(err == 0); | |
| 150 | ||
| 151 | url_t url; | |
| 152 | url_init(&url); | |
| 153 | ||
| 154 | err = url_parse(&url, input, len, has_base ? &base : NULL); | |
| 155 | ||
| 156 | free(input); | |
| 157 | ||
| 158 | url_destroy(&base); | |
| 159 | url_destroy(&url); | |
| 160 | ||
| 161 | js_value_t *result; | |
| 162 | err = js_get_boolean(env, err == 0, &result); | |
| 163 | assert(err == 0); | |
| 164 | ||
| 165 | return result; | |
| 166 | } | |
| 167 | ||
| 168 | static js_value_t * | |
| 169 | bare_url_exports (js_env_t *env, js_value_t *exports) { | |
| 170 | int err; | |
| 171 | ||
| 172 | #define V(name, fn) \ | |
| 173 | { \ | |
| 174 | js_value_t *val; \ | |
| 175 | err = js_create_function(env, name, -1, fn, NULL, &val); \ | |
| 176 | assert(err == 0); \ | |
| 177 | err = js_set_named_property(env, exports, name, val); \ | |
| 178 | assert(err == 0); \ | |
| 179 | } | |
| 180 | ||
| 181 | V("parse", bare_url_parse) | |
| 182 | V("canParse", bare_url_can_parse) | |
| 183 | #undef V | |
| 184 | ||
| 185 | return exports; | |
| 186 | } | |
| 187 | ||
| 188 | BARE_MODULE(bare_url, bare_url_exports) |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require.addon() |
| … | … | @@ -0,0 +1,12 @@ |
| 1 | import * as url from '.' | |
| 2 | ||
| 3 | type URLConstructor = typeof url.URL | |
| 4 | type URLSearchParamsConstructor = typeof url.URLSearchParams | |
| 5 | ||
| 6 | declare global { | |
| 7 | type URL = url.URL | |
| 8 | type URLSearchParams = url.URLSearchParams | |
| 9 | ||
| 10 | const URL: URLConstructor | |
| 11 | const URLSearchParams: URLSearchParamsConstructor | |
| 12 | } |
| … | … | @@ -0,0 +1,2 @@ |
| 1 | global.URL = require('.') | |
| 2 | global.URLSearchParams = require('./lib/url-search-params') |
| … | … | @@ -0,0 +1,41 @@ |
| 1 | import URLError from './lib/errors' | |
| 2 | import URLSearchParams from './lib/url-search-params' | |
| 3 | ||
| 4 | interface URL { | |
| 5 | href: string | |
| 6 | protocol: string | |
| 7 | username: string | |
| 8 | password: string | |
| 9 | host: string | |
| 10 | hostname: string | |
| 11 | port: string | |
| 12 | pathname: string | |
| 13 | search: string | |
| 14 | searchParams: URLSearchParams | |
| 15 | hash: string | |
| 16 | ||
| 17 | toString(): string | |
| 18 | toJSON(): string | |
| 19 | } | |
| 20 | ||
| 21 | declare class URL { | |
| 22 | constructor(input: string, base?: string | URL) | |
| 23 | } | |
| 24 | ||
| 25 | declare namespace URL { | |
| 26 | export function isURL(value: unknown): value is URL | |
| 27 | ||
| 28 | export function isURLSearchParams(value: unknown): value is URLSearchParams | |
| 29 | ||
| 30 | export function parse(input: string, base?: string | URL): URL | null | |
| 31 | ||
| 32 | export function canParse(input: string, base?: string | URL): boolean | |
| 33 | ||
| 34 | export function fileURLToPath(url: URL | string): string | |
| 35 | ||
| 36 | export function pathToFileURL(pathname: string): URL | |
| 37 | ||
| 38 | export { URL, type URLError, URLError as errors, URLSearchParams } | |
| 39 | } | |
| 40 | ||
| 41 | export = URL |
| … | … | @@ -0,0 +1,407 @@ |
| 1 | const path = require('bare-path') | |
| 2 | const binding = require('./binding') | |
| 3 | const errors = require('./lib/errors') | |
| 4 | const URLSearchParams = require('./lib/url-search-params') | |
| 5 | ||
| 6 | const kind = Symbol.for('bare.url.kind') | |
| 7 | ||
| 8 | const isWindows = Bare.platform === 'win32' | |
| 9 | ||
| 10 | class URL { | |
| 11 | static get [kind]() { | |
| 12 | return 0 // Compatibility version | |
| 13 | } | |
| 14 | ||
| 15 | constructor(input, base, opts = {}) { | |
| 16 | if (arguments.length === 0) throw errors.INVALID_URL() | |
| 17 | ||
| 18 | input = String(input) | |
| 19 | ||
| 20 | if (base !== undefined) base = String(base) | |
| 21 | ||
| 22 | this._components = new Uint32Array(8) | |
| 23 | ||
| 24 | this._parse(input, base, opts.throw !== false) | |
| 25 | ||
| 26 | if (this._href) this._params = new URLSearchParams(this.search, this) | |
| 27 | } | |
| 28 | ||
| 29 | get [kind]() { | |
| 30 | return URL[kind] | |
| 31 | } | |
| 32 | ||
| 33 | // https://url.spec.whatwg.org/#dom-url-href | |
| 34 | ||
| 35 | get href() { | |
| 36 | return this._href | |
| 37 | } | |
| 38 | ||
| 39 | set href(value) { | |
| 40 | this._update(value) | |
| 41 | ||
| 42 | this._params._parse(this.search) | |
| 43 | } | |
| 44 | ||
| 45 | // https://url.spec.whatwg.org/#dom-url-protocol | |
| 46 | ||
| 47 | get protocol() { | |
| 48 | return this._slice(0, this._components[0]) + ':' | |
| 49 | } | |
| 50 | ||
| 51 | set protocol(value) { | |
| 52 | this._update(this._replace(value.replace(/:+$/, ''), 0, this._components[0])) | |
| 53 | } | |
| 54 | ||
| 55 | // https://url.spec.whatwg.org/#dom-url-username | |
| 56 | ||
| 57 | get username() { | |
| 58 | return this._slice(this._components[0] + 3 /* :// */, this._components[1]) | |
| 59 | } | |
| 60 | ||
| 61 | set username(value) { | |
| 62 | if (cannotHaveCredentialsOrPort(this)) { | |
| 63 | return | |
| 64 | } | |
| 65 | ||
| 66 | if (this.username === '') value += '@' | |
| 67 | ||
| 68 | this._update(this._replace(value, this._components[0] + 3 /* :// */, this._components[1])) | |
| 69 | } | |
| 70 | ||
| 71 | // https://url.spec.whatwg.org/#dom-url-password | |
| 72 | ||
| 73 | get password() { | |
| 74 | return this._href.slice(this._components[1] + 1 /* : */, this._components[2] - 1 /* @ */) | |
| 75 | } | |
| 76 | ||
| 77 | set password(value) { | |
| 78 | if (cannotHaveCredentialsOrPort(this)) { | |
| 79 | return | |
| 80 | } | |
| 81 | ||
| 82 | let start = this._components[1] + 1 /* : */ | |
| 83 | let end = this._components[2] - 1 /* @ */ | |
| 84 | ||
| 85 | if (this.password === '') { | |
| 86 | value = ':' + value | |
| 87 | start-- | |
| 88 | } | |
| 89 | ||
| 90 | if (this.username === '') { | |
| 91 | value += '@' | |
| 92 | end++ | |
| 93 | } | |
| 94 | ||
| 95 | this._update(this._replace(value, start, end)) | |
| 96 | } | |
| 97 | ||
| 98 | // https://url.spec.whatwg.org/#dom-url-host | |
| 99 | ||
| 100 | get host() { | |
| 101 | return this._slice(this._components[2], this._components[5]) | |
| 102 | } | |
| 103 | ||
| 104 | set host(value) { | |
| 105 | if (hasOpaquePath(this)) { | |
| 106 | return | |
| 107 | } | |
| 108 | ||
| 109 | this._update( | |
| 110 | this._replace(value, this._components[2], this._components[value.includes(':') ? 5 : 3]) | |
| 111 | ) | |
| 112 | } | |
| 113 | ||
| 114 | // https://url.spec.whatwg.org/#dom-url-hostname | |
| 115 | ||
| 116 | get hostname() { | |
| 117 | return this._slice(this._components[2], this._components[3]) | |
| 118 | } | |
| 119 | ||
| 120 | set hostname(value) { | |
| 121 | if (hasOpaquePath(this)) { | |
| 122 | return | |
| 123 | } | |
| 124 | ||
| 125 | this._update(this._replace(value, this._components[2], this._components[3])) | |
| 126 | } | |
| 127 | ||
| 128 | // https://url.spec.whatwg.org/#dom-url-port | |
| 129 | ||
| 130 | get port() { | |
| 131 | return this._slice(this._components[3] + 1 /* : */, this._components[5]) | |
| 132 | } | |
| 133 | ||
| 134 | set port(value) { | |
| 135 | if (cannotHaveCredentialsOrPort(this)) { | |
| 136 | return | |
| 137 | } | |
| 138 | ||
| 139 | let start = this._components[3] + 1 /* : */ | |
| 140 | ||
| 141 | if (this.port === '') { | |
| 142 | value = ':' + value | |
| 143 | start-- | |
| 144 | } | |
| 145 | ||
| 146 | this._update(this._replace(value, start, this._components[5])) | |
| 147 | } | |
| 148 | ||
| 149 | // https://url.spec.whatwg.org/#dom-url-pathname | |
| 150 | ||
| 151 | get pathname() { | |
| 152 | return this._slice(this._components[5], this._components[6] - 1 /* ? */) | |
| 153 | } | |
| 154 | ||
| 155 | set pathname(value) { | |
| 156 | if (hasOpaquePath(this)) { | |
| 157 | return | |
| 158 | } | |
| 159 | ||
| 160 | if (value[0] !== '/' && value[0] !== '\\') { | |
| 161 | value = '/' + value | |
| 162 | } | |
| 163 | ||
| 164 | this._update(this._replace(value, this._components[5], this._components[6] - 1 /* ? */)) | |
| 165 | } | |
| 166 | ||
| 167 | // https://url.spec.whatwg.org/#dom-url-search | |
| 168 | ||
| 169 | get search() { | |
| 170 | return this._slice(this._components[6] - 1 /* ? */, this._components[7] - 1 /* # */) | |
| 171 | } | |
| 172 | ||
| 173 | set search(value) { | |
| 174 | if (value && value[0] !== '?') value = '?' + value | |
| 175 | ||
| 176 | this._update( | |
| 177 | this._replace(value, this._components[6] - 1 /* ? */, this._components[7] - 1 /* # */) | |
| 178 | ) | |
| 179 | ||
| 180 | this._params._parse(this.search) | |
| 181 | } | |
| 182 | ||
| 183 | // https://url.spec.whatwg.org/#dom-url-searchparams | |
| 184 | ||
| 185 | get searchParams() { | |
| 186 | return this._params | |
| 187 | } | |
| 188 | ||
| 189 | // https://url.spec.whatwg.org/#dom-url-hash | |
| 190 | ||
| 191 | get hash() { | |
| 192 | return this._slice(this._components[7] - 1 /* # */) | |
| 193 | } | |
| 194 | ||
| 195 | set hash(value) { | |
| 196 | if (value && value[0] !== '#') value = '#' + value | |
| 197 | ||
| 198 | this._update(this._replace(value, this._components[7] - 1 /* # */)) | |
| 199 | } | |
| 200 | ||
| 201 | toString() { | |
| 202 | return this._href | |
| 203 | } | |
| 204 | ||
| 205 | toJSON() { | |
| 206 | return this._href | |
| 207 | } | |
| 208 | ||
| 209 | [Symbol.for('bare.inspect')]() { | |
| 210 | return { | |
| 211 | __proto__: { constructor: URL }, | |
| 212 | ||
| 213 | href: this.href, | |
| 214 | protocol: this.protocol, | |
| 215 | username: this.username, | |
| 216 | password: this.password, | |
| 217 | host: this.host, | |
| 218 | hostname: this.hostname, | |
| 219 | port: this.port, | |
| 220 | pathname: this.pathname, | |
| 221 | search: this.search, | |
| 222 | searchParams: this.searchParams, | |
| 223 | hash: this.hash | |
| 224 | } | |
| 225 | } | |
| 226 | ||
| 227 | _slice(start, end = this._href.length) { | |
| 228 | return this._href.slice(start, end) | |
| 229 | } | |
| 230 | ||
| 231 | _replace(replacement, start, end = this._href.length) { | |
| 232 | return this._slice(0, start) + replacement + this._slice(end) | |
| 233 | } | |
| 234 | ||
| 235 | _parse(input, base, shouldThrow) { | |
| 236 | try { | |
| 237 | this._href = binding.parse( | |
| 238 | String(input), | |
| 239 | base ? String(base) : null, | |
| 240 | this._components, | |
| 241 | shouldThrow | |
| 242 | ) | |
| 243 | } catch (err) { | |
| 244 | if (err instanceof TypeError) throw err | |
| 245 | ||
| 246 | throw errors.INVALID_URL(`Invalid URL '${input}'`, input) | |
| 247 | } | |
| 248 | } | |
| 249 | ||
| 250 | _update(input) { | |
| 251 | try { | |
| 252 | this._parse(input, null, true) | |
| 253 | } catch (err) { | |
| 254 | if (err instanceof TypeError) throw err | |
| 255 | } | |
| 256 | } | |
| 257 | } | |
| 258 | ||
| 259 | module.exports = exports = URL | |
| 260 | ||
| 261 | // https://url.spec.whatwg.org/#url-opaque-path | |
| 262 | function hasOpaquePath(url) { | |
| 263 | return url.pathname[0] !== '/' | |
| 264 | } | |
| 265 | ||
| 266 | // https://url.spec.whatwg.org/#cannot-have-a-username-password-port | |
| 267 | function cannotHaveCredentialsOrPort(url) { | |
| 268 | return url.hostname === '' || url.protocol === 'file:' | |
| 269 | } | |
| 270 | ||
| 271 | exports.URL = URL | |
| 272 | exports.URLSearchParams = URLSearchParams | |
| 273 | ||
| 274 | exports.errors = errors | |
| 275 | ||
| 276 | exports.isURL = function isURL(value) { | |
| 277 | if (value instanceof URL) return true | |
| 278 | ||
| 279 | return typeof value === 'object' && value !== null && value[kind] === URL[kind] | |
| 280 | } | |
| 281 | ||
| 282 | exports.isURLSearchParams = URLSearchParams.isURLSearchParams | |
| 283 | ||
| 284 | // https://url.spec.whatwg.org/#dom-url-parse | |
| 285 | exports.parse = function parse(input, base) { | |
| 286 | const url = new URL(input, base, { throw: false }) | |
| 287 | return url._href ? url : null | |
| 288 | } | |
| 289 | ||
| 290 | // https://url.spec.whatwg.org/#dom-url-canparse | |
| 291 | exports.canParse = function canParse(input, base) { | |
| 292 | return binding.canParse(String(input), base ? String(base) : null) | |
| 293 | } | |
| 294 | ||
| 295 | exports.fileURLToPath = function fileURLToPath(url) { | |
| 296 | if (typeof url === 'string') { | |
| 297 | url = new URL(url) | |
| 298 | } | |
| 299 | ||
| 300 | if (url.protocol !== 'file:') { | |
| 301 | throw errors.INVALID_URL_SCHEME('The URL must use the file: protocol') | |
| 302 | } | |
| 303 | ||
| 304 | if (isWindows) { | |
| 305 | if (/%2f|%5c/i.test(url.pathname)) { | |
| 306 | throw errors.INVALID_FILE_URL_PATH( | |
| 307 | 'The file: URL path must not include encoded \\ or / characters' | |
| 308 | ) | |
| 309 | } | |
| 310 | } else { | |
| 311 | if (url.hostname) { | |
| 312 | throw errors.INVALID_FILE_URL_HOST("The file: URL host must be 'localhost' or empty") | |
| 313 | } | |
| 314 | ||
| 315 | if (/%2f/i.test(url.pathname)) { | |
| 316 | throw errors.INVALID_FILE_URL_PATH('The file: URL path must not include encoded / characters') | |
| 317 | } | |
| 318 | } | |
| 319 | ||
| 320 | const pathname = path.normalize(decodeURIComponent(url.pathname)) | |
| 321 | ||
| 322 | if (isWindows) { | |
| 323 | if (url.hostname) return '\\\\' + url.hostname + pathname | |
| 324 | ||
| 325 | const letter = pathname.charCodeAt(1) | 0x20 | |
| 326 | ||
| 327 | if (letter < 0x61 /* a */ || letter > 0x7a /* z */ || pathname.charCodeAt(2) !== 0x3a /* : */) { | |
| 328 | throw errors.INVALID_FILE_URL_PATH('The file: URL path must be absolute') | |
| 329 | } | |
| 330 | ||
| 331 | return pathname.slice(1) | |
| 332 | } | |
| 333 | ||
| 334 | return pathname | |
| 335 | } | |
| 336 | ||
| 337 | exports.pathToFileURL = function pathToFileURL(pathname) { | |
| 338 | let resolved = path.resolve(pathname) | |
| 339 | ||
| 340 | if (pathname[pathname.length - 1] === '/') { | |
| 341 | resolved += '/' | |
| 342 | } else if (isWindows && pathname[pathname.length - 1] === '\\') { | |
| 343 | resolved += '\\' | |
| 344 | } | |
| 345 | ||
| 346 | resolved = resolved | |
| 347 | .replaceAll('%', '%25') // Must be first | |
| 348 | .replaceAll('#', '%23') | |
| 349 | .replaceAll('?', '%3f') | |
| 350 | .replaceAll('\n', '%0a') | |
| 351 | .replaceAll('\r', '%0d') | |
| 352 | .replaceAll('\t', '%09') | |
| 353 | ||
| 354 | if (!isWindows) { | |
| 355 | resolved = resolved.replaceAll('\\', '%5c') | |
| 356 | } | |
| 357 | ||
| 358 | return new URL('file:' + resolved) | |
| 359 | } | |
| 360 | ||
| 361 | exports.format = function format(parts) { | |
| 362 | const { protocol, auth, host, hostname, port, pathname, search, query, hash, slashes } = parts | |
| 363 | ||
| 364 | let result = '' | |
| 365 | ||
| 366 | if (typeof protocol === 'string') { | |
| 367 | result += protocol | |
| 368 | ||
| 369 | if (protocol[protocol.length - 1] !== ':') { | |
| 370 | result += ':' | |
| 371 | } | |
| 372 | ||
| 373 | if (slashes === true || /https?|ftp|gopher|file/.test(protocol)) { | |
| 374 | result += '//' | |
| 375 | } | |
| 376 | } | |
| 377 | ||
| 378 | if (typeof auth === 'string') { | |
| 379 | if (host || hostname) result += auth + '@' | |
| 380 | } | |
| 381 | ||
| 382 | if (typeof host === 'string') result += host | |
| 383 | else { | |
| 384 | result += hostname | |
| 385 | ||
| 386 | if (port) result += ':' + port | |
| 387 | } | |
| 388 | ||
| 389 | if (typeof pathname === 'string' && pathname !== '') { | |
| 390 | if (pathname[0] !== '/') result += '/' | |
| 391 | result += pathname | |
| 392 | } | |
| 393 | ||
| 394 | if (typeof search === 'string') { | |
| 395 | if (search[0] !== '?') result += '?' | |
| 396 | result += search | |
| 397 | } else if (typeof query === 'object' && query !== null) { | |
| 398 | result += '?' + new URLSearchParams(query) | |
| 399 | } | |
| 400 | ||
| 401 | if (typeof hash === 'string') { | |
| 402 | if (hash[0] !== '#') result += '#' | |
| 403 | result += hash | |
| 404 | } | |
| 405 | ||
| 406 | return result | |
| 407 | } |
| … | … | @@ -0,0 +1,10 @@ |
| 1 | interface URLError extends Error { | |
| 2 | readonly code: string | |
| 3 | readonly input?: string | |
| 4 | } | |
| 5 | ||
| 6 | declare class URLError extends Error { | |
| 7 | private constructor() | |
| 8 | } | |
| 9 | ||
| 10 | export = URLError |
| … | … | @@ -0,0 +1,33 @@ |
| 1 | module.exports = class URLError extends Error { | |
| 2 | constructor(msg, fn = URLError, code = fn.name) { | |
| 3 | super(`${code}: ${msg}`) | |
| 4 | ||
| 5 | this.code = code | |
| 6 | ||
| 7 | if (Error.captureStackTrace) Error.captureStackTrace(this, fn) | |
| 8 | } | |
| 9 | ||
| 10 | get name() { | |
| 11 | return 'URLError' | |
| 12 | } | |
| 13 | ||
| 14 | static INVALID_URL(msg, input) { | |
| 15 | const err = new URLError(msg, URLError.INVALID_URL) | |
| 16 | ||
| 17 | err.input = input | |
| 18 | ||
| 19 | return err | |
| 20 | } | |
| 21 | ||
| 22 | static INVALID_URL_SCHEME(msg = 'Invalid URL') { | |
| 23 | return new URLError(msg, URLError.INVALID_URL_SCHEME) | |
| 24 | } | |
| 25 | ||
| 26 | static INVALID_FILE_URL_HOST(msg = 'Invalid file: URL host') { | |
| 27 | return new URLError(msg, URLError.INVALID_FILE_URL_HOST) | |
| 28 | } | |
| 29 | ||
| 30 | static INVALID_FILE_URL_PATH(msg = 'Invalid file: URL path') { | |
| 31 | return new URLError(msg, URLError.INVALID_FILE_URL_PATH) | |
| 32 | } | |
| 33 | } |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | interface URLSearchParams extends Iterable<[name: string, value: string]> { | |
| 2 | readonly size: number | |
| 3 | ||
| 4 | append(name: string, value: string): void | |
| 5 | delete(name: string, value?: string): void | |
| 6 | get(name: string): string | undefined | |
| 7 | getAll(name: string): string[] | |
| 8 | has(name: string, value?: string): boolean | |
| 9 | set(name: string, value: string): void | |
| 10 | ||
| 11 | toString(): string | |
| 12 | toJSON(): string | |
| 13 | } | |
| 14 | ||
| 15 | declare class URLSearchParams { | |
| 16 | constructor(init: string | Record<string, string> | Iterable<[string, string]>) | |
| 17 | } | |
| 18 | ||
| 19 | declare namespace URLSearchParams { | |
| 20 | export function isURLSearchParams(value: unknown): value is URLSearchParams | |
| 21 | } | |
| 22 | ||
| 23 | export = URLSearchParams |
| … | … | @@ -0,0 +1,192 @@ |
| 1 | const kind = Symbol.for('bare.url.search-params.kind') | |
| 2 | ||
| 3 | class URLSearchParams { | |
| 4 | static _urls = new WeakMap() | |
| 5 | ||
| 6 | static get [kind]() { | |
| 7 | return 0 // Compatibility version | |
| 8 | } | |
| 9 | ||
| 10 | // https://url.spec.whatwg.org/#dom-urlsearchparams-urlsearchparams | |
| 11 | constructor(init, url = null) { | |
| 12 | this._params = new Map() | |
| 13 | ||
| 14 | if (url) URLSearchParams._urls.set(this, url) | |
| 15 | ||
| 16 | if (typeof init === 'string') { | |
| 17 | this._parse(init) | |
| 18 | } else if (init) { | |
| 19 | for (const [name, value] of typeof init[Symbol.iterator] === 'function' | |
| 20 | ? init | |
| 21 | : Object.entries(init)) { | |
| 22 | this.append(name, value) | |
| 23 | } | |
| 24 | } | |
| 25 | } | |
| 26 | ||
| 27 | get [kind]() { | |
| 28 | return URLSearchParams[kind] | |
| 29 | } | |
| 30 | ||
| 31 | // https://url.spec.whatwg.org/#dom-urlsearchparams-size | |
| 32 | get size() { | |
| 33 | return this._params.length | |
| 34 | } | |
| 35 | ||
| 36 | // https://url.spec.whatwg.org/#dom-urlsearchparams-append | |
| 37 | append(name, value = null) { | |
| 38 | if (value === null) return | |
| 39 | ||
| 40 | let list = this._params.get(name) | |
| 41 | ||
| 42 | if (list === undefined) { | |
| 43 | list = [] | |
| 44 | this._params.set(name, list) | |
| 45 | } | |
| 46 | ||
| 47 | list.push(value) | |
| 48 | ||
| 49 | this._update() | |
| 50 | } | |
| 51 | ||
| 52 | // https://url.spec.whatwg.org/#dom-urlsearchparams-delete | |
| 53 | delete(name, value = null) { | |
| 54 | if (value === null) this._params.delete(name) | |
| 55 | else { | |
| 56 | let list = this._params.get(name) | |
| 57 | ||
| 58 | if (list === undefined) return | |
| 59 | ||
| 60 | list = list.filter((found) => found !== value) | |
| 61 | ||
| 62 | if (list.length === 0) this._params.delete(name) | |
| 63 | else this._params.set(name, list) | |
| 64 | } | |
| 65 | ||
| 66 | this._update() | |
| 67 | } | |
| 68 | ||
| 69 | // https://url.spec.whatwg.org/#dom-urlsearchparams-get | |
| 70 | get(name) { | |
| 71 | const list = this._params.get(name) | |
| 72 | ||
| 73 | if (list === undefined) return null | |
| 74 | ||
| 75 | return list[0] | |
| 76 | } | |
| 77 | ||
| 78 | // https://url.spec.whatwg.org/#dom-urlsearchparams-getall | |
| 79 | getAll(name) { | |
| 80 | const list = this._params.get(name) | |
| 81 | ||
| 82 | if (list === undefined) return [] | |
| 83 | ||
| 84 | return Array.from(list) | |
| 85 | } | |
| 86 | ||
| 87 | // https://url.spec.whatwg.org/#dom-urlsearchparams-has | |
| 88 | has(name, value = null) { | |
| 89 | const list = this._params.get(name) | |
| 90 | ||
| 91 | if (list === undefined) return false | |
| 92 | ||
| 93 | if (value === null) return true | |
| 94 | ||
| 95 | return list.includes(value) | |
| 96 | } | |
| 97 | ||
| 98 | // https://url.spec.whatwg.org/#dom-urlsearchparams-set | |
| 99 | set(name, value = null) { | |
| 100 | if (value === null) this._params.delete(name) | |
| 101 | else this._params.set(name, [value]) | |
| 102 | ||
| 103 | this._update() | |
| 104 | } | |
| 105 | ||
| 106 | toString() { | |
| 107 | return this._serialize() | |
| 108 | } | |
| 109 | ||
| 110 | toJSON() { | |
| 111 | return [...this] | |
| 112 | } | |
| 113 | ||
| 114 | *[Symbol.iterator]() { | |
| 115 | for (const [name, values] of this._params) { | |
| 116 | for (const value of values) yield [name, value] | |
| 117 | } | |
| 118 | } | |
| 119 | ||
| 120 | [Symbol.for('bare.inspect')]() { | |
| 121 | const object = { | |
| 122 | __proto__: { constructor: URLSearchParams } | |
| 123 | } | |
| 124 | ||
| 125 | for (const [name, values] of this._params) { | |
| 126 | if (values.length === 1) object[name] = values[0] | |
| 127 | else object[name] = values | |
| 128 | } | |
| 129 | ||
| 130 | return object | |
| 131 | } | |
| 132 | ||
| 133 | // https://url.spec.whatwg.org/#concept-urlsearchparams-update | |
| 134 | _update() { | |
| 135 | const url = URLSearchParams._urls.get(this) | |
| 136 | ||
| 137 | if (url === undefined) return | |
| 138 | ||
| 139 | url.search = this._serialize() | |
| 140 | } | |
| 141 | ||
| 142 | // https://url.spec.whatwg.org/#concept-urlencoded-parser | |
| 143 | _parse(input) { | |
| 144 | if (input[0] === '?') input = input.substring(1) | |
| 145 | ||
| 146 | this._params = new Map() | |
| 147 | ||
| 148 | for (const sequence of input.split('&')) { | |
| 149 | if (sequence.length === 0) continue | |
| 150 | ||
| 151 | let i = sequence.indexOf('=') | |
| 152 | if (i === -1) i = sequence.length | |
| 153 | ||
| 154 | const name = decodeURIComponent(sequence.substring(0, i)) | |
| 155 | const value = decodeURIComponent(sequence.substring(i + 1, sequence.length)) | |
| 156 | ||
| 157 | let list = this._params.get(name) | |
| 158 | ||
| 159 | if (list === undefined) { | |
| 160 | list = [] | |
| 161 | this._params.set(name, list) | |
| 162 | } | |
| 163 | ||
| 164 | list.push(value) | |
| 165 | } | |
| 166 | } | |
| 167 | ||
| 168 | // https://url.spec.whatwg.org/#concept-urlencoded-serializer | |
| 169 | _serialize() { | |
| 170 | let output = '' | |
| 171 | ||
| 172 | for (let [name, values] of this._params) { | |
| 173 | name = encodeURIComponent(name) | |
| 174 | ||
| 175 | for (const value of values) { | |
| 176 | if (output) output += '&' | |
| 177 | ||
| 178 | output += name + '=' + encodeURIComponent(value) | |
| 179 | } | |
| 180 | } | |
| 181 | ||
| 182 | return output | |
| 183 | } | |
| 184 | } | |
| 185 | ||
| 186 | module.exports = exports = URLSearchParams | |
| 187 | ||
| 188 | exports.isURLSearchParams = function isURLSearchParams(value) { | |
| 189 | if (value instanceof URLSearchParams) return true | |
| 190 | ||
| 191 | return typeof value === 'object' && value !== null && value[kind] === URLSearchParams[kind] | |
| 192 | } |
| … | … | @@ -0,0 +1,51 @@ |
| 1 | { | |
| 2 | "name": "bare-url", | |
| 3 | "version": "2.4.0", | |
| 4 | "description": "WHATWG URL implementation for JavaScript", | |
| 5 | "exports": { | |
| 6 | "./package": "./package.json", | |
| 7 | ".": { | |
| 8 | "types": "./index.d.ts", | |
| 9 | "default": "./index.js" | |
| 10 | }, | |
| 11 | "./global": { | |
| 12 | "types": "./global.d.ts", | |
| 13 | "default": "./global.js" | |
| 14 | } | |
| 15 | }, | |
| 16 | "files": [ | |
| 17 | "index.js", | |
| 18 | "index.d.ts", | |
| 19 | "global.js", | |
| 20 | "global.d.ts", | |
| 21 | "binding.c", | |
| 22 | "binding.js", | |
| 23 | "CMakeLists.txt", | |
| 24 | "lib", | |
| 25 | "prebuilds" | |
| 26 | ], | |
| 27 | "addon": true, | |
| 28 | "scripts": { | |
| 29 | "test": "prettier . --check && bare test.js" | |
| 30 | }, | |
| 31 | "repository": { | |
| 32 | "type": "git", | |
| 33 | "url": "git+https://github.com/holepunchto/bare-url.git" | |
| 34 | }, | |
| 35 | "author": "Holepunch", | |
| 36 | "license": "Apache-2.0", | |
| 37 | "bugs": { | |
| 38 | "url": "https://github.com/holepunchto/bare-url/issues" | |
| 39 | }, | |
| 40 | "homepage": "https://github.com/holepunchto/bare-url", | |
| 41 | "dependencies": { | |
| 42 | "bare-path": "^3.0.0" | |
| 43 | }, | |
| 44 | "devDependencies": { | |
| 45 | "brittle": "^3.3.2", | |
| 46 | "cmake-bare": "^1.1.6", | |
| 47 | "cmake-fetch": "^1.0.0", | |
| 48 | "prettier": "^3.3.3", | |
| 49 | "prettier-config-holepunch": "^2.0.0" | |
| 50 | } | |
| 51 | } |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | name: Build Status | |
| 2 | on: | |
| 3 | push: | |
| 4 | branches: | |
| 5 | - master | |
| 6 | pull_request: | |
| 7 | branches: | |
| 8 | - master | |
| 9 | jobs: | |
| 10 | build: | |
| 11 | strategy: | |
| 12 | matrix: | |
| 13 | node-version: [lts/*] | |
| 14 | os: [ubuntu-latest, macos-latest, windows-latest] | |
| 15 | runs-on: ${{ matrix.os }} | |
| 16 | steps: | |
| 17 | - uses: actions/checkout@v2 | |
| 18 | - name: Use Node.js ${{ matrix.node-version }} | |
| 19 | uses: actions/setup-node@v2 | |
| 20 | with: | |
| 21 | node-version: ${{ matrix.node-version }} | |
| 22 | - run: npm install | |
| 23 | - run: npm test |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2020 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,43 @@ |
| 1 | # big-sparse-array | |
| 2 | ||
| 3 | A sparse array optimised for low memory whilst still being fast. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install big-sparse-array | |
| 7 | ``` | |
| 8 | ||
| 9 | Uses a shallow tree structure with a branching factor of 4096 | |
| 10 | to index a series of small arrays that try to compress as much as possible | |
| 11 | to reduce the memory overhead needed. | |
| 12 | ||
| 13 | Similar to a Map, except it is faster, but might use a bit more memory, ymmv. | |
| 14 | ||
| 15 | ## Usage | |
| 16 | ||
| 17 | ``` js | |
| 18 | const BigSparseArray = require('big-sparse-array') | |
| 19 | ||
| 20 | const b = new BigSparseArray() | |
| 21 | ||
| 22 | b.set(42422242525, true) | |
| 23 | b.get(42422242525) // returns true | |
| 24 | b.get(111111111) // returns undefined | |
| 25 | ``` | |
| 26 | ||
| 27 | ## API | |
| 28 | ||
| 29 | #### `const b = new BigSparseArray()` | |
| 30 | ||
| 31 | Make a new sparse array. | |
| 32 | ||
| 33 | #### `b.set(index, value)` | |
| 34 | ||
| 35 | Insert a new value at an index. `index` must be a integer. | |
| 36 | ||
| 37 | #### `value = b.get(index)` | |
| 38 | ||
| 39 | Get a value out. Returns `undefined` if the value could not be found. | |
| 40 | ||
| 41 | ## License | |
| 42 | ||
| 43 | MIT |
| … | … | @@ -0,0 +1,124 @@ |
| 1 | const FACTOR = new Uint16Array(8) | |
| 2 | ||
| 3 | function factor4096 (i, n) { | |
| 4 | while (n > 0) { | |
| 5 | const f = i & 4095 | |
| 6 | FACTOR[--n] = f | |
| 7 | i = (i - f) / 4096 | |
| 8 | } | |
| 9 | return FACTOR | |
| 10 | } | |
| 11 | ||
| 12 | module.exports = class BigSparseArray { | |
| 13 | constructor () { | |
| 14 | this.tiny = new TinyArray() | |
| 15 | this.maxLength = 4096 | |
| 16 | this.factor = 1 | |
| 17 | } | |
| 18 | ||
| 19 | set (index, val) { | |
| 20 | if (val !== undefined) { | |
| 21 | while (index >= this.maxLength) { | |
| 22 | this.maxLength *= 4096 | |
| 23 | this.factor++ | |
| 24 | if (!this.tiny.isEmptyish()) { | |
| 25 | const t = new TinyArray() | |
| 26 | t.set(0, this.tiny) | |
| 27 | this.tiny = t | |
| 28 | } | |
| 29 | } | |
| 30 | } | |
| 31 | ||
| 32 | const f = factor4096(index, this.factor) | |
| 33 | const last = this.factor - 1 | |
| 34 | ||
| 35 | let tiny = this.tiny | |
| 36 | for (let i = 0; i < last; i++) { | |
| 37 | const next = tiny.get(f[i]) | |
| 38 | if (next === undefined) { | |
| 39 | if (val === undefined) return | |
| 40 | tiny = tiny.set(f[i], new TinyArray()) | |
| 41 | } else { | |
| 42 | tiny = next | |
| 43 | } | |
| 44 | } | |
| 45 | ||
| 46 | return tiny.set(f[last], val) | |
| 47 | } | |
| 48 | ||
| 49 | get (index) { | |
| 50 | if (index >= this.maxLength) return | |
| 51 | ||
| 52 | const f = factor4096(index, this.factor) | |
| 53 | const last = this.factor - 1 | |
| 54 | ||
| 55 | let tiny = this.tiny | |
| 56 | for (let i = 0; i < last; i++) { | |
| 57 | tiny = tiny.get(f[i]) | |
| 58 | if (tiny === undefined) return | |
| 59 | } | |
| 60 | ||
| 61 | return tiny.get(f[last]) | |
| 62 | } | |
| 63 | } | |
| 64 | ||
| 65 | class TinyArray { | |
| 66 | constructor () { | |
| 67 | this.s = 0 | |
| 68 | this.b = new Array(1) | |
| 69 | this.f = new Uint16Array(1) | |
| 70 | } | |
| 71 | ||
| 72 | isEmptyish () { | |
| 73 | return this.b.length === 1 && this.b[0] === undefined | |
| 74 | } | |
| 75 | ||
| 76 | get (i) { | |
| 77 | if (this.s === 12) return this.b[i] | |
| 78 | const f = i >>> this.s | |
| 79 | const r = i & (this.b.length - 1) | |
| 80 | return this.f[r] === f ? this.b[r] : undefined | |
| 81 | } | |
| 82 | ||
| 83 | set (i, v) { | |
| 84 | while (this.s !== 12) { | |
| 85 | const f = i >>> this.s | |
| 86 | const r = i & (this.b.length - 1) | |
| 87 | const o = this.b[r] | |
| 88 | ||
| 89 | if (o === undefined || f === this.f[r]) { | |
| 90 | this.b[r] = v | |
| 91 | this.f[r] = f | |
| 92 | return v | |
| 93 | } | |
| 94 | ||
| 95 | this.grow() | |
| 96 | } | |
| 97 | ||
| 98 | this.b[i] = v | |
| 99 | return v | |
| 100 | } | |
| 101 | ||
| 102 | grow () { | |
| 103 | const os = this.s | |
| 104 | const ob = this.b | |
| 105 | const of = this.f | |
| 106 | ||
| 107 | this.s += 4 | |
| 108 | this.b = new Array(this.b.length << 4) | |
| 109 | this.f = this.s === 12 ? null : new Uint8Array(this.b.length) | |
| 110 | ||
| 111 | const m = this.b.length - 1 | |
| 112 | ||
| 113 | for (let or = 0; or < ob.length; or++) { | |
| 114 | if (ob[or] === undefined) continue | |
| 115 | ||
| 116 | const i = of[or] << os | or | |
| 117 | const f = i >>> this.s | |
| 118 | const r = i & m | |
| 119 | ||
| 120 | this.b[r] = ob[or] | |
| 121 | if (this.s !== 12) this.f[r] = f | |
| 122 | } | |
| 123 | } | |
| 124 | } |
| … | … | @@ -0,0 +1,24 @@ |
| 1 | { | |
| 2 | "name": "big-sparse-array", | |
| 3 | "version": "1.0.3", | |
| 4 | "description": "A sparse array optimised for low memory whilst still being fast", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": {}, | |
| 7 | "devDependencies": { | |
| 8 | "brittle": "^3.1.1", | |
| 9 | "standard": "^16.0.3" | |
| 10 | }, | |
| 11 | "scripts": { | |
| 12 | "test": "standard && brittle test.js" | |
| 13 | }, | |
| 14 | "repository": { | |
| 15 | "type": "git", | |
| 16 | "url": "https://github.com/mafintosh/big-sparse-array.git" | |
| 17 | }, | |
| 18 | "author": "Mathias Buus (@mafintosh)", | |
| 19 | "license": "MIT", | |
| 20 | "bugs": { | |
| 21 | "url": "https://github.com/mafintosh/big-sparse-array/issues" | |
| 22 | }, | |
| 23 | "homepage": "https://github.com/mafintosh/big-sparse-array" | |
| 24 | } |
| … | … | @@ -0,0 +1,41 @@ |
| 1 | const test = require('brittle') | |
| 2 | const BigSparseArray = require('./') | |
| 3 | ||
| 4 | test('basic', function (t) { | |
| 5 | const b = new BigSparseArray() | |
| 6 | ||
| 7 | b.set(42, true) | |
| 8 | t.is(b.get(42), true) | |
| 9 | ||
| 10 | b.set(42, 42) | |
| 11 | t.is(b.get(42), 42) | |
| 12 | ||
| 13 | b.set(42424242424242, 'big') | |
| 14 | t.is(b.get(42424242424242), 'big') | |
| 15 | t.is(b.get(42424242424243), undefined) | |
| 16 | t.is(b.get(42), 42) | |
| 17 | }) | |
| 18 | ||
| 19 | test('grow', function (t) { | |
| 20 | const b = new BigSparseArray() | |
| 21 | ||
| 22 | for (let i = 0; i < 10000; i++) { | |
| 23 | b.set(i, i) | |
| 24 | } | |
| 25 | ||
| 26 | let missing = 10000 | |
| 27 | ||
| 28 | for (let i = 0; i < 10000; i++) { | |
| 29 | if (b.get(i) === i) missing-- | |
| 30 | } | |
| 31 | ||
| 32 | t.is(missing, 0) | |
| 33 | }) | |
| 34 | ||
| 35 | test('bounds', function (t) { | |
| 36 | const b = new BigSparseArray() | |
| 37 | ||
| 38 | b.set(0, true) | |
| 39 | ||
| 40 | t.is(b.get(4096), undefined) | |
| 41 | }) |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | name: Build Status | |
| 2 | on: | |
| 3 | push: | |
| 4 | branches: | |
| 5 | - master | |
| 6 | pull_request: | |
| 7 | branches: | |
| 8 | - master | |
| 9 | jobs: | |
| 10 | build: | |
| 11 | strategy: | |
| 12 | matrix: | |
| 13 | node-version: [lts/*] | |
| 14 | os: [ubuntu-latest, macos-latest, windows-latest] | |
| 15 | runs-on: ${{ matrix.os }} | |
| 16 | steps: | |
| 17 | - uses: actions/checkout@v2 | |
| 18 | - name: Use Node.js ${{ matrix.node-version }} | |
| 19 | uses: actions/setup-node@v2 | |
| 20 | with: | |
| 21 | node-version: ${{ matrix.node-version }} | |
| 22 | - run: npm install | |
| 23 | - run: npm test |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2022 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,22 @@ |
| 1 | # binary-stream-equals | |
| 2 | ||
| 3 | Check if two binary streams have the same content | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install binary-stream-equals | |
| 7 | ``` | |
| 8 | ||
| 9 | (Only works with [streamx](https://github.com/streamxorg/streamx) and the latest versions of Node's readable-stream) | |
| 10 | ||
| 11 | ## Usage | |
| 12 | ||
| 13 | ``` js | |
| 14 | const streamEquals = require('binary-stream-equals') | |
| 15 | ||
| 16 | // Note that streamEquals consumes and destroys the streams when done | |
| 17 | const eq = await streamEquals(binaryStreamA, binaryStreamB) | |
| 18 | ``` | |
| 19 | ||
| 20 | ## License | |
| 21 | ||
| 22 | MIT |
| … | … | @@ -0,0 +1,101 @@ |
| 1 | const b4a = require('b4a') | |
| 2 | ||
| 3 | module.exports = function (a, b) { | |
| 4 | return new Promise((resolve, reject) => binaryEquals(a, b, resolve, reject)) | |
| 5 | } | |
| 6 | ||
| 7 | function binaryEquals (a, b, resolve, reject) { | |
| 8 | let aBuf = null | |
| 9 | let aEnded = false | |
| 10 | ||
| 11 | let bBuf = null | |
| 12 | let bEnded = false | |
| 13 | ||
| 14 | let closed = 0 | |
| 15 | let done = false | |
| 16 | let error = null | |
| 17 | let equals = false | |
| 18 | ||
| 19 | a.on('readable', tick) | |
| 20 | a.on('end', onend) | |
| 21 | a.on('error', onerror) | |
| 22 | a.on('close', onclose) | |
| 23 | ||
| 24 | b.on('readable', tick) | |
| 25 | b.on('end', onend) | |
| 26 | b.on('error', onerror) | |
| 27 | b.on('close', onclose) | |
| 28 | ||
| 29 | function onerror (err) { | |
| 30 | error = err | |
| 31 | a.destroy() | |
| 32 | b.destroy() | |
| 33 | } | |
| 34 | ||
| 35 | function onclose () { | |
| 36 | if (++closed !== 2) return | |
| 37 | if (error !== null && done === false) reject(error) | |
| 38 | else resolve(equals) | |
| 39 | } | |
| 40 | ||
| 41 | function ondone (eq) { | |
| 42 | if (done) return | |
| 43 | done = true | |
| 44 | ||
| 45 | equals = eq | |
| 46 | ||
| 47 | a.destroy() | |
| 48 | b.destroy() | |
| 49 | } | |
| 50 | ||
| 51 | function onend () { | |
| 52 | if (this === a) aEnded = true | |
| 53 | else bEnded = true | |
| 54 | tick() | |
| 55 | } | |
| 56 | ||
| 57 | function tick () { | |
| 58 | while (done === false) { | |
| 59 | if (aBuf === null) aBuf = a.read() | |
| 60 | if (bBuf === null) bBuf = b.read() | |
| 61 | ||
| 62 | if (aBuf === null && bBuf === null && aEnded && bEnded) { | |
| 63 | ondone(true) | |
| 64 | return | |
| 65 | } | |
| 66 | ||
| 67 | if (aBuf !== null && (bBuf === null && bEnded)) { | |
| 68 | ondone(false) | |
| 69 | return | |
| 70 | } | |
| 71 | ||
| 72 | if (bBuf !== null && (aBuf === null && aEnded)) { | |
| 73 | ondone(false) | |
| 74 | return | |
| 75 | } | |
| 76 | ||
| 77 | if (aBuf === null || bBuf === null) return // read pending | |
| 78 | ||
| 79 | if (aBuf.byteLength === bBuf.byteLength) { | |
| 80 | if (b4a.equals(aBuf, bBuf)) { | |
| 81 | aBuf = bBuf = null | |
| 82 | continue | |
| 83 | } | |
| 84 | ||
| 85 | ondone(false) | |
| 86 | return | |
| 87 | } | |
| 88 | ||
| 89 | const min = Math.min(aBuf.byteLength, bBuf.byteLength) | |
| 90 | ||
| 91 | if (b4a.equals(aBuf.subarray(0, min), bBuf.subarray(0, min))) { | |
| 92 | aBuf = aBuf.byteLength === min ? null : aBuf.subarray(min) | |
| 93 | bBuf = bBuf.byteLength === min ? null : bBuf.subarray(min) | |
| 94 | continue | |
| 95 | } | |
| 96 | ||
| 97 | ondone(false) | |
| 98 | return | |
| 99 | } | |
| 100 | } | |
| 101 | } |
| … | … | @@ -0,0 +1,27 @@ |
| 1 | { | |
| 2 | "name": "binary-stream-equals", | |
| 3 | "version": "1.0.0", | |
| 4 | "description": "Check if two binary streams have the same content", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "b4a": "^1.3.1" | |
| 8 | }, | |
| 9 | "devDependencies": { | |
| 10 | "brittle": "^2.2.7", | |
| 11 | "standard": "^16.0.4", | |
| 12 | "streamx": "^2.12.4" | |
| 13 | }, | |
| 14 | "scripts": { | |
| 15 | "test": "standard && brittle test.js" | |
| 16 | }, | |
| 17 | "repository": { | |
| 18 | "type": "git", | |
| 19 | "url": "https://github.com/mafintosh/binary-stream-equals.git" | |
| 20 | }, | |
| 21 | "author": "Mathias Buus (@mafintosh)", | |
| 22 | "license": "MIT", | |
| 23 | "bugs": { | |
| 24 | "url": "https://github.com/mafintosh/binary-stream-equals/issues" | |
| 25 | }, | |
| 26 | "homepage": "https://github.com/mafintosh/binary-stream-equals" | |
| 27 | } |
| … | … | @@ -0,0 +1,164 @@ |
| 1 | const test = require('brittle') | |
| 2 | const { Readable } = require('streamx') | |
| 3 | const streamEquals = require('./') | |
| 4 | ||
| 5 | test('two exactly equal streams', async function (t) { | |
| 6 | const a = Readable.from([ | |
| 7 | Buffer.from('abe'), | |
| 8 | Buffer.from('fest'), | |
| 9 | Buffer.from('er'), | |
| 10 | Buffer.from('sjov') | |
| 11 | ]) | |
| 12 | ||
| 13 | const b = Readable.from([ | |
| 14 | Buffer.from('abe'), | |
| 15 | Buffer.from('fest'), | |
| 16 | Buffer.from('er'), | |
| 17 | Buffer.from('sjov') | |
| 18 | ]) | |
| 19 | ||
| 20 | const eq = await streamEquals(a, b) | |
| 21 | ||
| 22 | t.is(eq, true) | |
| 23 | }) | |
| 24 | ||
| 25 | test('two exactly unequal streams', async function (t) { | |
| 26 | const a = Readable.from([ | |
| 27 | Buffer.from('abe'), | |
| 28 | Buffer.from('fest'), | |
| 29 | Buffer.from('er'), | |
| 30 | Buffer.from('sjov') | |
| 31 | ]) | |
| 32 | ||
| 33 | const b = Readable.from([ | |
| 34 | Buffer.from('abe'), | |
| 35 | Buffer.from('fest'), | |
| 36 | Buffer.from('er'), | |
| 37 | Buffer.from('sjow') | |
| 38 | ]) | |
| 39 | ||
| 40 | const eq = await streamEquals(a, b) | |
| 41 | ||
| 42 | t.is(eq, false) | |
| 43 | }) | |
| 44 | ||
| 45 | test('first block is different', async function (t) { | |
| 46 | const a = Readable.from([ | |
| 47 | Buffer.from('abe'), | |
| 48 | Buffer.from('fest'), | |
| 49 | Buffer.from('er'), | |
| 50 | Buffer.from('sjov') | |
| 51 | ]) | |
| 52 | ||
| 53 | const b = Readable.from([ | |
| 54 | Buffer.from('xbe'), | |
| 55 | Buffer.from('fest'), | |
| 56 | Buffer.from('er'), | |
| 57 | Buffer.from('sjov') | |
| 58 | ]) | |
| 59 | ||
| 60 | const eq = await streamEquals(a, b) | |
| 61 | ||
| 62 | t.is(eq, false) | |
| 63 | }) | |
| 64 | ||
| 65 | test('two equal streams', async function (t) { | |
| 66 | const a = Readable.from([ | |
| 67 | Buffer.from('abe'), | |
| 68 | Buffer.from('fest'), | |
| 69 | Buffer.from('er'), | |
| 70 | Buffer.from('sjov') | |
| 71 | ]) | |
| 72 | ||
| 73 | const b = Readable.from([ | |
| 74 | Buffer.from('abef'), | |
| 75 | Buffer.from('es'), | |
| 76 | Buffer.from('t'), | |
| 77 | Buffer.from('e'), | |
| 78 | Buffer.from('rsjov') | |
| 79 | ]) | |
| 80 | ||
| 81 | const eq = await streamEquals(a, b) | |
| 82 | ||
| 83 | t.is(eq, true) | |
| 84 | }) | |
| 85 | ||
| 86 | test('two unequal streams', async function (t) { | |
| 87 | const a = Readable.from([ | |
| 88 | Buffer.from('abe'), | |
| 89 | Buffer.from('fest'), | |
| 90 | Buffer.from('er'), | |
| 91 | Buffer.from('sjov') | |
| 92 | ]) | |
| 93 | ||
| 94 | const b = Readable.from([ | |
| 95 | Buffer.from('abef'), | |
| 96 | Buffer.from('es'), | |
| 97 | Buffer.from('t'), | |
| 98 | Buffer.from('e'), | |
| 99 | Buffer.from('rsjow') | |
| 100 | ]) | |
| 101 | ||
| 102 | const eq = await streamEquals(a, b) | |
| 103 | ||
| 104 | t.is(eq, false) | |
| 105 | }) | |
| 106 | ||
| 107 | test('one stream shorter than the other', async function (t) { | |
| 108 | const a = Readable.from([ | |
| 109 | Buffer.from('abe'), | |
| 110 | Buffer.from('fest'), | |
| 111 | Buffer.from('er'), | |
| 112 | Buffer.from('sjov') | |
| 113 | ]) | |
| 114 | ||
| 115 | const b = Readable.from([ | |
| 116 | Buffer.from('abe'), | |
| 117 | Buffer.from('fest'), | |
| 118 | Buffer.from('er') | |
| 119 | ]) | |
| 120 | ||
| 121 | const eq = await streamEquals(a, b) | |
| 122 | ||
| 123 | t.is(eq, false) | |
| 124 | }) | |
| 125 | ||
| 126 | test('second stream is stream shorter than the other', async function (t) { | |
| 127 | const a = Readable.from([ | |
| 128 | Buffer.from('abe'), | |
| 129 | Buffer.from('fest'), | |
| 130 | Buffer.from('er') | |
| 131 | ]) | |
| 132 | ||
| 133 | const b = Readable.from([ | |
| 134 | Buffer.from('abe'), | |
| 135 | Buffer.from('fest'), | |
| 136 | Buffer.from('er'), | |
| 137 | Buffer.from('sjov') | |
| 138 | ]) | |
| 139 | ||
| 140 | const eq = await streamEquals(a, b) | |
| 141 | ||
| 142 | t.is(eq, false) | |
| 143 | }) | |
| 144 | ||
| 145 | test('one fails', async function (t) { | |
| 146 | const a = Readable.from([ | |
| 147 | Buffer.from('abe'), | |
| 148 | Buffer.from('fest'), | |
| 149 | Buffer.from('er'), | |
| 150 | Buffer.from('sjov') | |
| 151 | ]) | |
| 152 | ||
| 153 | const b = Readable.from([ | |
| 154 | Buffer.from('abef'), | |
| 155 | Buffer.from('es'), | |
| 156 | Buffer.from('t'), | |
| 157 | Buffer.from('e'), | |
| 158 | Buffer.from('rsjov') | |
| 159 | ]) | |
| 160 | ||
| 161 | b.destroy(new Error('stop')) | |
| 162 | ||
| 163 | t.exception(streamEquals(a, b)) | |
| 164 | }) |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | name: Build Status | |
| 2 | on: | |
| 3 | push: | |
| 4 | branches: | |
| 5 | - master | |
| 6 | pull_request: | |
| 7 | branches: | |
| 8 | - master | |
| 9 | jobs: | |
| 10 | build: | |
| 11 | strategy: | |
| 12 | matrix: | |
| 13 | node-version: [lts/*] | |
| 14 | os: [ubuntu-latest, macos-latest, windows-latest] | |
| 15 | runs-on: ${{ matrix.os }} | |
| 16 | steps: | |
| 17 | - uses: actions/checkout@v2 | |
| 18 | - name: Use Node.js ${{ matrix.node-version }} | |
| 19 | uses: actions/setup-node@v2 | |
| 20 | with: | |
| 21 | node-version: ${{ matrix.node-version }} | |
| 22 | - run: npm install | |
| 23 | - run: npm test |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2016 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,56 @@ |
| 1 | # codecs | |
| 2 | ||
| 3 | Create an binary encoder/decoder for Node's build in types like, json, utf-8, hex. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install codecs | |
| 7 | ``` | |
| 8 | ||
| 9 | Useful to support value encodings similar to leveldb's. | |
| 10 | ||
| 11 | ## Usage | |
| 12 | ||
| 13 | ``` js | |
| 14 | const codecs = require('codecs') | |
| 15 | const json = codecs('json') | |
| 16 | ||
| 17 | console.log(json.encode({ hello: 'world' })) // Buffer.from('{"hello":"world"}') | |
| 18 | console.log(json.decode(new Buffer('{"hello":"world"}'))) // { hello: 'world' } | |
| 19 | ``` | |
| 20 | ||
| 21 | ## API | |
| 22 | ||
| 23 | #### `const codec = codecs(type, [fallback])` | |
| 24 | ||
| 25 | Create a new codec. | |
| 26 | ||
| 27 | Supported types are | |
| 28 | ||
| 29 | * utf8 | |
| 30 | * json | |
| 31 | * [ndjson](http://ndjson.org/) | |
| 32 | * binary | |
| 33 | * hex | |
| 34 | * ascii | |
| 35 | * base64 | |
| 36 | * ucs2 | |
| 37 | * ucs-2 | |
| 38 | * utf16le | |
| 39 | * utf-16le | |
| 40 | * binary | |
| 41 | ||
| 42 | If an unknown type is passed-in, the `fallback` is used if given, else `binary`. | |
| 43 | If you want to use a custom codec you can pass in an object containing a an `encode` and `decode` method and that will be returned. | |
| 44 | If you pass a [compact encoder](https://github.com/compact-encoding/compact-encoding), then it will be converted into a codec for you and returned. | |
| 45 | ||
| 46 | #### `const buf = codec.encode(value)` | |
| 47 | ||
| 48 | Encode a value to a buffer. | |
| 49 | ||
| 50 | #### `const value = codec.decode(buf)` | |
| 51 | ||
| 52 | Decode a buffer to a value. | |
| 53 | ||
| 54 | ## License | |
| 55 | ||
| 56 | MIT |
| … | … | @@ -0,0 +1,96 @@ |
| 1 | const b4a = require('b4a') | |
| 2 | ||
| 3 | module.exports = codecs | |
| 4 | ||
| 5 | codecs.ascii = createString('ascii') | |
| 6 | codecs.utf8 = createString('utf-8') | |
| 7 | codecs.hex = createString('hex') | |
| 8 | codecs.base64 = createString('base64') | |
| 9 | codecs.ucs2 = createString('ucs2') | |
| 10 | codecs.utf16le = createString('utf16le') | |
| 11 | codecs.ndjson = createJSON(true) | |
| 12 | codecs.json = createJSON(false) | |
| 13 | codecs.binary = { | |
| 14 | name: 'binary', | |
| 15 | encode: function encodeBinary (obj) { | |
| 16 | return typeof obj === 'string' | |
| 17 | ? b4a.from(obj, 'utf-8') | |
| 18 | : b4a.toBuffer(obj) | |
| 19 | }, | |
| 20 | decode: function decodeBinary (buf) { | |
| 21 | return b4a.toBuffer(buf) | |
| 22 | } | |
| 23 | } | |
| 24 | ||
| 25 | function isCompactEncoding (c) { | |
| 26 | return !!(c.encode && c.decode && c.preencode) | |
| 27 | } | |
| 28 | ||
| 29 | function fromCompactEncoding (c) { | |
| 30 | return { | |
| 31 | name: 'compact-encoding', | |
| 32 | encode: function encodeWithCompact (value) { | |
| 33 | const state = { start: 0, end: 0, buffer: null, cache: null } | |
| 34 | c.preencode(state, value) | |
| 35 | state.buffer = b4a.allocUnsafe(state.end) | |
| 36 | c.encode(state, value) | |
| 37 | return state.buffer | |
| 38 | }, | |
| 39 | decode: function decodeWithCompact (buffer) { | |
| 40 | return c.decode({ start: 0, end: buffer.byteLength, buffer, cache: null }) | |
| 41 | } | |
| 42 | } | |
| 43 | } | |
| 44 | ||
| 45 | function codecs (fmt, fallback) { | |
| 46 | if (typeof fmt === 'object' && fmt) { | |
| 47 | return isCompactEncoding(fmt) ? fromCompactEncoding(fmt) : fmt | |
| 48 | } | |
| 49 | ||
| 50 | switch (fmt) { | |
| 51 | case 'ndjson': return codecs.ndjson | |
| 52 | case 'json': return codecs.json | |
| 53 | case 'ascii': return codecs.ascii | |
| 54 | case 'utf-8': | |
| 55 | case 'utf8': return codecs.utf8 | |
| 56 | case 'hex': return codecs.hex | |
| 57 | case 'base64': return codecs.base64 | |
| 58 | case 'ucs-2': | |
| 59 | case 'ucs2': return codecs.ucs2 | |
| 60 | case 'utf16-le': | |
| 61 | case 'utf16le': return codecs.utf16le | |
| 62 | } | |
| 63 | ||
| 64 | return fallback !== undefined ? fallback : codecs.binary | |
| 65 | } | |
| 66 | ||
| 67 | function createJSON (newline) { | |
| 68 | return { | |
| 69 | name: newline ? 'ndjson' : 'json', | |
| 70 | encode: newline ? encodeNDJSON : encodeJSON, | |
| 71 | decode: function decodeJSON (buf) { | |
| 72 | return JSON.parse(b4a.toString(buf)) | |
| 73 | } | |
| 74 | } | |
| 75 | ||
| 76 | function encodeJSON (val) { | |
| 77 | return b4a.from(JSON.stringify(val)) | |
| 78 | } | |
| 79 | ||
| 80 | function encodeNDJSON (val) { | |
| 81 | return b4a.from(JSON.stringify(val) + '\n') | |
| 82 | } | |
| 83 | } | |
| 84 | ||
| 85 | function createString (type) { | |
| 86 | return { | |
| 87 | name: type, | |
| 88 | encode: function encodeString (val) { | |
| 89 | if (typeof val !== 'string') val = val.toString() | |
| 90 | return b4a.from(val, type) | |
| 91 | }, | |
| 92 | decode: function decodeString (buf) { | |
| 93 | return b4a.toString(buf, type) | |
| 94 | } | |
| 95 | } | |
| 96 | } |
| … | … | @@ -0,0 +1,27 @@ |
| 1 | { | |
| 2 | "name": "codecs", | |
| 3 | "version": "3.1.0", | |
| 4 | "description": "Create an binary encoder/decoder for json, utf-8 or custom types", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "b4a": "^1.6.3" | |
| 8 | }, | |
| 9 | "devDependencies": { | |
| 10 | "brittle": "^3.2.1", | |
| 11 | "compact-encoding": "^2.11.0", | |
| 12 | "standard": "^17.0.0" | |
| 13 | }, | |
| 14 | "scripts": { | |
| 15 | "test": "standard && brittle test.js" | |
| 16 | }, | |
| 17 | "repository": { | |
| 18 | "type": "git", | |
| 19 | "url": "https://github.com/mafintosh/codecs.git" | |
| 20 | }, | |
| 21 | "author": "Mathias Buus (@mafintosh)", | |
| 22 | "license": "MIT", | |
| 23 | "bugs": { | |
| 24 | "url": "https://github.com/mafintosh/codecs/issues" | |
| 25 | }, | |
| 26 | "homepage": "https://github.com/mafintosh/codecs" | |
| 27 | } |
| … | … | @@ -0,0 +1,75 @@ |
| 1 | const test = require('brittle') | |
| 2 | const c = require('compact-encoding') | |
| 3 | const codecs = require('./') | |
| 4 | ||
| 5 | test('json', function (t) { | |
| 6 | const enc = codecs('json') | |
| 7 | t.alike(enc.name, 'json') | |
| 8 | t.alike(enc.encode({}), Buffer.from('{}')) | |
| 9 | t.alike(enc.decode(Buffer.from('{}')), {}) | |
| 10 | }) | |
| 11 | ||
| 12 | test('utf-8', function (t) { | |
| 13 | const enc = codecs('utf-8') | |
| 14 | t.alike(enc.name, 'utf-8') | |
| 15 | t.alike(enc.encode('hello world'), Buffer.from('hello world')) | |
| 16 | t.alike(enc.decode(Buffer.from('hello world')), 'hello world') | |
| 17 | }) | |
| 18 | ||
| 19 | test('hex', function (t) { | |
| 20 | const enc = codecs('hex') | |
| 21 | t.alike(enc.name, 'hex') | |
| 22 | t.alike(enc.encode('abcd'), Buffer.from([0xab, 0xcd])) | |
| 23 | t.alike(enc.decode(Buffer.from([0xab, 0xcd])), 'abcd') | |
| 24 | }) | |
| 25 | ||
| 26 | test('binary', function (t) { | |
| 27 | const enc = codecs() | |
| 28 | t.alike(enc.name, 'binary') | |
| 29 | const input = Buffer.from('hello world') | |
| 30 | t.alike(enc.encode('hello world'), input) | |
| 31 | t.alike(enc.encode(input), input) | |
| 32 | t.alike(enc.decode(input), input) | |
| 33 | const uint8 = new Uint8Array(input.buffer, input.byteOffset, input.byteLength) | |
| 34 | t.ok(Buffer.isBuffer(enc.encode(uint8))) | |
| 35 | t.alike(enc.encode(uint8).compare(input), 0) | |
| 36 | t.ok(Buffer.isBuffer(enc.decode(uint8))) | |
| 37 | t.alike(enc.decode(uint8).compare(input), 0) | |
| 38 | }) | |
| 39 | ||
| 40 | test('custom', function (t) { | |
| 41 | const enc = codecs({ | |
| 42 | name: 'custom', | |
| 43 | encode: function () { | |
| 44 | return Buffer.from('lol') | |
| 45 | }, | |
| 46 | decode: function () { | |
| 47 | return 42 | |
| 48 | } | |
| 49 | }) | |
| 50 | ||
| 51 | t.alike(enc.name, 'custom') | |
| 52 | t.alike(enc.encode('hello'), Buffer.from('lol')) | |
| 53 | t.alike(enc.encode(42), Buffer.from('lol')) | |
| 54 | t.alike(enc.decode(Buffer.from('lol')), 42) | |
| 55 | }) | |
| 56 | ||
| 57 | test('uint8arrays in binary', function (t) { | |
| 58 | const enc = codecs('binary') | |
| 59 | const buf = enc.encode(new Uint8Array([1, 2, 3])) | |
| 60 | t.alike(buf, Buffer.from([1, 2, 3])) | |
| 61 | }) | |
| 62 | ||
| 63 | test('custom fallback', function (t) { | |
| 64 | t.alike(codecs('baseless', null), null) | |
| 65 | const custom = { decode: function () {}, encode: function () {} } | |
| 66 | t.alike(codecs('baseless', custom), custom) | |
| 67 | }) | |
| 68 | ||
| 69 | test('compact encoding', function (t) { | |
| 70 | const uint = codecs(c.uint) | |
| 71 | t.alike(uint.encode(4), Buffer.from([4])) | |
| 72 | t.alike(uint.encode(1000), Buffer.from([253, 232, 3])) | |
| 73 | t.alike(uint.decode(Buffer.from([4])), 4) | |
| 74 | t.alike(uint.decode(Buffer.from([253, 232, 3])), 1000) | |
| 75 | }) |
| … | … | @@ -0,0 +1 @@ |
| 1 | * text=auto eol=lf |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | name: Build Status | |
| 2 | on: | |
| 3 | push: | |
| 4 | branches: | |
| 5 | - main | |
| 6 | pull_request: | |
| 7 | branches: | |
| 8 | - main | |
| 9 | jobs: | |
| 10 | build: | |
| 11 | strategy: | |
| 12 | matrix: | |
| 13 | node-version: [lts/*] | |
| 14 | os: [ubuntu-latest, macos-latest, windows-latest] | |
| 15 | runs-on: ${{ matrix.os }} | |
| 16 | steps: | |
| 17 | - uses: actions/checkout@v2 | |
| 18 | - name: Use Node.js ${{ matrix.node-version }} | |
| 19 | uses: actions/setup-node@v2 | |
| 20 | with: | |
| 21 | node-version: ${{ matrix.node-version }} | |
| 22 | - run: npm install | |
| 23 | - run: npm test |
| … | … | @@ -0,0 +1 @@ |
| 1 | prettier-config-holepunch |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,13 @@ |
| 1 | Copyright 2023 Holepunch Inc | |
| 2 | ||
| 3 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 | you may not use this file except in compliance with the License. | |
| 5 | You may obtain a copy of the License at | |
| 6 | ||
| 7 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | ||
| 9 | Unless required by applicable law or agreed to in writing, software | |
| 10 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 | See the License for the specific language governing permissions and | |
| 13 | limitations under the License. |
| … | … | @@ -0,0 +1,165 @@ |
| 1 | # compact-encoding | |
| 2 | ||
| 3 | A series of compact encoding schemes for building small and fast parsers and serializers | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install compact-encoding | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const cenc = require('compact-encoding') | |
| 13 | ||
| 14 | const state = cenc.state() | |
| 15 | ||
| 16 | // use preencode to figure out how big a buffer is needed | |
| 17 | cenc.uint.preencode(state, 42) | |
| 18 | cenc.string.preencode(state, 'hi') | |
| 19 | ||
| 20 | console.log(state) // { start: 0, end: 4, buffer: null } | |
| 21 | ||
| 22 | state.buffer = Buffer.allocUnsafe(state.end) | |
| 23 | ||
| 24 | // then use encode to actually encode it to the buffer | |
| 25 | cenc.uint.encode(state, 42) | |
| 26 | cenc.string.encode(state, 'hi') | |
| 27 | ||
| 28 | // to decode it simply use decode instead | |
| 29 | ||
| 30 | state.start = 0 | |
| 31 | cenc.uint.decode(state) // 42 | |
| 32 | cenc.string.decode(state) // 'hi' | |
| 33 | ``` | |
| 34 | ||
| 35 | ## Encoder API | |
| 36 | ||
| 37 | #### `state` | |
| 38 | ||
| 39 | Should be an object that looks like this `{ start, end, buffer }`. | |
| 40 | ||
| 41 | You can also get a blank state object using `cenc.state()`. | |
| 42 | ||
| 43 | - `start` is the byte offset to start encoding/decoding at. | |
| 44 | - `end` is the byte offset indicating the end of the buffer. | |
| 45 | - `buffer` should be either a Node.js Buffer or Uint8Array. | |
| 46 | ||
| 47 | #### `enc.preencode(state, val)` | |
| 48 | ||
| 49 | Does a fast preencode dry-run that only sets state.end. | |
| 50 | Use this to figure out how big of a buffer you need. | |
| 51 | ||
| 52 | #### `enc.encode(state, val)` | |
| 53 | ||
| 54 | Encodes `val` into `state.buffer` at position `state.start`. | |
| 55 | Updates `state.start` to point after the encoded value when done. | |
| 56 | ||
| 57 | #### `val = enc.decode(state)` | |
| 58 | ||
| 59 | Decodes a value from `state.buffer` as position `state.start`. | |
| 60 | Updates `state.start` to point after the decoded value when done in the buffer. | |
| 61 | ||
| 62 | ## Helpers | |
| 63 | ||
| 64 | If you are just encoding to a buffer or decoding from one you can use the `encode` and `decode` helpers | |
| 65 | to reduce your boilerplate | |
| 66 | ||
| 67 | ```js | |
| 68 | const buf = cenc.encode(cenc.bool, true) | |
| 69 | const bool = cenc.decode(cenc.bool, buf) | |
| 70 | ``` | |
| 71 | ||
| 72 | ## Bundled encodings | |
| 73 | ||
| 74 | The following encodings are bundled as they are primitives that can be used | |
| 75 | to build others on top. Feel free to PR more that are missing. | |
| 76 | ||
| 77 | - `cenc.raw` - Pass through encodes a buffer, i.e. a basic copy. | |
| 78 | - `cenc.uint` - Encodes a uint using the smallest fixed size encoding with a prefix to signal which one. Useful for uints that can be a wide range of values. | |
| 79 | - `cenc.uint8` - Encodes a fixed size uint8. | |
| 80 | - `cenc.uint16` - Encodes a fixed size uint16. Useful for things like ports. | |
| 81 | - `cenc.uint24` - Encodes a fixed size uint24. Useful for message framing. | |
| 82 | - `cenc.uint32` - Encodes a fixed size uint32. Useful for very large message framing. | |
| 83 | - `cenc.uint40` - Encodes a fixed size uint40. | |
| 84 | - `cenc.uint48` - Encodes a fixed size uint48. | |
| 85 | - `cenc.uint56` - Encodes a fixed size uint56. | |
| 86 | - `cenc.uint64` - Encodes a fixed size uint64. | |
| 87 | - `cenc.int` - Encodes an int using `cenc.uint` with ZigZag encoding. | |
| 88 | - `cenc.int8` - Encodes a fixed size int8 using `cenc.uint8` with ZigZag encoding. | |
| 89 | - `cenc.int16` - Encodes a fixed size int16 using `cenc.uint16` with ZigZag encoding. | |
| 90 | - `cenc.int24` - Encodes a fixed size int24 using `cenc.uint24` with ZigZag encoding. | |
| 91 | - `cenc.int32` - Encodes a fixed size int32 using `cenc.uint32` with ZigZag encoding. | |
| 92 | - `cenc.int40` - Encodes a fixed size int40 using `cenc.uint40` with ZigZag encoding. | |
| 93 | - `cenc.int48` - Encodes a fixed size int48 using `cenc.uint48` with ZigZag encoding. | |
| 94 | - `cenc.int56` - Encodes a fixed size int56 using `cenc.uint56` with ZigZag encoding. | |
| 95 | - `cenc.int64` - Encodes a fixed size int64 using `cenc.uint64` with ZigZag encoding. | |
| 96 | - `cenc.biguint64` - Encodes a fixed size biguint64. | |
| 97 | - `cenc.bigint64` - Encodes a fixed size bigint64 using `cenc.biguint64` with ZigZag encoding. | |
| 98 | - `cenc.biguint` - Encodes a biguint with its word count uint prefixed. | |
| 99 | - `cenc.bigint` - Encodes a bigint using `cenc.biguint` with ZigZag encoding. | |
| 100 | - `cenc.float32` - Encodes a fixed size float32. | |
| 101 | - `cenc.float64` - Encodes a fixed size float64. | |
| 102 | - `cenc.buffer` - Encodes a buffer with its length uint prefixed. When decoding an empty buffer, `null` is returned. | |
| 103 | - `cenc.raw.buffer` - Encodes a buffer without a length prefixed. | |
| 104 | - `cenc.arraybuffer` - Encodes an arraybuffer with its length uint prefixed. | |
| 105 | - `cenc.raw.arraybuffer` - Encodes an arraybuffer without a length prefixed. | |
| 106 | - `cenc.uint8array` - Encodes a uint8array with its element length uint prefixed. | |
| 107 | - `cenc.raw.uint8array` - Encodes a uint8array without a length prefixed. | |
| 108 | - `cenc.uint16array` - Encodes a uint16array with its element length uint prefixed. | |
| 109 | - `cenc.raw.uint16array` - Encodes a uint16array without a length prefixed. | |
| 110 | - `cenc.uint32array` - Encodes a uint32array with its element length uint prefixed. | |
| 111 | - `cenc.raw.uint32array` - Encodes a uint32array without a length prefixed. | |
| 112 | - `cenc.int8array` - Encodes a int8array with its element length uint prefixed. | |
| 113 | - `cenc.raw.int8array` - Encodes a int8array without a length prefixed. | |
| 114 | - `cenc.int16array` - Encodes a int16array with its element length uint prefixed. | |
| 115 | - `cenc.raw.int16array` - Encodes a int16array without a length prefixed. | |
| 116 | - `cenc.int32array` - Encodes a int32array with its element length uint prefixed. | |
| 117 | - `cenc.raw.int32array` - Encodes a int32array without a length prefixed. | |
| 118 | - `cenc.biguint64array` - Encodes a biguint64array with its element length uint prefixed. | |
| 119 | - `cenc.raw.biguint64array` - Encodes a biguint64array without a length prefixed. | |
| 120 | - `cenc.bigint64array` - Encodes a bigint64array with its element length uint prefixed. | |
| 121 | - `cenc.raw.bigint64array` - Encodes a bigint64array without a length prefixed. | |
| 122 | - `cenc.float32array` - Encodes a float32array with its element length uint prefixed. | |
| 123 | - `cenc.raw.float32array` - Encodes a float32array without a length prefixed. | |
| 124 | - `cenc.float64array` - Encodes a float64array with its element length uint prefixed. | |
| 125 | - `cenc.raw.float64array` - Encodes a float64array without a length prefixed. | |
| 126 | - `cenc.bool` - Encodes a boolean as 1 or 0. | |
| 127 | - `cenc.string`, `cenc.utf8` - Encodes a utf-8 string, similar to buffer. | |
| 128 | - `cenc.raw.string`, `cenc.raw.utf8` - Encodes a utf-8 string without a length prefixed. | |
| 129 | - `cenc.string.fixed(n)`, `cenc.utf8.fixed(n)` - Encodes a fixed sized utf-8 string. | |
| 130 | - `cenc.ascii` - Encodes an ascii string. | |
| 131 | - `cenc.raw.ascii` - Encodes an ascii string without a length prefixed. | |
| 132 | - `cenc.ascii.fixed(n)` - Encodes a fixed size ascii string. | |
| 133 | - `cenc.hex` - Encodes a hex string. | |
| 134 | - `cenc.raw.hex` - Encodes a hex string without a length prefixed. | |
| 135 | - `cenc.hex.fixed(n)` - Encodes a fixed size hex string. | |
| 136 | - `cenc.base64` - Encodes a base64 string. | |
| 137 | - `cenc.raw.base64` - Encodes a base64 string without a length prefixed. | |
| 138 | - `cenc.base64.fixed(n)` - Encodes a fixed size base64 string. | |
| 139 | - `cenc.utf16le`, `cenc.ucs2` - Encodes a utf16le string. | |
| 140 | - `cenc.raw.utf16le`, `cenc.raw.ucs2` - Encodes a utf16le string without a length prefixed. | |
| 141 | - `cenc.utf16le.fixed(n)`, `cenc.ucs2.fixed(n)` - Encodes a fixed size utf16le string. | |
| 142 | - `cenc.fixed32` - Encodes a fixed 32 byte buffer. | |
| 143 | - `cenc.fixed64` - Encodes a fixed 64 byte buffer. | |
| 144 | - `cenc.fixed(n)` - Makes a fixed sized encoder. | |
| 145 | - `cenc.date(d)` - Encodes a date object. | |
| 146 | - `cenc.array(enc)` - Makes an array encoder from another encoder. Arrays are uint prefixed with their length. | |
| 147 | - `cenc.raw.array(enc)` - Makes an array encoder from another encoder, without a length prefixed. | |
| 148 | - `cenc.json` - Encodes a JSON value as utf-8. | |
| 149 | - `cenc.raw.json` - Encodes a JSON value as utf-8 without a length prefixed. | |
| 150 | - `cenc.ndjson` - Encodes a JSON value as newline delimited utf-8. | |
| 151 | - `cenc.raw.ndjson` - Encodes a JSON value as newline delimited utf-8 without a length prefixed. | |
| 152 | - `cenc.any` - Encodes any JSON representable value into a self described buffer. Like JSON + buffer, but using compact types. Useful for schemaless codecs. | |
| 153 | - `cenc.port` - Encodes a port number for network addresses. | |
| 154 | - `cenc.ipv4` - Encodes an IPv4 network address. | |
| 155 | - `cenc.ipv4Address` Encodes an IPv4 network address and a port number. | |
| 156 | - `cenc.ipv6` - Encodes an IPv6 network address. | |
| 157 | - `cenc.ipv6Address` Encodes an IPv6 network address and a port number. | |
| 158 | - `cenc.ip` - Encodes a dual IPv4/6 network address. | |
| 159 | - `cenc.ipAddress` Encodes a dual IPv4/6 network address and a port number. | |
| 160 | - `cenc.from(enc)` - Makes a compact encoder from a [codec](https://github.com/mafintosh/codecs) or [abstract-encoding](https://github.com/mafintosh/abstract-encoding). | |
| 161 | - `cenc.none` - Helper for when you want to just express nothing | |
| 162 | ||
| 163 | ## License | |
| 164 | ||
| 165 | Apache 2.0 |
| … | … | @@ -0,0 +1,4 @@ |
| 1 | const LE = (exports.LE = | |
| 2 | new Uint8Array(new Uint16Array([0xff]).buffer)[0] === 0xff) | |
| 3 | ||
| 4 | exports.BE = !LE |
| … | … | @@ -0,0 +1,1053 @@ |
| 1 | const b4a = require('b4a') | |
| 2 | ||
| 3 | const { BE } = require('./endian') | |
| 4 | ||
| 5 | exports.state = function (start = 0, end = 0, buffer = null) { | |
| 6 | return { start, end, buffer } | |
| 7 | } | |
| 8 | ||
| 9 | const raw = (exports.raw = require('./raw')) | |
| 10 | ||
| 11 | const uint = (exports.uint = { | |
| 12 | preencode(state, n) { | |
| 13 | state.end += n <= 0xfc ? 1 : n <= 0xffff ? 3 : n <= 0xffffffff ? 5 : 9 | |
| 14 | }, | |
| 15 | encode(state, n) { | |
| 16 | if (n <= 0xfc) uint8.encode(state, n) | |
| 17 | else if (n <= 0xffff) { | |
| 18 | state.buffer[state.start++] = 0xfd | |
| 19 | uint16.encode(state, n) | |
| 20 | } else if (n <= 0xffffffff) { | |
| 21 | state.buffer[state.start++] = 0xfe | |
| 22 | uint32.encode(state, n) | |
| 23 | } else { | |
| 24 | state.buffer[state.start++] = 0xff | |
| 25 | uint64.encode(state, n) | |
| 26 | } | |
| 27 | }, | |
| 28 | decode(state) { | |
| 29 | const a = uint8.decode(state) | |
| 30 | if (a <= 0xfc) return a | |
| 31 | if (a === 0xfd) return uint16.decode(state) | |
| 32 | if (a === 0xfe) return uint32.decode(state) | |
| 33 | return uint64.decode(state) | |
| 34 | } | |
| 35 | }) | |
| 36 | ||
| 37 | const uint8 = (exports.uint8 = { | |
| 38 | preencode(state, n) { | |
| 39 | state.end += 1 | |
| 40 | }, | |
| 41 | encode(state, n) { | |
| 42 | validateUint(n) | |
| 43 | state.buffer[state.start++] = n | |
| 44 | }, | |
| 45 | decode(state) { | |
| 46 | if (state.start >= state.end) throw new Error('Out of bounds') | |
| 47 | return state.buffer[state.start++] | |
| 48 | } | |
| 49 | }) | |
| 50 | ||
| 51 | const uint16 = (exports.uint16 = { | |
| 52 | preencode(state, n) { | |
| 53 | state.end += 2 | |
| 54 | }, | |
| 55 | encode(state, n) { | |
| 56 | validateUint(n) | |
| 57 | state.buffer[state.start++] = n | |
| 58 | state.buffer[state.start++] = n >>> 8 | |
| 59 | }, | |
| 60 | decode(state) { | |
| 61 | if (state.end - state.start < 2) throw new Error('Out of bounds') | |
| 62 | return state.buffer[state.start++] + state.buffer[state.start++] * 0x100 | |
| 63 | } | |
| 64 | }) | |
| 65 | ||
| 66 | const uint24 = (exports.uint24 = { | |
| 67 | preencode(state, n) { | |
| 68 | state.end += 3 | |
| 69 | }, | |
| 70 | encode(state, n) { | |
| 71 | validateUint(n) | |
| 72 | state.buffer[state.start++] = n | |
| 73 | state.buffer[state.start++] = n >>> 8 | |
| 74 | state.buffer[state.start++] = n >>> 16 | |
| 75 | }, | |
| 76 | decode(state) { | |
| 77 | if (state.end - state.start < 3) throw new Error('Out of bounds') | |
| 78 | return ( | |
| 79 | state.buffer[state.start++] + | |
| 80 | state.buffer[state.start++] * 0x100 + | |
| 81 | state.buffer[state.start++] * 0x10000 | |
| 82 | ) | |
| 83 | } | |
| 84 | }) | |
| 85 | ||
| 86 | const uint32 = (exports.uint32 = { | |
| 87 | preencode(state, n) { | |
| 88 | state.end += 4 | |
| 89 | }, | |
| 90 | encode(state, n) { | |
| 91 | validateUint(n) | |
| 92 | state.buffer[state.start++] = n | |
| 93 | state.buffer[state.start++] = n >>> 8 | |
| 94 | state.buffer[state.start++] = n >>> 16 | |
| 95 | state.buffer[state.start++] = n >>> 24 | |
| 96 | }, | |
| 97 | decode(state) { | |
| 98 | if (state.end - state.start < 4) throw new Error('Out of bounds') | |
| 99 | return ( | |
| 100 | state.buffer[state.start++] + | |
| 101 | state.buffer[state.start++] * 0x100 + | |
| 102 | state.buffer[state.start++] * 0x10000 + | |
| 103 | state.buffer[state.start++] * 0x1000000 | |
| 104 | ) | |
| 105 | } | |
| 106 | }) | |
| 107 | ||
| 108 | const uint40 = (exports.uint40 = { | |
| 109 | preencode(state, n) { | |
| 110 | state.end += 5 | |
| 111 | }, | |
| 112 | encode(state, n) { | |
| 113 | validateUint(n) | |
| 114 | const r = Math.floor(n / 0x100) | |
| 115 | uint8.encode(state, n) | |
| 116 | uint32.encode(state, r) | |
| 117 | }, | |
| 118 | decode(state) { | |
| 119 | if (state.end - state.start < 5) throw new Error('Out of bounds') | |
| 120 | return uint8.decode(state) + 0x100 * uint32.decode(state) | |
| 121 | } | |
| 122 | }) | |
| 123 | ||
| 124 | const uint48 = (exports.uint48 = { | |
| 125 | preencode(state, n) { | |
| 126 | state.end += 6 | |
| 127 | }, | |
| 128 | encode(state, n) { | |
| 129 | validateUint(n) | |
| 130 | const r = Math.floor(n / 0x10000) | |
| 131 | uint16.encode(state, n) | |
| 132 | uint32.encode(state, r) | |
| 133 | }, | |
| 134 | decode(state) { | |
| 135 | if (state.end - state.start < 6) throw new Error('Out of bounds') | |
| 136 | return uint16.decode(state) + 0x10000 * uint32.decode(state) | |
| 137 | } | |
| 138 | }) | |
| 139 | ||
| 140 | const uint56 = (exports.uint56 = { | |
| 141 | preencode(state, n) { | |
| 142 | state.end += 7 | |
| 143 | }, | |
| 144 | encode(state, n) { | |
| 145 | validateUint(n) | |
| 146 | const r = Math.floor(n / 0x1000000) | |
| 147 | uint24.encode(state, n) | |
| 148 | uint32.encode(state, r) | |
| 149 | }, | |
| 150 | decode(state) { | |
| 151 | if (state.end - state.start < 7) throw new Error('Out of bounds') | |
| 152 | return uint24.decode(state) + 0x1000000 * uint32.decode(state) | |
| 153 | } | |
| 154 | }) | |
| 155 | ||
| 156 | const uint64 = (exports.uint64 = { | |
| 157 | preencode(state, n) { | |
| 158 | state.end += 8 | |
| 159 | }, | |
| 160 | encode(state, n) { | |
| 161 | validateUint(n) | |
| 162 | const r = Math.floor(n / 0x100000000) | |
| 163 | uint32.encode(state, n) | |
| 164 | uint32.encode(state, r) | |
| 165 | }, | |
| 166 | decode(state) { | |
| 167 | if (state.end - state.start < 8) throw new Error('Out of bounds') | |
| 168 | return uint32.decode(state) + 0x100000000 * uint32.decode(state) | |
| 169 | } | |
| 170 | }) | |
| 171 | ||
| 172 | const int = (exports.int = zigZagInt(uint)) | |
| 173 | exports.int8 = zigZagInt(uint8) | |
| 174 | exports.int16 = zigZagInt(uint16) | |
| 175 | exports.int24 = zigZagInt(uint24) | |
| 176 | exports.int32 = zigZagInt(uint32) | |
| 177 | exports.int40 = zigZagInt(uint40) | |
| 178 | exports.int48 = zigZagInt(uint48) | |
| 179 | exports.int56 = zigZagInt(uint56) | |
| 180 | exports.int64 = zigZagInt(uint64) | |
| 181 | ||
| 182 | const biguint64 = (exports.biguint64 = { | |
| 183 | preencode(state, n) { | |
| 184 | state.end += 8 | |
| 185 | }, | |
| 186 | encode(state, n) { | |
| 187 | const view = new DataView( | |
| 188 | state.buffer.buffer, | |
| 189 | state.start + state.buffer.byteOffset, | |
| 190 | 8 | |
| 191 | ) | |
| 192 | view.setBigUint64(0, n, true) // little endian | |
| 193 | state.start += 8 | |
| 194 | }, | |
| 195 | decode(state) { | |
| 196 | if (state.end - state.start < 8) throw new Error('Out of bounds') | |
| 197 | const view = new DataView( | |
| 198 | state.buffer.buffer, | |
| 199 | state.start + state.buffer.byteOffset, | |
| 200 | 8 | |
| 201 | ) | |
| 202 | const n = view.getBigUint64(0, true) // little endian | |
| 203 | state.start += 8 | |
| 204 | return n | |
| 205 | } | |
| 206 | }) | |
| 207 | ||
| 208 | exports.bigint64 = zigZagBigInt(biguint64) | |
| 209 | ||
| 210 | const biguint = (exports.biguint = { | |
| 211 | preencode(state, n) { | |
| 212 | let len = 0 | |
| 213 | for (let m = n; m; m = m >> 64n) len++ | |
| 214 | uint.preencode(state, len) | |
| 215 | state.end += 8 * len | |
| 216 | }, | |
| 217 | encode(state, n) { | |
| 218 | let len = 0 | |
| 219 | for (let m = n; m; m = m >> 64n) len++ | |
| 220 | uint.encode(state, len) | |
| 221 | const view = new DataView( | |
| 222 | state.buffer.buffer, | |
| 223 | state.start + state.buffer.byteOffset, | |
| 224 | 8 * len | |
| 225 | ) | |
| 226 | for (let m = n, i = 0; m; m = m >> 64n, i += 8) { | |
| 227 | view.setBigUint64(i, BigInt.asUintN(64, m), true) // little endian | |
| 228 | } | |
| 229 | state.start += 8 * len | |
| 230 | }, | |
| 231 | decode(state) { | |
| 232 | const len = uint.decode(state) | |
| 233 | if (state.end - state.start < 8 * len) throw new Error('Out of bounds') | |
| 234 | const view = new DataView( | |
| 235 | state.buffer.buffer, | |
| 236 | state.start + state.buffer.byteOffset, | |
| 237 | 8 * len | |
| 238 | ) | |
| 239 | let n = 0n | |
| 240 | for (let i = len - 1; i >= 0; i--) | |
| 241 | n = (n << 64n) + view.getBigUint64(i * 8, true) // little endian | |
| 242 | state.start += 8 * len | |
| 243 | return n | |
| 244 | } | |
| 245 | }) | |
| 246 | ||
| 247 | exports.bigint = zigZagBigInt(biguint) | |
| 248 | ||
| 249 | exports.lexint = require('./lexint') | |
| 250 | ||
| 251 | exports.float32 = { | |
| 252 | preencode(state, n) { | |
| 253 | state.end += 4 | |
| 254 | }, | |
| 255 | encode(state, n) { | |
| 256 | const view = new DataView( | |
| 257 | state.buffer.buffer, | |
| 258 | state.start + state.buffer.byteOffset, | |
| 259 | 4 | |
| 260 | ) | |
| 261 | view.setFloat32(0, n, true) // little endian | |
| 262 | state.start += 4 | |
| 263 | }, | |
| 264 | decode(state) { | |
| 265 | if (state.end - state.start < 4) throw new Error('Out of bounds') | |
| 266 | const view = new DataView( | |
| 267 | state.buffer.buffer, | |
| 268 | state.start + state.buffer.byteOffset, | |
| 269 | 4 | |
| 270 | ) | |
| 271 | const float = view.getFloat32(0, true) // little endian | |
| 272 | state.start += 4 | |
| 273 | return float | |
| 274 | } | |
| 275 | } | |
| 276 | ||
| 277 | exports.float64 = { | |
| 278 | preencode(state, n) { | |
| 279 | state.end += 8 | |
| 280 | }, | |
| 281 | encode(state, n) { | |
| 282 | const view = new DataView( | |
| 283 | state.buffer.buffer, | |
| 284 | state.start + state.buffer.byteOffset, | |
| 285 | 8 | |
| 286 | ) | |
| 287 | view.setFloat64(0, n, true) // little endian | |
| 288 | state.start += 8 | |
| 289 | }, | |
| 290 | decode(state) { | |
| 291 | if (state.end - state.start < 8) throw new Error('Out of bounds') | |
| 292 | const view = new DataView( | |
| 293 | state.buffer.buffer, | |
| 294 | state.start + state.buffer.byteOffset, | |
| 295 | 8 | |
| 296 | ) | |
| 297 | const float = view.getFloat64(0, true) // little endian | |
| 298 | state.start += 8 | |
| 299 | return float | |
| 300 | } | |
| 301 | } | |
| 302 | ||
| 303 | const buffer = (exports.buffer = { | |
| 304 | preencode(state, b) { | |
| 305 | if (b) uint8array.preencode(state, b) | |
| 306 | else state.end++ | |
| 307 | }, | |
| 308 | encode(state, b) { | |
| 309 | if (b) uint8array.encode(state, b) | |
| 310 | else state.buffer[state.start++] = 0 | |
| 311 | }, | |
| 312 | decode(state) { | |
| 313 | const len = uint.decode(state) | |
| 314 | if (len === 0) return null | |
| 315 | if (state.end - state.start < len) throw new Error('Out of bounds') | |
| 316 | return state.buffer.subarray(state.start, (state.start += len)) | |
| 317 | } | |
| 318 | }) | |
| 319 | ||
| 320 | exports.binary = { | |
| 321 | ...buffer, | |
| 322 | preencode(state, b) { | |
| 323 | if (typeof b === 'string') utf8.preencode(state, b) | |
| 324 | else buffer.preencode(state, b) | |
| 325 | }, | |
| 326 | encode(state, b) { | |
| 327 | if (typeof b === 'string') utf8.encode(state, b) | |
| 328 | else buffer.encode(state, b) | |
| 329 | } | |
| 330 | } | |
| 331 | ||
| 332 | exports.arraybuffer = { | |
| 333 | preencode(state, b) { | |
| 334 | uint.preencode(state, b.byteLength) | |
| 335 | state.end += b.byteLength | |
| 336 | }, | |
| 337 | encode(state, b) { | |
| 338 | uint.encode(state, b.byteLength) | |
| 339 | ||
| 340 | const view = new Uint8Array(b) | |
| 341 | ||
| 342 | state.buffer.set(view, state.start) | |
| 343 | state.start += b.byteLength | |
| 344 | }, | |
| 345 | decode(state) { | |
| 346 | const len = uint.decode(state) | |
| 347 | ||
| 348 | const b = new ArrayBuffer(len) | |
| 349 | const view = new Uint8Array(b) | |
| 350 | ||
| 351 | view.set(state.buffer.subarray(state.start, (state.start += len))) | |
| 352 | ||
| 353 | return b | |
| 354 | } | |
| 355 | } | |
| 356 | ||
| 357 | function typedarray(TypedArray, swap) { | |
| 358 | const n = TypedArray.BYTES_PER_ELEMENT | |
| 359 | ||
| 360 | return { | |
| 361 | preencode(state, b) { | |
| 362 | uint.preencode(state, b.length) | |
| 363 | state.end += b.byteLength | |
| 364 | }, | |
| 365 | encode(state, b) { | |
| 366 | uint.encode(state, b.length) | |
| 367 | ||
| 368 | const view = new Uint8Array(b.buffer, b.byteOffset, b.byteLength) | |
| 369 | ||
| 370 | if (BE && swap) swap(view) | |
| 371 | ||
| 372 | state.buffer.set(view, state.start) | |
| 373 | state.start += b.byteLength | |
| 374 | }, | |
| 375 | decode(state) { | |
| 376 | const len = uint.decode(state) | |
| 377 | ||
| 378 | let b = state.buffer.subarray(state.start, (state.start += len * n)) | |
| 379 | if (b.byteLength !== len * n) throw new Error('Out of bounds') | |
| 380 | if (b.byteOffset % n !== 0) b = new Uint8Array(b) | |
| 381 | ||
| 382 | if (BE && swap) swap(b) | |
| 383 | ||
| 384 | return new TypedArray(b.buffer, b.byteOffset, b.byteLength / n) | |
| 385 | } | |
| 386 | } | |
| 387 | } | |
| 388 | ||
| 389 | const uint8array = (exports.uint8array = typedarray(Uint8Array)) | |
| 390 | exports.uint16array = typedarray(Uint16Array, b4a.swap16) | |
| 391 | exports.uint32array = typedarray(Uint32Array, b4a.swap32) | |
| 392 | ||
| 393 | exports.int8array = typedarray(Int8Array) | |
| 394 | exports.int16array = typedarray(Int16Array, b4a.swap16) | |
| 395 | exports.int32array = typedarray(Int32Array, b4a.swap32) | |
| 396 | ||
| 397 | exports.biguint64array = typedarray(BigUint64Array, b4a.swap64) | |
| 398 | exports.bigint64array = typedarray(BigInt64Array, b4a.swap64) | |
| 399 | ||
| 400 | exports.float32array = typedarray(Float32Array, b4a.swap32) | |
| 401 | exports.float64array = typedarray(Float64Array, b4a.swap64) | |
| 402 | ||
| 403 | function string(encoding) { | |
| 404 | return { | |
| 405 | preencode(state, s) { | |
| 406 | const len = b4a.byteLength(s, encoding) | |
| 407 | uint.preencode(state, len) | |
| 408 | state.end += len | |
| 409 | }, | |
| 410 | encode(state, s) { | |
| 411 | const len = b4a.byteLength(s, encoding) | |
| 412 | uint.encode(state, len) | |
| 413 | b4a.write(state.buffer, s, state.start, encoding) | |
| 414 | state.start += len | |
| 415 | }, | |
| 416 | decode(state) { | |
| 417 | const len = uint.decode(state) | |
| 418 | if (state.end - state.start < len) throw new Error('Out of bounds') | |
| 419 | return b4a.toString( | |
| 420 | state.buffer, | |
| 421 | encoding, | |
| 422 | state.start, | |
| 423 | (state.start += len) | |
| 424 | ) | |
| 425 | }, | |
| 426 | fixed(n) { | |
| 427 | return { | |
| 428 | preencode(state) { | |
| 429 | state.end += n | |
| 430 | }, | |
| 431 | encode(state, s) { | |
| 432 | b4a.write(state.buffer, s, state.start, n, encoding) | |
| 433 | state.start += n | |
| 434 | }, | |
| 435 | decode(state) { | |
| 436 | if (state.end - state.start < n) throw new Error('Out of bounds') | |
| 437 | return b4a.toString( | |
| 438 | state.buffer, | |
| 439 | encoding, | |
| 440 | state.start, | |
| 441 | (state.start += n) | |
| 442 | ) | |
| 443 | } | |
| 444 | } | |
| 445 | } | |
| 446 | } | |
| 447 | } | |
| 448 | ||
| 449 | const utf8 = (exports.string = exports.utf8 = string('utf-8')) | |
| 450 | exports.ascii = string('ascii') | |
| 451 | exports.hex = string('hex') | |
| 452 | exports.base64 = string('base64') | |
| 453 | exports.ucs2 = exports.utf16le = string('utf16le') | |
| 454 | ||
| 455 | exports.bool = { | |
| 456 | preencode(state, b) { | |
| 457 | state.end++ | |
| 458 | }, | |
| 459 | encode(state, b) { | |
| 460 | state.buffer[state.start++] = b ? 1 : 0 | |
| 461 | }, | |
| 462 | decode(state) { | |
| 463 | if (state.start >= state.end) throw Error('Out of bounds') | |
| 464 | return state.buffer[state.start++] === 1 | |
| 465 | } | |
| 466 | } | |
| 467 | ||
| 468 | const fixed = (exports.fixed = function fixed(n) { | |
| 469 | return { | |
| 470 | preencode(state, s) { | |
| 471 | if (s.byteLength !== n) throw new Error('Incorrect buffer size') | |
| 472 | state.end += n | |
| 473 | }, | |
| 474 | encode(state, s) { | |
| 475 | state.buffer.set(s, state.start) | |
| 476 | state.start += n | |
| 477 | }, | |
| 478 | decode(state) { | |
| 479 | if (state.end - state.start < n) throw new Error('Out of bounds') | |
| 480 | return state.buffer.subarray(state.start, (state.start += n)) | |
| 481 | } | |
| 482 | } | |
| 483 | }) | |
| 484 | ||
| 485 | exports.fixed32 = fixed(32) | |
| 486 | exports.fixed64 = fixed(64) | |
| 487 | ||
| 488 | exports.array = function array(enc) { | |
| 489 | return { | |
| 490 | preencode(state, list) { | |
| 491 | uint.preencode(state, list.length) | |
| 492 | for (let i = 0; i < list.length; i++) enc.preencode(state, list[i]) | |
| 493 | }, | |
| 494 | encode(state, list) { | |
| 495 | uint.encode(state, list.length) | |
| 496 | for (let i = 0; i < list.length; i++) enc.encode(state, list[i]) | |
| 497 | }, | |
| 498 | decode(state) { | |
| 499 | const len = uint.decode(state) | |
| 500 | if (len > 0x100000) throw new Error('Array is too big') | |
| 501 | const arr = new Array(len) | |
| 502 | for (let i = 0; i < len; i++) arr[i] = enc.decode(state) | |
| 503 | return arr | |
| 504 | } | |
| 505 | } | |
| 506 | } | |
| 507 | ||
| 508 | exports.frame = function frame(enc) { | |
| 509 | const dummy = exports.state() | |
| 510 | ||
| 511 | return { | |
| 512 | preencode(state, m) { | |
| 513 | const end = state.end | |
| 514 | enc.preencode(state, m) | |
| 515 | uint.preencode(state, state.end - end) | |
| 516 | }, | |
| 517 | encode(state, m) { | |
| 518 | dummy.end = 0 | |
| 519 | enc.preencode(dummy, m) | |
| 520 | uint.encode(state, dummy.end) | |
| 521 | enc.encode(state, m) | |
| 522 | }, | |
| 523 | decode(state) { | |
| 524 | const end = state.end | |
| 525 | const len = uint.decode(state) | |
| 526 | state.end = state.start + len | |
| 527 | const m = enc.decode(state) | |
| 528 | state.start = state.end | |
| 529 | state.end = end | |
| 530 | return m | |
| 531 | } | |
| 532 | } | |
| 533 | } | |
| 534 | ||
| 535 | exports.date = { | |
| 536 | preencode(state, d) { | |
| 537 | int.preencode(state, d.getTime()) | |
| 538 | }, | |
| 539 | encode(state, d) { | |
| 540 | int.encode(state, d.getTime()) | |
| 541 | }, | |
| 542 | decode(state, d) { | |
| 543 | return new Date(int.decode(state)) | |
| 544 | } | |
| 545 | } | |
| 546 | ||
| 547 | exports.json = { | |
| 548 | preencode(state, v) { | |
| 549 | utf8.preencode(state, JSON.stringify(v)) | |
| 550 | }, | |
| 551 | encode(state, v) { | |
| 552 | utf8.encode(state, JSON.stringify(v)) | |
| 553 | }, | |
| 554 | decode(state) { | |
| 555 | return JSON.parse(utf8.decode(state)) | |
| 556 | } | |
| 557 | } | |
| 558 | ||
| 559 | exports.ndjson = { | |
| 560 | preencode(state, v) { | |
| 561 | utf8.preencode(state, JSON.stringify(v) + '\n') | |
| 562 | }, | |
| 563 | encode(state, v) { | |
| 564 | utf8.encode(state, JSON.stringify(v) + '\n') | |
| 565 | }, | |
| 566 | decode(state) { | |
| 567 | return JSON.parse(utf8.decode(state)) | |
| 568 | } | |
| 569 | } | |
| 570 | ||
| 571 | // simple helper for when you want to just express nothing | |
| 572 | exports.none = { | |
| 573 | preencode(state, n) { | |
| 574 | // do nothing | |
| 575 | }, | |
| 576 | encode(state, n) { | |
| 577 | // do nothing | |
| 578 | }, | |
| 579 | decode(state) { | |
| 580 | return null | |
| 581 | } | |
| 582 | } | |
| 583 | ||
| 584 | // "any" encoders here for helping just structure any object without schematising it | |
| 585 | ||
| 586 | const anyArray = { | |
| 587 | preencode(state, arr) { | |
| 588 | uint.preencode(state, arr.length) | |
| 589 | for (let i = 0; i < arr.length; i++) { | |
| 590 | any.preencode(state, arr[i]) | |
| 591 | } | |
| 592 | }, | |
| 593 | encode(state, arr) { | |
| 594 | uint.encode(state, arr.length) | |
| 595 | for (let i = 0; i < arr.length; i++) { | |
| 596 | any.encode(state, arr[i]) | |
| 597 | } | |
| 598 | }, | |
| 599 | decode(state) { | |
| 600 | const arr = [] | |
| 601 | let len = uint.decode(state) | |
| 602 | while (len-- > 0) { | |
| 603 | arr.push(any.decode(state)) | |
| 604 | } | |
| 605 | return arr | |
| 606 | } | |
| 607 | } | |
| 608 | ||
| 609 | const anyObject = { | |
| 610 | preencode(state, o) { | |
| 611 | const keys = Object.keys(o) | |
| 612 | uint.preencode(state, keys.length) | |
| 613 | for (const key of keys) { | |
| 614 | utf8.preencode(state, key) | |
| 615 | any.preencode(state, o[key]) | |
| 616 | } | |
| 617 | }, | |
| 618 | encode(state, o) { | |
| 619 | const keys = Object.keys(o) | |
| 620 | uint.encode(state, keys.length) | |
| 621 | for (const key of keys) { | |
| 622 | utf8.encode(state, key) | |
| 623 | any.encode(state, o[key]) | |
| 624 | } | |
| 625 | }, | |
| 626 | decode(state) { | |
| 627 | let len = uint.decode(state) | |
| 628 | const o = {} | |
| 629 | while (len-- > 0) { | |
| 630 | const key = utf8.decode(state) | |
| 631 | o[key] = any.decode(state) | |
| 632 | } | |
| 633 | return o | |
| 634 | } | |
| 635 | } | |
| 636 | ||
| 637 | const anyTypes = [ | |
| 638 | exports.none, | |
| 639 | exports.bool, | |
| 640 | exports.string, | |
| 641 | exports.buffer, | |
| 642 | exports.uint, | |
| 643 | exports.int, | |
| 644 | exports.float64, | |
| 645 | anyArray, | |
| 646 | anyObject, | |
| 647 | exports.date | |
| 648 | ] | |
| 649 | ||
| 650 | const any = (exports.any = { | |
| 651 | preencode(state, o) { | |
| 652 | const t = getType(o) | |
| 653 | uint.preencode(state, t) | |
| 654 | anyTypes[t].preencode(state, o) | |
| 655 | }, | |
| 656 | encode(state, o) { | |
| 657 | const t = getType(o) | |
| 658 | uint.encode(state, t) | |
| 659 | anyTypes[t].encode(state, o) | |
| 660 | }, | |
| 661 | decode(state) { | |
| 662 | const t = uint.decode(state) | |
| 663 | if (t >= anyTypes.length) throw new Error('Unknown type: ' + t) | |
| 664 | return anyTypes[t].decode(state) | |
| 665 | } | |
| 666 | }) | |
| 667 | ||
| 668 | const port = (exports.port = { | |
| 669 | preencode(state, p) { | |
| 670 | if (!p) throw new Error('Port must be >0 and <65536') | |
| 671 | uint16.preencode(state, p) | |
| 672 | }, | |
| 673 | encode(state, p) { | |
| 674 | if (!p) throw new Error('Port must be >0 and <65536') | |
| 675 | uint16.encode(state, p) | |
| 676 | }, | |
| 677 | decode(state) { | |
| 678 | const p = uint16.decode(state) | |
| 679 | if (!p) throw new Error('Port must be >0 and <65536') | |
| 680 | return p | |
| 681 | } | |
| 682 | }) | |
| 683 | ||
| 684 | const address = (host, family) => { | |
| 685 | return { | |
| 686 | preencode(state, m) { | |
| 687 | host.preencode(state, m.host) | |
| 688 | port.preencode(state, m.port) | |
| 689 | }, | |
| 690 | encode(state, m) { | |
| 691 | host.encode(state, m.host) | |
| 692 | port.encode(state, m.port) | |
| 693 | }, | |
| 694 | decode(state) { | |
| 695 | return { | |
| 696 | host: host.decode(state), | |
| 697 | family, | |
| 698 | port: port.decode(state) | |
| 699 | } | |
| 700 | } | |
| 701 | } | |
| 702 | } | |
| 703 | ||
| 704 | const ipv4 = (exports.ipv4 = { | |
| 705 | preencode(state) { | |
| 706 | state.end += 4 | |
| 707 | }, | |
| 708 | encode(state, string) { | |
| 709 | const start = state.start | |
| 710 | const end = start + 4 | |
| 711 | ||
| 712 | let i = 0 | |
| 713 | ||
| 714 | while (i < string.length) { | |
| 715 | let n = 0 | |
| 716 | let c | |
| 717 | ||
| 718 | while ( | |
| 719 | i < string.length && | |
| 720 | (c = string.charCodeAt(i++)) !== /* . */ 0x2e | |
| 721 | ) { | |
| 722 | n = n * 10 + (c - /* 0 */ 0x30) | |
| 723 | } | |
| 724 | ||
| 725 | state.buffer[state.start++] = n | |
| 726 | } | |
| 727 | ||
| 728 | state.start = end | |
| 729 | }, | |
| 730 | decode(state) { | |
| 731 | if (state.end - state.start < 4) throw new Error('Out of bounds') | |
| 732 | return ( | |
| 733 | state.buffer[state.start++] + | |
| 734 | '.' + | |
| 735 | state.buffer[state.start++] + | |
| 736 | '.' + | |
| 737 | state.buffer[state.start++] + | |
| 738 | '.' + | |
| 739 | state.buffer[state.start++] | |
| 740 | ) | |
| 741 | } | |
| 742 | }) | |
| 743 | ||
| 744 | exports.ipv4Address = address(ipv4, 4) | |
| 745 | ||
| 746 | const ipv6 = (exports.ipv6 = { | |
| 747 | preencode(state) { | |
| 748 | state.end += 16 | |
| 749 | }, | |
| 750 | encode(state, string) { | |
| 751 | const start = state.start | |
| 752 | const end = start + 16 | |
| 753 | ||
| 754 | let i = 0 | |
| 755 | let split = null | |
| 756 | ||
| 757 | while (i < string.length) { | |
| 758 | let n = 0 | |
| 759 | let c | |
| 760 | ||
| 761 | while ( | |
| 762 | i < string.length && | |
| 763 | (c = string.charCodeAt(i++)) !== /* : */ 0x3a | |
| 764 | ) { | |
| 765 | if (c >= 0x30 && c <= 0x39) n = n * 0x10 + (c - /* 0 */ 0x30) | |
| 766 | else if (c >= 0x41 && c <= 0x46) n = n * 0x10 + (c - /* A */ 0x41 + 10) | |
| 767 | else if (c >= 0x61 && c <= 0x66) n = n * 0x10 + (c - /* a */ 0x61 + 10) | |
| 768 | } | |
| 769 | ||
| 770 | state.buffer[state.start++] = n >>> 8 | |
| 771 | state.buffer[state.start++] = n | |
| 772 | ||
| 773 | if (i < string.length && string.charCodeAt(i) === /* : */ 0x3a) { | |
| 774 | i++ | |
| 775 | split = state.start | |
| 776 | } | |
| 777 | } | |
| 778 | ||
| 779 | if (split !== null) { | |
| 780 | const offset = end - state.start | |
| 781 | state.buffer | |
| 782 | .copyWithin(split + offset, split) | |
| 783 | .fill(0, split, split + offset) | |
| 784 | } | |
| 785 | ||
| 786 | state.start = end | |
| 787 | }, | |
| 788 | decode(state) { | |
| 789 | if (state.end - state.start < 16) throw new Error('Out of bounds') | |
| 790 | return ( | |
| 791 | ( | |
| 792 | state.buffer[state.start++] * 256 + | |
| 793 | state.buffer[state.start++] | |
| 794 | ).toString(16) + | |
| 795 | ':' + | |
| 796 | ( | |
| 797 | state.buffer[state.start++] * 256 + | |
| 798 | state.buffer[state.start++] | |
| 799 | ).toString(16) + | |
| 800 | ':' + | |
| 801 | ( | |
| 802 | state.buffer[state.start++] * 256 + | |
| 803 | state.buffer[state.start++] | |
| 804 | ).toString(16) + | |
| 805 | ':' + | |
| 806 | ( | |
| 807 | state.buffer[state.start++] * 256 + | |
| 808 | state.buffer[state.start++] | |
| 809 | ).toString(16) + | |
| 810 | ':' + | |
| 811 | ( | |
| 812 | state.buffer[state.start++] * 256 + | |
| 813 | state.buffer[state.start++] | |
| 814 | ).toString(16) + | |
| 815 | ':' + | |
| 816 | ( | |
| 817 | state.buffer[state.start++] * 256 + | |
| 818 | state.buffer[state.start++] | |
| 819 | ).toString(16) + | |
| 820 | ':' + | |
| 821 | ( | |
| 822 | state.buffer[state.start++] * 256 + | |
| 823 | state.buffer[state.start++] | |
| 824 | ).toString(16) + | |
| 825 | ':' + | |
| 826 | ( | |
| 827 | state.buffer[state.start++] * 256 + | |
| 828 | state.buffer[state.start++] | |
| 829 | ).toString(16) | |
| 830 | ) | |
| 831 | } | |
| 832 | }) | |
| 833 | ||
| 834 | exports.ipv6Address = address(ipv6, 6) | |
| 835 | ||
| 836 | const ip = (exports.ip = { | |
| 837 | preencode(state, string) { | |
| 838 | const family = string.includes(':') ? 6 : 4 | |
| 839 | uint8.preencode(state, family) | |
| 840 | if (family === 4) ipv4.preencode(state) | |
| 841 | else ipv6.preencode(state) | |
| 842 | }, | |
| 843 | encode(state, string) { | |
| 844 | const family = string.includes(':') ? 6 : 4 | |
| 845 | uint8.encode(state, family) | |
| 846 | if (family === 4) ipv4.encode(state, string) | |
| 847 | else ipv6.encode(state, string) | |
| 848 | }, | |
| 849 | decode(state) { | |
| 850 | const family = uint8.decode(state) | |
| 851 | if (family === 4) return ipv4.decode(state) | |
| 852 | else return ipv6.decode(state) | |
| 853 | } | |
| 854 | }) | |
| 855 | ||
| 856 | exports.ipAddress = { | |
| 857 | preencode(state, m) { | |
| 858 | ip.preencode(state, m.host) | |
| 859 | port.preencode(state, m.port) | |
| 860 | }, | |
| 861 | encode(state, m) { | |
| 862 | ip.encode(state, m.host) | |
| 863 | port.encode(state, m.port) | |
| 864 | }, | |
| 865 | decode(state) { | |
| 866 | const family = uint8.decode(state) | |
| 867 | return { | |
| 868 | host: family === 4 ? ipv4.decode(state) : ipv6.decode(state), | |
| 869 | family, | |
| 870 | port: port.decode(state) | |
| 871 | } | |
| 872 | } | |
| 873 | } | |
| 874 | ||
| 875 | const record = (exports.record = function (keyEncoding, valueEncoding) { | |
| 876 | return { | |
| 877 | preencode(state, v) { | |
| 878 | const keys = Object.keys(v) | |
| 879 | uint.preencode(state, keys.length) | |
| 880 | for (const k of keys) { | |
| 881 | keyEncoding.preencode(state, k) | |
| 882 | valueEncoding.preencode(state, v[k]) | |
| 883 | } | |
| 884 | }, | |
| 885 | encode(state, v) { | |
| 886 | const keys = Object.keys(v) | |
| 887 | uint.encode(state, keys.length) | |
| 888 | for (const k of keys) { | |
| 889 | keyEncoding.encode(state, k) | |
| 890 | valueEncoding.encode(state, v[k]) | |
| 891 | } | |
| 892 | }, | |
| 893 | decode(state) { | |
| 894 | const out = Object.create(null) | |
| 895 | const keys = uint.decode(state) | |
| 896 | for (let i = 0; i < keys; i++) { | |
| 897 | out[keyEncoding.decode(state)] = valueEncoding.decode(state) | |
| 898 | } | |
| 899 | return out | |
| 900 | } | |
| 901 | } | |
| 902 | }) | |
| 903 | ||
| 904 | exports.stringRecord = record(utf8, utf8) | |
| 905 | ||
| 906 | function getType(o) { | |
| 907 | if (o === null || o === undefined) return 0 | |
| 908 | if (typeof o === 'boolean') return 1 | |
| 909 | if (typeof o === 'string') return 2 | |
| 910 | if (b4a.isBuffer(o)) return 3 | |
| 911 | if (typeof o === 'number') { | |
| 912 | if (Number.isInteger(o)) return o >= 0 ? 4 : 5 | |
| 913 | return 6 | |
| 914 | } | |
| 915 | if (Array.isArray(o)) return 7 | |
| 916 | if (o instanceof Date) return 9 | |
| 917 | if (typeof o === 'object') return 8 | |
| 918 | ||
| 919 | throw new Error('Unsupported type for ' + o) | |
| 920 | } | |
| 921 | ||
| 922 | exports.from = function from(enc) { | |
| 923 | if (typeof enc === 'string') return fromNamed(enc) | |
| 924 | if (enc.preencode) return enc | |
| 925 | if (enc.encodingLength) return fromAbstractEncoder(enc) | |
| 926 | return fromCodec(enc) | |
| 927 | } | |
| 928 | ||
| 929 | function fromNamed(enc) { | |
| 930 | switch (enc) { | |
| 931 | case 'ascii': | |
| 932 | return raw.ascii | |
| 933 | case 'utf-8': | |
| 934 | case 'utf8': | |
| 935 | return raw.utf8 | |
| 936 | case 'hex': | |
| 937 | return raw.hex | |
| 938 | case 'base64': | |
| 939 | return raw.base64 | |
| 940 | case 'utf16-le': | |
| 941 | case 'utf16le': | |
| 942 | case 'ucs-2': | |
| 943 | case 'ucs2': | |
| 944 | return raw.ucs2 | |
| 945 | case 'ndjson': | |
| 946 | return raw.ndjson | |
| 947 | case 'json': | |
| 948 | return raw.json | |
| 949 | case 'binary': | |
| 950 | default: | |
| 951 | return raw.binary | |
| 952 | } | |
| 953 | } | |
| 954 | ||
| 955 | function fromCodec(enc) { | |
| 956 | let tmpM = null | |
| 957 | let tmpBuf = null | |
| 958 | ||
| 959 | return { | |
| 960 | preencode(state, m) { | |
| 961 | tmpM = m | |
| 962 | tmpBuf = enc.encode(m) | |
| 963 | state.end += tmpBuf.byteLength | |
| 964 | }, | |
| 965 | encode(state, m) { | |
| 966 | raw.encode(state, m === tmpM ? tmpBuf : enc.encode(m)) | |
| 967 | tmpM = tmpBuf = null | |
| 968 | }, | |
| 969 | decode(state) { | |
| 970 | return enc.decode(raw.decode(state)) | |
| 971 | } | |
| 972 | } | |
| 973 | } | |
| 974 | ||
| 975 | function fromAbstractEncoder(enc) { | |
| 976 | return { | |
| 977 | preencode(state, m) { | |
| 978 | state.end += enc.encodingLength(m) | |
| 979 | }, | |
| 980 | encode(state, m) { | |
| 981 | enc.encode(m, state.buffer, state.start) | |
| 982 | state.start += enc.encode.bytes | |
| 983 | }, | |
| 984 | decode(state) { | |
| 985 | const m = enc.decode(state.buffer, state.start, state.end) | |
| 986 | state.start += enc.decode.bytes | |
| 987 | return m | |
| 988 | } | |
| 989 | } | |
| 990 | } | |
| 991 | ||
| 992 | exports.encode = function encode(enc, m) { | |
| 993 | const state = exports.state() | |
| 994 | enc.preencode(state, m) | |
| 995 | state.buffer = b4a.allocUnsafe(state.end) | |
| 996 | enc.encode(state, m) | |
| 997 | return state.buffer | |
| 998 | } | |
| 999 | ||
| 1000 | exports.decode = function decode(enc, buffer) { | |
| 1001 | return enc.decode(exports.state(0, buffer.byteLength, buffer)) | |
| 1002 | } | |
| 1003 | ||
| 1004 | function zigZagInt(enc) { | |
| 1005 | return { | |
| 1006 | preencode(state, n) { | |
| 1007 | enc.preencode(state, zigZagEncodeInt(n)) | |
| 1008 | }, | |
| 1009 | encode(state, n) { | |
| 1010 | enc.encode(state, zigZagEncodeInt(n)) | |
| 1011 | }, | |
| 1012 | decode(state) { | |
| 1013 | return zigZagDecodeInt(enc.decode(state)) | |
| 1014 | } | |
| 1015 | } | |
| 1016 | } | |
| 1017 | ||
| 1018 | function zigZagDecodeInt(n) { | |
| 1019 | return n === 0 ? n : (n & 1) === 0 ? n / 2 : -(n + 1) / 2 | |
| 1020 | } | |
| 1021 | ||
| 1022 | function zigZagEncodeInt(n) { | |
| 1023 | // 0, -1, 1, -2, 2, ... | |
| 1024 | return n < 0 ? 2 * -n - 1 : n === 0 ? 0 : 2 * n | |
| 1025 | } | |
| 1026 | ||
| 1027 | function zigZagBigInt(enc) { | |
| 1028 | return { | |
| 1029 | preencode(state, n) { | |
| 1030 | enc.preencode(state, zigZagEncodeBigInt(n)) | |
| 1031 | }, | |
| 1032 | encode(state, n) { | |
| 1033 | enc.encode(state, zigZagEncodeBigInt(n)) | |
| 1034 | }, | |
| 1035 | decode(state) { | |
| 1036 | return zigZagDecodeBigInt(enc.decode(state)) | |
| 1037 | } | |
| 1038 | } | |
| 1039 | } | |
| 1040 | ||
| 1041 | function zigZagDecodeBigInt(n) { | |
| 1042 | return n === 0n ? n : (n & 1n) === 0n ? n / 2n : -(n + 1n) / 2n | |
| 1043 | } | |
| 1044 | ||
| 1045 | function zigZagEncodeBigInt(n) { | |
| 1046 | // 0, -1, 1, -2, 2, ... | |
| 1047 | return n < 0n ? 2n * -n - 1n : n === 0n ? 0n : 2n * n | |
| 1048 | } | |
| 1049 | ||
| 1050 | function validateUint(n) { | |
| 1051 | if (n >= 0 === false /* Handles NaN as well */) | |
| 1052 | throw new Error('uint must be positive') | |
| 1053 | } |
| … | … | @@ -0,0 +1,117 @@ |
| 1 | module.exports = { | |
| 2 | preencode, | |
| 3 | encode, | |
| 4 | decode | |
| 5 | } | |
| 6 | ||
| 7 | function preencode(state, num) { | |
| 8 | if (num < 251) { | |
| 9 | state.end++ | |
| 10 | } else if (num < 256) { | |
| 11 | state.end += 2 | |
| 12 | } else if (num < 0x10000) { | |
| 13 | state.end += 3 | |
| 14 | } else if (num < 0x1000000) { | |
| 15 | state.end += 4 | |
| 16 | } else if (num < 0x100000000) { | |
| 17 | state.end += 5 | |
| 18 | } else { | |
| 19 | state.end++ | |
| 20 | const exp = Math.floor(Math.log(num) / Math.log(2)) - 32 | |
| 21 | preencode(state, exp) | |
| 22 | state.end += 6 | |
| 23 | } | |
| 24 | } | |
| 25 | ||
| 26 | function encode(state, num) { | |
| 27 | const max = 251 | |
| 28 | const x = num - max | |
| 29 | ||
| 30 | if (num < max) { | |
| 31 | state.buffer[state.start++] = num | |
| 32 | } else if (num < 256) { | |
| 33 | state.buffer[state.start++] = max | |
| 34 | state.buffer[state.start++] = x | |
| 35 | } else if (num < 0x10000) { | |
| 36 | state.buffer[state.start++] = max + 1 | |
| 37 | state.buffer[state.start++] = (x >> 8) & 0xff | |
| 38 | state.buffer[state.start++] = x & 0xff | |
| 39 | } else if (num < 0x1000000) { | |
| 40 | state.buffer[state.start++] = max + 2 | |
| 41 | state.buffer[state.start++] = x >> 16 | |
| 42 | state.buffer[state.start++] = (x >> 8) & 0xff | |
| 43 | state.buffer[state.start++] = x & 0xff | |
| 44 | } else if (num < 0x100000000) { | |
| 45 | state.buffer[state.start++] = max + 3 | |
| 46 | state.buffer[state.start++] = x >> 24 | |
| 47 | state.buffer[state.start++] = (x >> 16) & 0xff | |
| 48 | state.buffer[state.start++] = (x >> 8) & 0xff | |
| 49 | state.buffer[state.start++] = x & 0xff | |
| 50 | } else { | |
| 51 | // need to use Math here as bitwise ops are 32 bit | |
| 52 | const exp = Math.floor(Math.log(x) / Math.log(2)) - 32 | |
| 53 | state.buffer[state.start++] = 0xff | |
| 54 | ||
| 55 | encode(state, exp) | |
| 56 | const rem = x / Math.pow(2, exp - 11) | |
| 57 | ||
| 58 | for (let i = 5; i >= 0; i--) { | |
| 59 | state.buffer[state.start++] = (rem / Math.pow(2, 8 * i)) & 0xff | |
| 60 | } | |
| 61 | } | |
| 62 | } | |
| 63 | ||
| 64 | function decode(state) { | |
| 65 | const max = 251 | |
| 66 | ||
| 67 | if (state.end - state.start < 1) throw new Error('Out of bounds') | |
| 68 | ||
| 69 | const flag = state.buffer[state.start++] | |
| 70 | ||
| 71 | if (flag < max) return flag | |
| 72 | ||
| 73 | if (state.end - state.start < flag - max + 1) { | |
| 74 | throw new Error('Out of bounds.') | |
| 75 | } | |
| 76 | ||
| 77 | if (flag < 252) { | |
| 78 | return state.buffer[state.start++] + max | |
| 79 | } | |
| 80 | ||
| 81 | if (flag < 253) { | |
| 82 | return ( | |
| 83 | (state.buffer[state.start++] << 8) + state.buffer[state.start++] + max | |
| 84 | ) | |
| 85 | } | |
| 86 | ||
| 87 | if (flag < 254) { | |
| 88 | return ( | |
| 89 | (state.buffer[state.start++] << 16) + | |
| 90 | (state.buffer[state.start++] << 8) + | |
| 91 | state.buffer[state.start++] + | |
| 92 | max | |
| 93 | ) | |
| 94 | } | |
| 95 | ||
| 96 | // << 24 result may be interpreted as negative | |
| 97 | if (flag < 255) { | |
| 98 | return ( | |
| 99 | state.buffer[state.start++] * 0x1000000 + | |
| 100 | (state.buffer[state.start++] << 16) + | |
| 101 | (state.buffer[state.start++] << 8) + | |
| 102 | state.buffer[state.start++] + | |
| 103 | max | |
| 104 | ) | |
| 105 | } | |
| 106 | ||
| 107 | const exp = decode(state) | |
| 108 | ||
| 109 | if (state.end - state.start < 6) throw new Error('Out of bounds') | |
| 110 | ||
| 111 | let rem = 0 | |
| 112 | for (let i = 5; i >= 0; i--) { | |
| 113 | rem += state.buffer[state.start++] * Math.pow(2, 8 * i) | |
| 114 | } | |
| 115 | ||
| 116 | return rem * Math.pow(2, exp - 11) + max | |
| 117 | } |
| … | … | @@ -0,0 +1,28 @@ |
| 1 | { | |
| 2 | "name": "compact-encoding", | |
| 3 | "version": "2.19.1", | |
| 4 | "description": "A series of compact encoding schemes for building small and fast parsers and serializers", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "b4a": "^1.3.0" | |
| 8 | }, | |
| 9 | "devDependencies": { | |
| 10 | "brittle": "^3.0.0", | |
| 11 | "prettier": "^3.6.2", | |
| 12 | "prettier-config-holepunch": "^1.0.0" | |
| 13 | }, | |
| 14 | "scripts": { | |
| 15 | "format": "prettier . --write", | |
| 16 | "test": "prettier . --check && brittle test.js" | |
| 17 | }, | |
| 18 | "repository": { | |
| 19 | "type": "git", | |
| 20 | "url": "https://github.com/holepunchto/compact-encoding.git" | |
| 21 | }, | |
| 22 | "author": "Mathias Buus (@mafintosh)", | |
| 23 | "license": "Apache-2.0", | |
| 24 | "bugs": { | |
| 25 | "url": "https://github.com/holepunchto/compact-encoding/issues" | |
| 26 | }, | |
| 27 | "homepage": "https://github.com/holepunchto/compact-encoding" | |
| 28 | } |
| … | … | @@ -0,0 +1,173 @@ |
| 1 | const b4a = require('b4a') | |
| 2 | ||
| 3 | const { BE } = require('./endian') | |
| 4 | ||
| 5 | exports = module.exports = { | |
| 6 | preencode(state, b) { | |
| 7 | state.end += b.byteLength | |
| 8 | }, | |
| 9 | encode(state, b) { | |
| 10 | state.buffer.set(b, state.start) | |
| 11 | state.start += b.byteLength | |
| 12 | }, | |
| 13 | decode(state) { | |
| 14 | const b = state.buffer.subarray(state.start, state.end) | |
| 15 | state.start = state.end | |
| 16 | return b | |
| 17 | } | |
| 18 | } | |
| 19 | ||
| 20 | const buffer = (exports.buffer = { | |
| 21 | preencode(state, b) { | |
| 22 | if (b) uint8array.preencode(state, b) | |
| 23 | else state.end++ | |
| 24 | }, | |
| 25 | encode(state, b) { | |
| 26 | if (b) uint8array.encode(state, b) | |
| 27 | else state.buffer[state.start++] = 0 | |
| 28 | }, | |
| 29 | decode(state) { | |
| 30 | const b = state.buffer.subarray(state.start) | |
| 31 | if (b.byteLength === 0) return null | |
| 32 | state.start = state.end | |
| 33 | return b | |
| 34 | } | |
| 35 | }) | |
| 36 | ||
| 37 | exports.binary = { | |
| 38 | ...buffer, | |
| 39 | preencode(state, b) { | |
| 40 | if (typeof b === 'string') utf8.preencode(state, b) | |
| 41 | else buffer.preencode(state, b) | |
| 42 | }, | |
| 43 | encode(state, b) { | |
| 44 | if (typeof b === 'string') utf8.encode(state, b) | |
| 45 | else buffer.encode(state, b) | |
| 46 | } | |
| 47 | } | |
| 48 | ||
| 49 | exports.arraybuffer = { | |
| 50 | preencode(state, b) { | |
| 51 | state.end += b.byteLength | |
| 52 | }, | |
| 53 | encode(state, b) { | |
| 54 | const view = new Uint8Array(b) | |
| 55 | ||
| 56 | state.buffer.set(view, state.start) | |
| 57 | state.start += b.byteLength | |
| 58 | }, | |
| 59 | decode(state) { | |
| 60 | const b = new ArrayBuffer(state.end - state.start) | |
| 61 | const view = new Uint8Array(b) | |
| 62 | ||
| 63 | view.set(state.buffer.subarray(state.start)) | |
| 64 | ||
| 65 | state.start = state.end | |
| 66 | ||
| 67 | return b | |
| 68 | } | |
| 69 | } | |
| 70 | ||
| 71 | function typedarray(TypedArray, swap) { | |
| 72 | const n = TypedArray.BYTES_PER_ELEMENT | |
| 73 | ||
| 74 | return { | |
| 75 | preencode(state, b) { | |
| 76 | state.end += b.byteLength | |
| 77 | }, | |
| 78 | encode(state, b) { | |
| 79 | const view = new Uint8Array(b.buffer, b.byteOffset, b.byteLength) | |
| 80 | ||
| 81 | if (BE && swap) swap(view) | |
| 82 | ||
| 83 | state.buffer.set(view, state.start) | |
| 84 | state.start += b.byteLength | |
| 85 | }, | |
| 86 | decode(state) { | |
| 87 | let b = state.buffer.subarray(state.start) | |
| 88 | if (b.byteOffset % n !== 0) b = new Uint8Array(b) | |
| 89 | ||
| 90 | if (BE && swap) swap(b) | |
| 91 | ||
| 92 | state.start = state.end | |
| 93 | ||
| 94 | return new TypedArray(b.buffer, b.byteOffset, b.byteLength / n) | |
| 95 | } | |
| 96 | } | |
| 97 | } | |
| 98 | ||
| 99 | const uint8array = (exports.uint8array = typedarray(Uint8Array)) | |
| 100 | exports.uint16array = typedarray(Uint16Array, b4a.swap16) | |
| 101 | exports.uint32array = typedarray(Uint32Array, b4a.swap32) | |
| 102 | ||
| 103 | exports.int8array = typedarray(Int8Array) | |
| 104 | exports.int16array = typedarray(Int16Array, b4a.swap16) | |
| 105 | exports.int32array = typedarray(Int32Array, b4a.swap32) | |
| 106 | ||
| 107 | exports.biguint64array = typedarray(BigUint64Array, b4a.swap64) | |
| 108 | exports.bigint64array = typedarray(BigInt64Array, b4a.swap64) | |
| 109 | ||
| 110 | exports.float32array = typedarray(Float32Array, b4a.swap32) | |
| 111 | exports.float64array = typedarray(Float64Array, b4a.swap64) | |
| 112 | ||
| 113 | function string(encoding) { | |
| 114 | return { | |
| 115 | preencode(state, s) { | |
| 116 | state.end += b4a.byteLength(s, encoding) | |
| 117 | }, | |
| 118 | encode(state, s) { | |
| 119 | state.start += b4a.write(state.buffer, s, state.start, encoding) | |
| 120 | }, | |
| 121 | decode(state) { | |
| 122 | const s = b4a.toString(state.buffer, encoding, state.start) | |
| 123 | state.start = state.end | |
| 124 | return s | |
| 125 | } | |
| 126 | } | |
| 127 | } | |
| 128 | ||
| 129 | const utf8 = (exports.string = exports.utf8 = string('utf-8')) | |
| 130 | exports.ascii = string('ascii') | |
| 131 | exports.hex = string('hex') | |
| 132 | exports.base64 = string('base64') | |
| 133 | exports.ucs2 = exports.utf16le = string('utf16le') | |
| 134 | ||
| 135 | exports.array = function array(enc) { | |
| 136 | return { | |
| 137 | preencode(state, list) { | |
| 138 | for (const value of list) enc.preencode(state, value) | |
| 139 | }, | |
| 140 | encode(state, list) { | |
| 141 | for (const value of list) enc.encode(state, value) | |
| 142 | }, | |
| 143 | decode(state) { | |
| 144 | const arr = [] | |
| 145 | while (state.start < state.end) arr.push(enc.decode(state)) | |
| 146 | return arr | |
| 147 | } | |
| 148 | } | |
| 149 | } | |
| 150 | ||
| 151 | exports.json = { | |
| 152 | preencode(state, v) { | |
| 153 | utf8.preencode(state, JSON.stringify(v)) | |
| 154 | }, | |
| 155 | encode(state, v) { | |
| 156 | utf8.encode(state, JSON.stringify(v)) | |
| 157 | }, | |
| 158 | decode(state) { | |
| 159 | return JSON.parse(utf8.decode(state)) | |
| 160 | } | |
| 161 | } | |
| 162 | ||
| 163 | exports.ndjson = { | |
| 164 | preencode(state, v) { | |
| 165 | utf8.preencode(state, JSON.stringify(v) + '\n') | |
| 166 | }, | |
| 167 | encode(state, v) { | |
| 168 | utf8.encode(state, JSON.stringify(v) + '\n') | |
| 169 | }, | |
| 170 | decode(state) { | |
| 171 | return JSON.parse(utf8.decode(state)) | |
| 172 | } | |
| 173 | } |
| … | … | @@ -0,0 +1,1184 @@ |
| 1 | const enc = require('./') | |
| 2 | const test = require('brittle') | |
| 3 | const b4a = require('b4a') | |
| 4 | ||
| 5 | test('uint', function (t) { | |
| 6 | const state = enc.state() | |
| 7 | ||
| 8 | enc.uint.preencode(state, 42) | |
| 9 | t.alike(state, enc.state(0, 1)) | |
| 10 | enc.uint.preencode(state, 4200) | |
| 11 | t.alike(state, enc.state(0, 4)) | |
| 12 | enc.uint.preencode(state, Number.MAX_SAFE_INTEGER) | |
| 13 | t.alike(state, enc.state(0, 13)) | |
| 14 | ||
| 15 | state.buffer = b4a.alloc(state.end) | |
| 16 | enc.uint.encode(state, 42) | |
| 17 | t.alike( | |
| 18 | state, | |
| 19 | enc.state(1, 13, b4a.from([42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])) | |
| 20 | ) | |
| 21 | enc.uint.encode(state, 4200) | |
| 22 | t.alike( | |
| 23 | state, | |
| 24 | enc.state(4, 13, b4a.from([42, 0xfd, 104, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0])) | |
| 25 | ) | |
| 26 | enc.uint.encode(state, Number.MAX_SAFE_INTEGER) | |
| 27 | t.alike( | |
| 28 | state, | |
| 29 | enc.state( | |
| 30 | 13, | |
| 31 | 13, | |
| 32 | b4a.from([42, 0xfd, 104, 16, 0xff, 255, 255, 255, 255, 255, 255, 31, 0]) | |
| 33 | ) | |
| 34 | ) | |
| 35 | ||
| 36 | state.start = 0 | |
| 37 | t.is(enc.uint.decode(state), 42) | |
| 38 | t.is(enc.uint.decode(state), 4200) | |
| 39 | t.is(enc.uint.decode(state), Number.MAX_SAFE_INTEGER) | |
| 40 | t.is(state.start, state.end) | |
| 41 | ||
| 42 | t.exception(() => enc.uint.decode(state)) | |
| 43 | }) | |
| 44 | ||
| 45 | test('int', function (t) { | |
| 46 | const state = enc.state() | |
| 47 | ||
| 48 | enc.int.preencode(state, 42) | |
| 49 | t.alike(state, enc.state(0, 1)) | |
| 50 | enc.int.preencode(state, -4200) | |
| 51 | t.alike(state, enc.state(0, 4)) | |
| 52 | ||
| 53 | state.buffer = b4a.alloc(state.end) | |
| 54 | enc.int.encode(state, 42) | |
| 55 | t.alike(state, enc.state(1, 4, b4a.from([84, 0, 0, 0]))) | |
| 56 | enc.int.encode(state, -4200) | |
| 57 | t.alike(state, enc.state(4, 4, b4a.from([84, 0xfd, 207, 32]))) | |
| 58 | ||
| 59 | state.start = 0 | |
| 60 | t.is(enc.int.decode(state), 42) | |
| 61 | t.is(enc.int.decode(state), -4200) | |
| 62 | t.is(state.start, state.end) | |
| 63 | ||
| 64 | t.exception(() => enc.int.decode(state)) | |
| 65 | }) | |
| 66 | ||
| 67 | test('float64', function (t) { | |
| 68 | const state = enc.state() | |
| 69 | ||
| 70 | enc.float64.preencode(state, 162.2377294) | |
| 71 | t.alike(state, enc.state(0, 8)) | |
| 72 | ||
| 73 | state.buffer = b4a.alloc(state.end) | |
| 74 | t.alike(state, enc.state(0, 8, b4a.from([0, 0, 0, 0, 0, 0, 0, 0]))) | |
| 75 | enc.float64.encode(state, 162.2377294) | |
| 76 | t.alike( | |
| 77 | state, | |
| 78 | enc.state(8, 8, b4a.from([0x87, 0xc9, 0xaf, 0x7a, 0x9b, 0x47, 0x64, 0x40])) | |
| 79 | ) | |
| 80 | ||
| 81 | state.start = 0 | |
| 82 | t.is(enc.float64.decode(state), 162.2377294) | |
| 83 | t.is(state.start, state.end) | |
| 84 | ||
| 85 | t.exception(() => enc.float64.decode(state)) | |
| 86 | ||
| 87 | // alignement | |
| 88 | state.start = 0 | |
| 89 | state.end = 0 | |
| 90 | state.buffer = null | |
| 91 | ||
| 92 | enc.int.preencode(state, 0) | |
| 93 | enc.float64.preencode(state, 162.2377294) | |
| 94 | t.alike(state, enc.state(0, 9)) | |
| 95 | ||
| 96 | state.buffer = b4a.alloc(state.end) | |
| 97 | t.alike(state, enc.state(0, 9, b4a.from([0, 0, 0, 0, 0, 0, 0, 0, 0]))) | |
| 98 | enc.int.encode(state, 0) | |
| 99 | enc.float64.encode(state, 162.2377294) | |
| 100 | t.alike( | |
| 101 | state, | |
| 102 | enc.state( | |
| 103 | 9, | |
| 104 | 9, | |
| 105 | b4a.from([0, 0x87, 0xc9, 0xaf, 0x7a, 0x9b, 0x47, 0x64, 0x40]) | |
| 106 | ) | |
| 107 | ) | |
| 108 | ||
| 109 | state.start = 0 | |
| 110 | t.is(enc.int.decode(state), 0) | |
| 111 | t.is(enc.float64.decode(state), 162.2377294) | |
| 112 | t.is(state.start, state.end) | |
| 113 | ||
| 114 | // subarray | |
| 115 | const buf = b4a.alloc(10) | |
| 116 | state.start = 0 | |
| 117 | state.buffer = buf.subarray(1) | |
| 118 | t.alike(state, enc.state(0, 9, b4a.from([0, 0, 0, 0, 0, 0, 0, 0, 0]))) | |
| 119 | enc.int.encode(state, 0) | |
| 120 | enc.float64.encode(state, 162.2377294) | |
| 121 | t.alike( | |
| 122 | state, | |
| 123 | enc.state( | |
| 124 | 9, | |
| 125 | 9, | |
| 126 | b4a.from([0, 0x87, 0xc9, 0xaf, 0x7a, 0x9b, 0x47, 0x64, 0x40]) | |
| 127 | ) | |
| 128 | ) | |
| 129 | t.alike(buf, b4a.from([0, 0, 0x87, 0xc9, 0xaf, 0x7a, 0x9b, 0x47, 0x64, 0x40])) | |
| 130 | ||
| 131 | state.start = 0 | |
| 132 | t.is(enc.int.decode(state), 0) | |
| 133 | t.is(enc.float64.decode(state), 162.2377294) | |
| 134 | t.is(state.start, state.end) | |
| 135 | ||
| 136 | // 0 | |
| 137 | state.start = 0 | |
| 138 | state.end = 0 | |
| 139 | state.buffer = null | |
| 140 | ||
| 141 | enc.float64.preencode(state, 162.2377294) | |
| 142 | state.buffer = b4a.alloc(state.end) | |
| 143 | enc.float64.encode(state, 0) | |
| 144 | t.alike(state, enc.state(8, 8, b4a.from([0, 0, 0, 0, 0, 0, 0, 0]))) | |
| 145 | ||
| 146 | state.start = 0 | |
| 147 | t.is(enc.float64.decode(state), 0) | |
| 148 | t.is(state.start, state.end) | |
| 149 | ||
| 150 | // Infinity | |
| 151 | state.start = 0 | |
| 152 | state.end = 0 | |
| 153 | state.buffer = null | |
| 154 | ||
| 155 | enc.float64.preencode(state, Infinity) | |
| 156 | state.buffer = b4a.alloc(state.end) | |
| 157 | enc.float64.encode(state, Infinity) | |
| 158 | t.alike(state, enc.state(8, 8, b4a.from([0, 0, 0, 0, 0, 0, 0xf0, 0x7f]))) | |
| 159 | ||
| 160 | state.start = 0 | |
| 161 | t.is(enc.float64.decode(state), Infinity) | |
| 162 | t.is(state.start, state.end) | |
| 163 | ||
| 164 | // Edge cases | |
| 165 | state.start = 0 | |
| 166 | state.end = 0 | |
| 167 | state.buffer = null | |
| 168 | ||
| 169 | enc.float64.preencode(state, 0.1 + 0.2) | |
| 170 | state.buffer = b4a.alloc(state.end) | |
| 171 | enc.float64.encode(state, 0.1 + 0.2) | |
| 172 | t.alike( | |
| 173 | state, | |
| 174 | enc.state(8, 8, b4a.from([0x34, 0x33, 0x33, 0x33, 0x33, 0x33, 0xd3, 0x3f])) | |
| 175 | ) | |
| 176 | ||
| 177 | state.start = 0 | |
| 178 | t.is(enc.float64.decode(state), 0.1 + 0.2) | |
| 179 | t.is(state.start, state.end) | |
| 180 | }) | |
| 181 | ||
| 182 | test('biguint64', function (t) { | |
| 183 | const state = enc.state() | |
| 184 | ||
| 185 | const n = 0x0102030405060708n | |
| 186 | ||
| 187 | enc.biguint64.preencode(state, n) | |
| 188 | t.alike(state, enc.state(0, 8)) | |
| 189 | ||
| 190 | state.buffer = b4a.alloc(state.end) | |
| 191 | enc.biguint64.encode(state, n) | |
| 192 | t.alike( | |
| 193 | state, | |
| 194 | enc.state(8, 8, b4a.from([0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1])) | |
| 195 | ) | |
| 196 | ||
| 197 | state.start = 0 | |
| 198 | t.is(enc.biguint64.decode(state), n) | |
| 199 | t.is(state.start, state.end) | |
| 200 | ||
| 201 | t.exception(() => enc.biguint64.decode(state)) | |
| 202 | }) | |
| 203 | ||
| 204 | test('bigint64', function (t) { | |
| 205 | const state = enc.state() | |
| 206 | ||
| 207 | const n = -0x0102030405060708n | |
| 208 | ||
| 209 | enc.bigint64.preencode(state, n) | |
| 210 | t.alike(state, enc.state(0, 8)) | |
| 211 | ||
| 212 | state.buffer = b4a.alloc(state.end) | |
| 213 | enc.bigint64.encode(state, n) | |
| 214 | t.alike( | |
| 215 | state, | |
| 216 | enc.state(8, 8, b4a.from([0xf, 0xe, 0xc, 0xa, 0x8, 0x6, 0x4, 0x2])) | |
| 217 | ) | |
| 218 | ||
| 219 | state.start = 0 | |
| 220 | t.is(enc.bigint64.decode(state), n) | |
| 221 | t.is(state.start, state.end) | |
| 222 | ||
| 223 | t.exception(() => enc.bigint64.decode(state)) | |
| 224 | }) | |
| 225 | ||
| 226 | test('biguint', function (t) { | |
| 227 | const state = enc.state() | |
| 228 | ||
| 229 | const n = 0x0102030405060708090a0b0cn | |
| 230 | ||
| 231 | enc.biguint.preencode(state, n) | |
| 232 | t.alike(state, enc.state(0, 17)) | |
| 233 | ||
| 234 | state.buffer = b4a.alloc(state.end) | |
| 235 | enc.biguint.encode(state, n) | |
| 236 | t.alike( | |
| 237 | state, | |
| 238 | enc.state( | |
| 239 | 17, | |
| 240 | 17, | |
| 241 | b4a.from([ | |
| 242 | 2, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0, 0x0, | |
| 243 | 0x0, 0x0 | |
| 244 | ]) | |
| 245 | ) | |
| 246 | ) | |
| 247 | ||
| 248 | state.start = 0 | |
| 249 | t.is(enc.biguint.decode(state), n) | |
| 250 | t.is(state.start, state.end) | |
| 251 | ||
| 252 | t.exception(() => enc.biguint.decode(state)) | |
| 253 | }) | |
| 254 | ||
| 255 | test('bigint', function (t) { | |
| 256 | const state = enc.state() | |
| 257 | ||
| 258 | const n = -0x0102030405060708090a0b0cn | |
| 259 | ||
| 260 | enc.bigint.preencode(state, n) | |
| 261 | t.alike(state, enc.state(0, 17)) | |
| 262 | ||
| 263 | state.buffer = b4a.alloc(state.end) | |
| 264 | enc.bigint.encode(state, n) | |
| 265 | t.alike( | |
| 266 | state, | |
| 267 | enc.state( | |
| 268 | 17, | |
| 269 | 17, | |
| 270 | b4a.from([ | |
| 271 | 2, 0x17, 0x16, 0x14, 0x12, 0x10, 0xe, 0xc, 0xa, 0x8, 0x6, 0x4, 0x2, 0x0, | |
| 272 | 0x0, 0x0, 0x0 | |
| 273 | ]) | |
| 274 | ) | |
| 275 | ) | |
| 276 | ||
| 277 | state.start = 0 | |
| 278 | t.is(enc.bigint.decode(state), n) | |
| 279 | t.is(state.start, state.end) | |
| 280 | ||
| 281 | t.exception(() => enc.bigint.decode(state)) | |
| 282 | }) | |
| 283 | ||
| 284 | test('buffer', function (t) { | |
| 285 | const state = enc.state() | |
| 286 | ||
| 287 | enc.buffer.preencode(state, b4a.from('hi')) | |
| 288 | t.alike(state, enc.state(0, 3)) | |
| 289 | enc.buffer.preencode(state, b4a.from('hello')) | |
| 290 | t.alike(state, enc.state(0, 9)) | |
| 291 | enc.buffer.preencode(state, null) | |
| 292 | t.alike(state, enc.state(0, 10)) | |
| 293 | ||
| 294 | state.buffer = b4a.alloc(state.end) | |
| 295 | enc.buffer.encode(state, b4a.from('hi')) | |
| 296 | t.alike( | |
| 297 | state, | |
| 298 | enc.state(3, 10, b4a.from('\x02hi\x00\x00\x00\x00\x00\x00\x00')) | |
| 299 | ) | |
| 300 | enc.buffer.encode(state, b4a.from('hello')) | |
| 301 | t.alike(state, enc.state(9, 10, b4a.from('\x02hi\x05hello\x00'))) | |
| 302 | enc.buffer.encode(state, null) | |
| 303 | t.alike(state, enc.state(10, 10, b4a.from('\x02hi\x05hello\x00'))) | |
| 304 | ||
| 305 | state.start = 0 | |
| 306 | t.alike(enc.buffer.decode(state), b4a.from('hi')) | |
| 307 | t.alike(enc.buffer.decode(state), b4a.from('hello')) | |
| 308 | t.is(enc.buffer.decode(state), null) | |
| 309 | t.is(state.start, state.end) | |
| 310 | ||
| 311 | t.exception(() => enc.buffer.decode(state)) | |
| 312 | }) | |
| 313 | ||
| 314 | test('arraybuffer', function (t) { | |
| 315 | const state = enc.state() | |
| 316 | ||
| 317 | const b1 = new ArrayBuffer(4) | |
| 318 | b4a.from(b1).fill('a') | |
| 319 | ||
| 320 | const b2 = new ArrayBuffer(8) | |
| 321 | b4a.from(b2).fill('b') | |
| 322 | ||
| 323 | enc.arraybuffer.preencode(state, b1) | |
| 324 | t.alike(state, enc.state(0, 5)) | |
| 325 | enc.arraybuffer.preencode(state, b2) | |
| 326 | t.alike(state, enc.state(0, 14)) | |
| 327 | ||
| 328 | state.buffer = b4a.alloc(state.end) | |
| 329 | enc.arraybuffer.encode(state, b1) | |
| 330 | t.alike( | |
| 331 | state, | |
| 332 | enc.state(5, 14, b4a.from('\x04aaaa\x00\x00\x00\x00\x00\x00\x00\x00\x00')) | |
| 333 | ) | |
| 334 | enc.arraybuffer.encode(state, b2) | |
| 335 | t.alike(state, enc.state(14, 14, b4a.from('\x04aaaa\x08bbbbbbbb'))) | |
| 336 | ||
| 337 | state.start = 0 | |
| 338 | t.alike(enc.arraybuffer.decode(state), b1) | |
| 339 | t.alike(enc.arraybuffer.decode(state), b2) | |
| 340 | t.is(state.start, state.end) | |
| 341 | ||
| 342 | t.exception(() => enc.arraybuffer.decode(state)) | |
| 343 | }) | |
| 344 | ||
| 345 | test('raw', function (t) { | |
| 346 | const state = enc.state() | |
| 347 | ||
| 348 | enc.raw.preencode(state, b4a.from('hi')) | |
| 349 | t.alike(state, enc.state(0, 2)) | |
| 350 | ||
| 351 | state.buffer = b4a.alloc(state.end) | |
| 352 | enc.raw.encode(state, b4a.from('hi')) | |
| 353 | t.alike(state, enc.state(2, 2, b4a.from('hi'))) | |
| 354 | ||
| 355 | state.start = 0 | |
| 356 | t.alike(enc.raw.decode(state), b4a.from('hi')) | |
| 357 | t.is(state.start, state.end) | |
| 358 | }) | |
| 359 | ||
| 360 | test('raw uint8array', function (t) { | |
| 361 | const state = enc.state() | |
| 362 | ||
| 363 | enc.raw.uint8array.preencode(state, Uint8Array.of(1, 2)) | |
| 364 | t.alike(state, enc.state(0, 2)) | |
| 365 | enc.raw.uint8array.preencode(state, Uint8Array.of(3, 4)) | |
| 366 | t.alike(state, enc.state(0, 4)) | |
| 367 | ||
| 368 | state.buffer = b4a.alloc(state.end) | |
| 369 | enc.raw.uint8array.encode(state, Uint8Array.of(1, 2)) | |
| 370 | t.alike(state, enc.state(2, 4, b4a.from([1, 2, 0, 0]))) | |
| 371 | enc.raw.uint8array.encode(state, Uint8Array.of(3, 4)) | |
| 372 | t.alike(state, enc.state(4, 4, b4a.from([1, 2, 3, 4]))) | |
| 373 | ||
| 374 | state.start = 0 | |
| 375 | t.alike(enc.raw.uint8array.decode(state), Uint8Array.of(1, 2, 3, 4)) | |
| 376 | t.alike(enc.raw.uint8array.decode(state), Uint8Array.of()) | |
| 377 | }) | |
| 378 | ||
| 379 | test('uint16array', function (t) { | |
| 380 | const state = enc.state() | |
| 381 | ||
| 382 | enc.uint16array.preencode(state, new Uint16Array([1, 2, 3])) | |
| 383 | t.alike(state, enc.state(0, 7)) | |
| 384 | ||
| 385 | state.buffer = b4a.alloc(state.end) | |
| 386 | enc.uint16array.encode(state, new Uint16Array([1, 2, 3])) | |
| 387 | t.alike(state, enc.state(7, 7, b4a.from([3, 1, 0, 2, 0, 3, 0]))) | |
| 388 | ||
| 389 | state.start = 0 | |
| 390 | t.alike(enc.uint16array.decode(state), new Uint16Array([1, 2, 3])) | |
| 391 | t.is(state.start, state.end) | |
| 392 | ||
| 393 | t.exception(() => enc.uint16array.decode(state)) | |
| 394 | }) | |
| 395 | ||
| 396 | test('uint32array', function (t) { | |
| 397 | const state = enc.state() | |
| 398 | ||
| 399 | enc.uint32array.preencode(state, new Uint32Array([1])) | |
| 400 | t.alike(state, enc.state(0, 5)) | |
| 401 | enc.uint32array.preencode(state, new Uint32Array([42, 43])) | |
| 402 | t.alike(state, enc.state(0, 14)) | |
| 403 | ||
| 404 | state.buffer = b4a.alloc(state.end) | |
| 405 | enc.uint32array.encode(state, new Uint32Array([1])) | |
| 406 | t.alike( | |
| 407 | state, | |
| 408 | enc.state(5, 14, b4a.from([1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])) | |
| 409 | ) | |
| 410 | enc.uint32array.encode(state, new Uint32Array([42, 43])) | |
| 411 | t.alike( | |
| 412 | state, | |
| 413 | enc.state(14, 14, b4a.from([1, 1, 0, 0, 0, 2, 42, 0, 0, 0, 43, 0, 0, 0])) | |
| 414 | ) | |
| 415 | ||
| 416 | state.start = 0 | |
| 417 | t.alike(enc.uint32array.decode(state), new Uint32Array([1])) | |
| 418 | t.alike(enc.uint32array.decode(state), new Uint32Array([42, 43])) | |
| 419 | t.is(state.start, state.end) | |
| 420 | ||
| 421 | t.exception(() => enc.uint32array.decode(state)) | |
| 422 | }) | |
| 423 | ||
| 424 | test('int16array', function (t) { | |
| 425 | const state = enc.state() | |
| 426 | ||
| 427 | enc.int16array.preencode(state, new Int16Array([1, -2, 3])) | |
| 428 | t.alike(state, enc.state(0, 7)) | |
| 429 | ||
| 430 | state.buffer = b4a.alloc(state.end) | |
| 431 | enc.int16array.encode(state, new Int16Array([1, -2, 3])) | |
| 432 | t.alike(state, enc.state(7, 7, b4a.from([3, 1, 0, 0xfe, 0xff, 3, 0]))) | |
| 433 | ||
| 434 | state.start = 0 | |
| 435 | t.alike(enc.int16array.decode(state), new Int16Array([1, -2, 3])) | |
| 436 | t.is(state.start, state.end) | |
| 437 | ||
| 438 | t.exception(() => enc.int16array.decode(state)) | |
| 439 | }) | |
| 440 | ||
| 441 | test('int32array', function (t) { | |
| 442 | const state = enc.state() | |
| 443 | ||
| 444 | enc.int32array.preencode(state, new Int32Array([1, -2, 3])) | |
| 445 | t.alike(state, enc.state(0, 13)) | |
| 446 | ||
| 447 | state.buffer = b4a.alloc(state.end) | |
| 448 | enc.int32array.encode(state, new Int32Array([1, -2, 3])) | |
| 449 | t.alike( | |
| 450 | state, | |
| 451 | enc.state( | |
| 452 | 13, | |
| 453 | 13, | |
| 454 | b4a.from([3, 1, 0, 0, 0, 0xfe, 0xff, 0xff, 0xff, 3, 0, 0, 0]) | |
| 455 | ) | |
| 456 | ) | |
| 457 | ||
| 458 | state.start = 0 | |
| 459 | t.alike(enc.int32array.decode(state), new Int32Array([1, -2, 3])) | |
| 460 | t.is(state.start, state.end) | |
| 461 | ||
| 462 | t.exception(() => enc.int32array.decode(state)) | |
| 463 | }) | |
| 464 | ||
| 465 | test('biguint64array', function (t) { | |
| 466 | const state = enc.state() | |
| 467 | ||
| 468 | const arr = new BigUint64Array([0x01020304n, 0x05060708n, 0x090a0b0cn]) | |
| 469 | ||
| 470 | enc.biguint64array.preencode(state, arr) | |
| 471 | t.alike(state, enc.state(0, 25)) | |
| 472 | ||
| 473 | state.buffer = b4a.alloc(state.end) | |
| 474 | enc.biguint64array.encode(state, arr) | |
| 475 | t.alike( | |
| 476 | state, | |
| 477 | enc.state( | |
| 478 | 25, | |
| 479 | 25, | |
| 480 | b4a.from([ | |
| 481 | 3, 0x4, 0x3, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x8, 0x7, 0x6, 0x5, 0x0, 0x0, | |
| 482 | 0x0, 0x0, 0xc, 0xb, 0xa, 0x9, 0x0, 0x0, 0x0, 0x0 | |
| 483 | ]) | |
| 484 | ) | |
| 485 | ) | |
| 486 | ||
| 487 | state.start = 0 | |
| 488 | t.alike(enc.biguint64array.decode(state), arr) | |
| 489 | t.is(state.start, state.end) | |
| 490 | ||
| 491 | t.exception(() => enc.biguint64array.decode(state)) | |
| 492 | }) | |
| 493 | ||
| 494 | test('bigint64array', function (t) { | |
| 495 | const state = enc.state() | |
| 496 | ||
| 497 | const arr = new BigInt64Array([-0x01020304n, 0x05060708n, -0x090a0b0cn]) | |
| 498 | ||
| 499 | enc.bigint64array.preencode(state, arr) | |
| 500 | t.alike(state, enc.state(0, 25)) | |
| 501 | ||
| 502 | state.buffer = b4a.alloc(state.end) | |
| 503 | enc.bigint64array.encode(state, arr) | |
| 504 | t.alike( | |
| 505 | state, | |
| 506 | enc.state( | |
| 507 | 25, | |
| 508 | 25, | |
| 509 | b4a.from([ | |
| 510 | 3, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x8, 0x7, 0x6, 0x5, | |
| 511 | 0x0, 0x0, 0x0, 0x0, 0xf4, 0xf4, 0xf5, 0xf6, 0xff, 0xff, 0xff, 0xff | |
| 512 | ]) | |
| 513 | ) | |
| 514 | ) | |
| 515 | ||
| 516 | state.start = 0 | |
| 517 | t.alike(enc.bigint64array.decode(state), arr) | |
| 518 | t.is(state.start, state.end) | |
| 519 | ||
| 520 | t.exception(() => enc.bigint64array.decode(state)) | |
| 521 | }) | |
| 522 | ||
| 523 | test('float32array', function (t) { | |
| 524 | const state = enc.state() | |
| 525 | ||
| 526 | enc.float32array.preencode(state, new Float32Array([1.1, -2.2, 3.3])) | |
| 527 | t.alike(state, enc.state(0, 13)) | |
| 528 | ||
| 529 | state.buffer = b4a.alloc(state.end) | |
| 530 | enc.float32array.encode(state, new Float32Array([1.1, -2.2, 3.3])) | |
| 531 | t.alike( | |
| 532 | state, | |
| 533 | enc.state( | |
| 534 | 13, | |
| 535 | 13, | |
| 536 | b4a.from([ | |
| 537 | 3, 0xcd, 0xcc, 0x8c, 0x3f, 0xcd, 0xcc, 0x0c, 0xc0, 0x33, 0x33, 0x53, | |
| 538 | 0x40 | |
| 539 | ]) | |
| 540 | ) | |
| 541 | ) | |
| 542 | ||
| 543 | state.start = 0 | |
| 544 | t.alike(enc.float32array.decode(state), new Float32Array([1.1, -2.2, 3.3])) | |
| 545 | t.is(state.start, state.end) | |
| 546 | ||
| 547 | t.exception(() => enc.float32array.decode(state)) | |
| 548 | }) | |
| 549 | ||
| 550 | test('float64array', function (t) { | |
| 551 | const state = enc.state() | |
| 552 | ||
| 553 | enc.float64array.preencode(state, new Float64Array([1.1, -2.2, 3.3])) | |
| 554 | t.alike(state, enc.state(0, 25)) | |
| 555 | ||
| 556 | state.buffer = b4a.alloc(state.end) | |
| 557 | enc.float64array.encode(state, new Float64Array([1.1, -2.2, 3.3])) | |
| 558 | t.alike( | |
| 559 | state, | |
| 560 | enc.state( | |
| 561 | 25, | |
| 562 | 25, | |
| 563 | b4a.from([ | |
| 564 | 3, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0xf1, 0x3f, 0x9a, 0x99, 0x99, | |
| 565 | 0x99, 0x99, 0x99, 0x01, 0xc0, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x0a, | |
| 566 | 0x40 | |
| 567 | ]) | |
| 568 | ) | |
| 569 | ) | |
| 570 | ||
| 571 | state.start = 0 | |
| 572 | t.alike(enc.float64array.decode(state), new Float64Array([1.1, -2.2, 3.3])) | |
| 573 | t.is(state.start, state.end) | |
| 574 | ||
| 575 | t.exception(() => enc.float64array.decode(state)) | |
| 576 | }) | |
| 577 | ||
| 578 | test('string', function (t) { | |
| 579 | const state = enc.state() | |
| 580 | ||
| 581 | enc.string.preencode(state, '🌾') | |
| 582 | t.alike(state, enc.state(0, 5)) | |
| 583 | enc.string.preencode(state, 'høsten er fin') | |
| 584 | t.alike(state, enc.state(0, 20)) | |
| 585 | ||
| 586 | state.buffer = b4a.alloc(state.end) | |
| 587 | enc.string.encode(state, '🌾') | |
| 588 | t.alike( | |
| 589 | state, | |
| 590 | enc.state( | |
| 591 | 5, | |
| 592 | 20, | |
| 593 | b4a.from( | |
| 594 | '\x04🌾\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' | |
| 595 | ) | |
| 596 | ) | |
| 597 | ) | |
| 598 | enc.string.encode(state, 'høsten er fin') | |
| 599 | t.alike(state, enc.state(20, 20, b4a.from('\x04🌾\x0ehøsten er fin'))) | |
| 600 | ||
| 601 | state.start = 0 | |
| 602 | t.is(enc.string.decode(state), '🌾') | |
| 603 | t.is(enc.string.decode(state), 'høsten er fin') | |
| 604 | t.is(state.start, state.end) | |
| 605 | ||
| 606 | t.exception(() => enc.string.decode(state)) | |
| 607 | }) | |
| 608 | ||
| 609 | test('raw string', function (t) { | |
| 610 | const state = enc.state() | |
| 611 | ||
| 612 | enc.raw.string.preencode(state, 'hello') | |
| 613 | t.alike(state, enc.state(0, 5)) | |
| 614 | enc.raw.string.preencode(state, ' world') | |
| 615 | t.alike(state, enc.state(0, 11)) | |
| 616 | ||
| 617 | state.buffer = b4a.alloc(state.end) | |
| 618 | enc.raw.string.encode(state, 'hello') | |
| 619 | enc.raw.string.encode(state, ' world') | |
| 620 | t.alike(state, enc.state(11, 11, b4a.from('hello world'))) | |
| 621 | ||
| 622 | state.start = 0 | |
| 623 | t.is(enc.raw.string.decode(state), 'hello world') | |
| 624 | t.is(enc.raw.string.decode(state), '') | |
| 625 | }) | |
| 626 | ||
| 627 | test('fixed32', function (t) { | |
| 628 | const state = enc.state() | |
| 629 | ||
| 630 | enc.fixed32.preencode(state, b4a.alloc(32).fill('a')) | |
| 631 | t.alike(state, enc.state(0, 32)) | |
| 632 | enc.fixed32.preencode(state, b4a.alloc(32).fill('b')) | |
| 633 | t.alike(state, enc.state(0, 64)) | |
| 634 | ||
| 635 | state.buffer = b4a.alloc(state.end) | |
| 636 | enc.fixed32.encode(state, b4a.alloc(32).fill('a')) | |
| 637 | t.alike(state, enc.state(32, 64, b4a.alloc(64).fill('a', 0, 32))) | |
| 638 | enc.fixed32.encode(state, b4a.alloc(32).fill('b')) | |
| 639 | t.alike( | |
| 640 | state, | |
| 641 | enc.state(64, 64, b4a.alloc(64).fill('a', 0, 32).fill('b', 32, 64)) | |
| 642 | ) | |
| 643 | ||
| 644 | state.start = 0 | |
| 645 | t.alike(enc.fixed32.decode(state), b4a.alloc(32).fill('a')) | |
| 646 | t.alike(enc.fixed32.decode(state), b4a.alloc(32).fill('b')) | |
| 647 | t.is(state.start, state.end) | |
| 648 | ||
| 649 | t.exception(() => enc.fixed32.decode(state)) | |
| 650 | }) | |
| 651 | ||
| 652 | test('fixed64', function (t) { | |
| 653 | const state = enc.state() | |
| 654 | ||
| 655 | enc.fixed64.preencode(state, b4a.alloc(64).fill('a')) | |
| 656 | t.alike(state, enc.state(0, 64)) | |
| 657 | enc.fixed64.preencode(state, b4a.alloc(64).fill('b')) | |
| 658 | t.alike(state, enc.state(0, 128)) | |
| 659 | ||
| 660 | state.buffer = b4a.alloc(state.end) | |
| 661 | enc.fixed64.encode(state, b4a.alloc(64).fill('a')) | |
| 662 | t.alike(state, enc.state(64, 128, b4a.alloc(128).fill('a', 0, 64))) | |
| 663 | enc.fixed64.encode(state, b4a.alloc(64).fill('b')) | |
| 664 | t.alike( | |
| 665 | state, | |
| 666 | enc.state(128, 128, b4a.alloc(128).fill('a', 0, 64).fill('b', 64, 128)) | |
| 667 | ) | |
| 668 | ||
| 669 | state.start = 0 | |
| 670 | t.alike(enc.fixed64.decode(state), b4a.alloc(64).fill('a')) | |
| 671 | t.alike(enc.fixed64.decode(state), b4a.alloc(64).fill('b')) | |
| 672 | t.is(state.start, state.end) | |
| 673 | ||
| 674 | t.exception(() => enc.fixed64.decode(state)) | |
| 675 | }) | |
| 676 | ||
| 677 | test('fixed n', function (t) { | |
| 678 | const state = enc.state() | |
| 679 | const fixed = enc.fixed(3) | |
| 680 | ||
| 681 | fixed.preencode(state, b4a.alloc(3).fill('a')) | |
| 682 | t.alike(state, enc.state(0, 3)) | |
| 683 | fixed.preencode(state, b4a.alloc(3).fill('b')) | |
| 684 | t.alike(state, enc.state(0, 6)) | |
| 685 | ||
| 686 | state.buffer = b4a.alloc(state.end) | |
| 687 | fixed.encode(state, b4a.alloc(3).fill('a')) | |
| 688 | t.alike(state, enc.state(3, 6, b4a.alloc(6).fill('a', 0, 3))) | |
| 689 | fixed.encode(state, b4a.alloc(3).fill('b')) | |
| 690 | t.alike(state, enc.state(6, 6, b4a.alloc(6).fill('a', 0, 3).fill('b', 3, 6))) | |
| 691 | ||
| 692 | state.start = 0 | |
| 693 | t.alike(fixed.decode(state), b4a.alloc(3).fill('a')) | |
| 694 | t.alike(fixed.decode(state), b4a.alloc(3).fill('b')) | |
| 695 | t.is(state.start, state.end) | |
| 696 | ||
| 697 | t.exception(() => fixed.decode(state)) | |
| 698 | state.start = 4 | |
| 699 | t.exception(() => fixed.decode(state)) | |
| 700 | }) | |
| 701 | ||
| 702 | test('error for incorrect buffer sizes when encoding fixed-length buffers', function (t) { | |
| 703 | const smallbuf = b4a.from('aa', 'hex') | |
| 704 | const bigBuf = b4a.from('aa'.repeat(500), 'hex') | |
| 705 | ||
| 706 | t.exception(() => enc.encode(enc.fixed32, smallbuf), /Incorrect buffer size/) | |
| 707 | t.exception(() => enc.encode(enc.fixed64, smallbuf), /Incorrect buffer size/) | |
| 708 | t.exception( | |
| 709 | () => enc.encode(enc.fixed(100), smallbuf), | |
| 710 | /Incorrect buffer size/ | |
| 711 | ) | |
| 712 | ||
| 713 | t.exception(() => enc.encode(enc.fixed32, bigBuf), /Incorrect buffer size/) | |
| 714 | t.exception(() => enc.encode(enc.fixed64, bigBuf), /Incorrect buffer size/) | |
| 715 | t.exception(() => enc.encode(enc.fixed(100), bigBuf), /Incorrect buffer size/) | |
| 716 | }) | |
| 717 | ||
| 718 | test('array', function (t) { | |
| 719 | const state = enc.state() | |
| 720 | const arr = enc.array(enc.bool) | |
| 721 | ||
| 722 | arr.preencode(state, [true, false, true]) | |
| 723 | t.alike(state, enc.state(0, 4)) | |
| 724 | arr.preencode(state, [false, false, true, true]) | |
| 725 | t.alike(state, enc.state(0, 9)) | |
| 726 | ||
| 727 | state.buffer = b4a.alloc(state.end) | |
| 728 | arr.encode(state, [true, false, true]) | |
| 729 | t.alike(state, enc.state(4, 9, b4a.from([3, 1, 0, 1, 0, 0, 0, 0, 0]))) | |
| 730 | arr.encode(state, [false, false, true, true]) | |
| 731 | t.alike(state, enc.state(9, 9, b4a.from([3, 1, 0, 1, 4, 0, 0, 1, 1]))) | |
| 732 | ||
| 733 | state.start = 0 | |
| 734 | t.alike(arr.decode(state), [true, false, true]) | |
| 735 | t.alike(arr.decode(state), [false, false, true, true]) | |
| 736 | t.is(state.start, state.end) | |
| 737 | ||
| 738 | t.exception(() => arr.decode(state)) | |
| 739 | }) | |
| 740 | ||
| 741 | test('raw array', function (t) { | |
| 742 | const state = enc.state() | |
| 743 | const arr = enc.raw.array(enc.bool) | |
| 744 | ||
| 745 | arr.preencode(state, [true]) | |
| 746 | t.alike(state, enc.state(0, 1)) | |
| 747 | arr.preencode(state, [true]) | |
| 748 | t.alike(state, enc.state(0, 2)) | |
| 749 | ||
| 750 | state.buffer = b4a.alloc(state.end) | |
| 751 | arr.encode(state, [true]) | |
| 752 | t.alike(state, enc.state(1, 2, b4a.from([1, 0]))) | |
| 753 | arr.encode(state, [true]) | |
| 754 | t.alike(state, enc.state(2, 2, b4a.from([1, 1]))) | |
| 755 | ||
| 756 | state.start = 0 | |
| 757 | t.alike(arr.decode(state), [true, true]) | |
| 758 | t.alike(arr.decode(state), []) | |
| 759 | }) | |
| 760 | ||
| 761 | test('json', function (t) { | |
| 762 | const state = enc.state() | |
| 763 | ||
| 764 | enc.json.preencode(state, { a: 1, b: 2 }) | |
| 765 | t.alike(state, enc.state(0, 14)) | |
| 766 | ||
| 767 | state.buffer = b4a.alloc(state.end) | |
| 768 | enc.json.encode(state, { a: 1, b: 2 }) | |
| 769 | t.alike( | |
| 770 | state, | |
| 771 | enc.state(14, 14, b4a.concat([b4a.from([13]), b4a.from('{"a":1,"b":2}')])) | |
| 772 | ) | |
| 773 | ||
| 774 | state.start = 0 | |
| 775 | t.alike(enc.json.decode(state), { a: 1, b: 2 }) | |
| 776 | ||
| 777 | t.exception(() => enc.json.decode(state)) | |
| 778 | }) | |
| 779 | ||
| 780 | test('raw json', function (t) { | |
| 781 | const state = enc.state() | |
| 782 | ||
| 783 | enc.raw.json.preencode(state, { a: 1, b: 2 }) | |
| 784 | t.alike(state, enc.state(0, 13)) | |
| 785 | ||
| 786 | state.buffer = b4a.alloc(state.end) | |
| 787 | enc.raw.json.encode(state, { a: 1, b: 2 }) | |
| 788 | t.alike(state, enc.state(13, 13, b4a.from('{"a":1,"b":2}'))) | |
| 789 | ||
| 790 | state.start = 0 | |
| 791 | t.alike(enc.raw.json.decode(state), { a: 1, b: 2 }) | |
| 792 | ||
| 793 | t.exception(() => enc.json.decode(state)) | |
| 794 | }) | |
| 795 | ||
| 796 | test('lexint: big numbers', function (t) { | |
| 797 | t.plan(1) | |
| 798 | ||
| 799 | let prev = enc.encode(enc.lexint, 0) | |
| 800 | ||
| 801 | let n | |
| 802 | let skip = 1 | |
| 803 | ||
| 804 | for (n = 1; n < Number.MAX_VALUE; n += skip) { | |
| 805 | const cur = enc.encode(enc.lexint, n) | |
| 806 | if (b4a.compare(cur, prev) < 1) break | |
| 807 | prev = cur | |
| 808 | skip = 1 + Math.pow(245, Math.ceil(Math.log(n) / Math.log(256))) | |
| 809 | } | |
| 810 | t.is(n, Infinity) | |
| 811 | }) | |
| 812 | ||
| 813 | test('lexint: range precision', function (t) { | |
| 814 | t.plan(2) | |
| 815 | const a = 1e55 | |
| 816 | const b = 1.0000000000001e55 | |
| 817 | const ha = enc.encode(enc.lexint, a).toString('hex') | |
| 818 | const hb = enc.encode(enc.lexint, b).toString('hex') | |
| 819 | t.not(a, b) | |
| 820 | t.not(ha, hb) | |
| 821 | }) | |
| 822 | ||
| 823 | test('lexint: range precision', function (t) { | |
| 824 | let prev = enc.encode(enc.lexint, 0) | |
| 825 | const skip = 0.000000001e55 | |
| 826 | for (let i = 0, n = 1e55; i < 1000; n = 1e55 + skip * ++i) { | |
| 827 | const cur = enc.encode(enc.lexint, n) | |
| 828 | if (b4a.compare(cur, prev) < 1) t.fail('cur <= prev') | |
| 829 | prev = cur | |
| 830 | } | |
| 831 | t.ok(true) | |
| 832 | t.end() | |
| 833 | }) | |
| 834 | ||
| 835 | test('lexint: small numbers', function (t) { | |
| 836 | let prev = enc.encode(enc.lexint, 0) | |
| 837 | for (let n = 1; n < 256 * 256 * 16; n++) { | |
| 838 | const cur = enc.encode(enc.lexint, n) | |
| 839 | if (b4a.compare(cur, prev) < 1) t.fail('cur <= prev') | |
| 840 | prev = cur | |
| 841 | } | |
| 842 | t.ok(true) | |
| 843 | t.end() | |
| 844 | }) | |
| 845 | ||
| 846 | test('lexint: throws', function (t) { | |
| 847 | t.exception(() => { | |
| 848 | enc.decode(enc.lexint, b4a.alloc(1, 251)) | |
| 849 | }) | |
| 850 | ||
| 851 | let num = 252 | |
| 852 | ||
| 853 | const state = enc.state() | |
| 854 | ||
| 855 | enc.lexint.preencode(state, num) | |
| 856 | state.buffer = b4a.alloc(state.end - state.start) | |
| 857 | enc.lexint.encode(state, num) | |
| 858 | ||
| 859 | t.exception(() => { | |
| 860 | enc.decode( | |
| 861 | enc.lexint, | |
| 862 | state.buffer.subarray(0, state.buffer.byteLength - 2) | |
| 863 | ) | |
| 864 | }) | |
| 865 | ||
| 866 | num <<= 8 | |
| 867 | ||
| 868 | state.start = 0 | |
| 869 | state.end = 0 | |
| 870 | state.buffer = null | |
| 871 | ||
| 872 | enc.lexint.preencode(state, num) | |
| 873 | state.buffer = b4a.alloc(state.end - state.start) | |
| 874 | enc.lexint.encode(state, num) | |
| 875 | ||
| 876 | t.exception(() => { | |
| 877 | enc.decode( | |
| 878 | enc.lexint, | |
| 879 | state.buffer.subarray(0, state.buffer.byteLength - 2) | |
| 880 | ) | |
| 881 | }) | |
| 882 | ||
| 883 | num <<= 8 | |
| 884 | ||
| 885 | state.start = 0 | |
| 886 | state.end = 0 | |
| 887 | state.buffer = null | |
| 888 | ||
| 889 | enc.lexint.preencode(state, num) | |
| 890 | state.buffer = b4a.alloc(state.end - state.start) | |
| 891 | enc.lexint.encode(state, num) | |
| 892 | ||
| 893 | t.exception(() => { | |
| 894 | enc.decode( | |
| 895 | enc.lexint, | |
| 896 | state.buffer.subarray(0, state.buffer.byteLength - 2) | |
| 897 | ) | |
| 898 | }) | |
| 899 | ||
| 900 | num *= 256 | |
| 901 | ||
| 902 | state.start = 0 | |
| 903 | state.end = 0 | |
| 904 | state.buffer = null | |
| 905 | ||
| 906 | enc.lexint.preencode(state, num) | |
| 907 | state.buffer = b4a.alloc(state.end - state.start) | |
| 908 | enc.lexint.encode(state, num) | |
| 909 | ||
| 910 | t.exception(() => { | |
| 911 | enc.decode( | |
| 912 | enc.lexint, | |
| 913 | state.buffer.subarray(0, state.buffer.byteLength - 2) | |
| 914 | ) | |
| 915 | }) | |
| 916 | ||
| 917 | num *= 256 * 256 | |
| 918 | ||
| 919 | state.start = 0 | |
| 920 | state.end = 0 | |
| 921 | state.buffer = null | |
| 922 | ||
| 923 | enc.lexint.preencode(state, num) | |
| 924 | state.buffer = b4a.alloc(state.end - state.start) | |
| 925 | enc.lexint.encode(state, num) | |
| 926 | ||
| 927 | t.exception(() => { | |
| 928 | enc.decode( | |
| 929 | enc.lexint, | |
| 930 | state.buffer.subarray(0, state.buffer.byteLength - 2) | |
| 931 | ) | |
| 932 | }) | |
| 933 | ||
| 934 | t.end() | |
| 935 | }) | |
| 936 | ||
| 937 | test('lexint: unpack', function (t) { | |
| 938 | let n | |
| 939 | let skip = 1 | |
| 940 | ||
| 941 | for (n = 1; n < Number.MAX_VALUE; n += skip) { | |
| 942 | const cur = enc.encode(enc.lexint, n) | |
| 943 | compare(n, enc.decode(enc.lexint, cur)) | |
| 944 | skip = 1 + Math.pow(245, Math.ceil(Math.log(n) / Math.log(256))) | |
| 945 | } | |
| 946 | t.is(n, Infinity) | |
| 947 | t.end() | |
| 948 | ||
| 949 | function compare(a, b) { | |
| 950 | const desc = a + ' !=~ ' + b | |
| 951 | if (/e\+\d+$/.test(a) || /e\+\d+$/.test(b)) { | |
| 952 | if ( | |
| 953 | String(a).slice(0, 8) !== String(b).slice(0, 8) || | |
| 954 | /e\+(\d+)$/.exec(a)[1] !== /e\+(\d+)$/.exec(b)[1] | |
| 955 | ) { | |
| 956 | t.fail(desc) | |
| 957 | } | |
| 958 | } else { | |
| 959 | if ( | |
| 960 | String(a).slice(0, 8) !== String(b).slice(0, 8) || | |
| 961 | String(a).length !== String(b).length | |
| 962 | ) { | |
| 963 | t.fail(desc) | |
| 964 | } | |
| 965 | } | |
| 966 | } | |
| 967 | }) | |
| 968 | ||
| 969 | test('date', function (t) { | |
| 970 | const d = new Date() | |
| 971 | ||
| 972 | t.alike(enc.decode(enc.date, enc.encode(enc.date, d)), d) | |
| 973 | }) | |
| 974 | ||
| 975 | test('any', function (t) { | |
| 976 | const o = { | |
| 977 | hello: 'world', | |
| 978 | num: 42, | |
| 979 | neg: -42, | |
| 980 | arr: [{ yes: 1 }, { no: false }], | |
| 981 | nest: {}, | |
| 982 | today: new Date(), | |
| 983 | float: 0.54 | |
| 984 | } | |
| 985 | ||
| 986 | t.alike(enc.decode(enc.any, enc.encode(enc.any, o)), o) | |
| 987 | ||
| 988 | const arr = new Array(3) | |
| 989 | ||
| 990 | t.alike(enc.decode(enc.any, enc.encode(enc.any, arr)), [null, null, null]) | |
| 991 | }) | |
| 992 | ||
| 993 | test('framed', function (t) { | |
| 994 | const e = enc.frame(enc.uint) | |
| 995 | t.alike(enc.encode(e, 42), b4a.from([0x01, 0x2a])) | |
| 996 | t.alike(enc.decode(e, b4a.from([0x01, 0x2a])), 42) | |
| 997 | t.alike(enc.encode(e, 4200), b4a.from([0x03, 0xfd, 0x68, 0x10])) | |
| 998 | t.alike(enc.decode(e, b4a.from([0x03, 0xfd, 0x68, 0x10])), 4200) | |
| 999 | }) | |
| 1000 | ||
| 1001 | test('port', function (t) { | |
| 1002 | const p = 0x1234 | |
| 1003 | const buf = Buffer.from([0x34, 0x12]) | |
| 1004 | ||
| 1005 | t.alike(enc.encode(enc.port, p), buf) | |
| 1006 | t.alike(enc.decode(enc.port, buf), p) | |
| 1007 | }) | |
| 1008 | ||
| 1009 | test('ipv4', function (t) { | |
| 1010 | const ip = '1.2.3.4' | |
| 1011 | const buf = Buffer.from([1, 2, 3, 4]) | |
| 1012 | ||
| 1013 | t.alike(enc.encode(enc.ipv4, ip), buf) | |
| 1014 | t.alike(enc.decode(enc.ipv4, buf), ip) | |
| 1015 | }) | |
| 1016 | ||
| 1017 | test('ipv4 + port', function (t) { | |
| 1018 | const host = '1.2.3.4' | |
| 1019 | const port = 1234 | |
| 1020 | ||
| 1021 | t.alike( | |
| 1022 | enc.decode(enc.ipv4Address, enc.encode(enc.ipv4Address, { host, port })), | |
| 1023 | { | |
| 1024 | host, | |
| 1025 | family: 4, | |
| 1026 | port | |
| 1027 | } | |
| 1028 | ) | |
| 1029 | }) | |
| 1030 | ||
| 1031 | test('ipv6', function (t) { | |
| 1032 | const ip = '1:2:3:4:5:6:7:8' | |
| 1033 | const buf = Buffer.from([0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8]) | |
| 1034 | ||
| 1035 | t.alike(enc.encode(enc.ipv6, ip), buf) | |
| 1036 | t.alike(enc.decode(enc.ipv6, buf), ip) | |
| 1037 | ||
| 1038 | t.test('abbreviated', function (t) { | |
| 1039 | const buf = Buffer.from([0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 8]) | |
| 1040 | ||
| 1041 | t.alike(enc.encode(enc.ipv6, '1:2::7:8'), buf) | |
| 1042 | t.alike(enc.decode(enc.ipv6, buf), '1:2:0:0:0:0:7:8') | |
| 1043 | }) | |
| 1044 | ||
| 1045 | t.test('prefix abbreviated', function (t) { | |
| 1046 | const buf = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, 7, 0, 8]) | |
| 1047 | ||
| 1048 | t.alike(enc.encode(enc.ipv6, '::5:6:7:8'), buf) | |
| 1049 | t.alike(enc.decode(enc.ipv6, buf), '0:0:0:0:5:6:7:8') | |
| 1050 | }) | |
| 1051 | ||
| 1052 | t.test('suffix abbreviated', function (t) { | |
| 1053 | const buf = Buffer.from([0, 1, 0, 2, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0]) | |
| 1054 | ||
| 1055 | t.alike(enc.encode(enc.ipv6, '1:2:3:4::'), buf) | |
| 1056 | t.alike(enc.decode(enc.ipv6, buf), '1:2:3:4:0:0:0:0') | |
| 1057 | }) | |
| 1058 | ||
| 1059 | t.test('any', function (t) { | |
| 1060 | const buf = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) | |
| 1061 | ||
| 1062 | t.alike(enc.encode(enc.ipv6, '::'), buf) | |
| 1063 | t.alike(enc.decode(enc.ipv6, buf), '0:0:0:0:0:0:0:0') | |
| 1064 | }) | |
| 1065 | ||
| 1066 | t.test('lowercase hex', function (t) { | |
| 1067 | const buf = Buffer.from([ | |
| 1068 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xab, 0xcd | |
| 1069 | ]) | |
| 1070 | ||
| 1071 | t.alike(enc.encode(enc.ipv6, '::abcd'), buf) | |
| 1072 | t.alike(enc.decode(enc.ipv6, buf), '0:0:0:0:0:0:0:abcd') | |
| 1073 | }) | |
| 1074 | ||
| 1075 | t.test('uppercase hex', function (t) { | |
| 1076 | const buf = Buffer.from([ | |
| 1077 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xab, 0xcd | |
| 1078 | ]) | |
| 1079 | ||
| 1080 | t.alike(enc.encode(enc.ipv6, '::ABCD'), buf) | |
| 1081 | t.alike(enc.decode(enc.ipv6, buf), '0:0:0:0:0:0:0:abcd') | |
| 1082 | }) | |
| 1083 | }) | |
| 1084 | ||
| 1085 | test('ipv6 + port', function (t) { | |
| 1086 | const host = '1:2:3:4:5:6:7:8' | |
| 1087 | const port = 1234 | |
| 1088 | ||
| 1089 | t.alike( | |
| 1090 | enc.decode(enc.ipv6Address, enc.encode(enc.ipv6Address, { host, port })), | |
| 1091 | { | |
| 1092 | host, | |
| 1093 | family: 6, | |
| 1094 | port | |
| 1095 | } | |
| 1096 | ) | |
| 1097 | }) | |
| 1098 | ||
| 1099 | test('dual ip', function (t) { | |
| 1100 | { | |
| 1101 | const host = '1.2.3.4' | |
| 1102 | ||
| 1103 | t.alike(enc.decode(enc.ip, enc.encode(enc.ip, host)), host, 'ipv4') | |
| 1104 | } | |
| 1105 | { | |
| 1106 | const host = '1:2:3:4:5:6:7:8' | |
| 1107 | ||
| 1108 | t.alike(enc.decode(enc.ip, enc.encode(enc.ip, host)), host, 'ipv6') | |
| 1109 | } | |
| 1110 | }) | |
| 1111 | ||
| 1112 | test('dual ip + port', function (t) { | |
| 1113 | const port = 1234 | |
| 1114 | ||
| 1115 | { | |
| 1116 | const host = '1.2.3.4' | |
| 1117 | ||
| 1118 | t.alike( | |
| 1119 | enc.decode(enc.ipAddress, enc.encode(enc.ipAddress, { host, port })), | |
| 1120 | { | |
| 1121 | host, | |
| 1122 | family: 4, | |
| 1123 | port | |
| 1124 | }, | |
| 1125 | 'ipv4' | |
| 1126 | ) | |
| 1127 | } | |
| 1128 | { | |
| 1129 | const host = '1:2:3:4:5:6:7:8' | |
| 1130 | ||
| 1131 | t.alike( | |
| 1132 | enc.decode(enc.ipAddress, enc.encode(enc.ipAddress, { host, port })), | |
| 1133 | { | |
| 1134 | host, | |
| 1135 | family: 6, | |
| 1136 | port | |
| 1137 | }, | |
| 1138 | 'ipv6' | |
| 1139 | ) | |
| 1140 | } | |
| 1141 | }) | |
| 1142 | ||
| 1143 | test('record', function (t) { | |
| 1144 | const encoding = enc.record(enc.string, enc.string) | |
| 1145 | ||
| 1146 | t.alike( | |
| 1147 | enc.decode(encoding, enc.encode(encoding, { a: 'hello', b: 'world' })), | |
| 1148 | Object.assign(Object.create(null), { | |
| 1149 | a: 'hello', | |
| 1150 | b: 'world' | |
| 1151 | }) | |
| 1152 | ) | |
| 1153 | }) | |
| 1154 | ||
| 1155 | test('record - nested', function (t) { | |
| 1156 | const encoding = enc.record(enc.string, enc.record(enc.string, enc.string)) | |
| 1157 | ||
| 1158 | t.alike( | |
| 1159 | enc.decode( | |
| 1160 | encoding, | |
| 1161 | enc.encode(encoding, { | |
| 1162 | a: { b: 'record' }, | |
| 1163 | c: { d: 'nested', e: 'test' } | |
| 1164 | }) | |
| 1165 | ), | |
| 1166 | Object.assign(Object.create(null), { | |
| 1167 | a: Object.assign(Object.create(null), { b: 'record' }), | |
| 1168 | c: Object.assign(Object.create(null), { d: 'nested', e: 'test' }) | |
| 1169 | }) | |
| 1170 | ) | |
| 1171 | }) | |
| 1172 | ||
| 1173 | test('stringRecord', function (t) { | |
| 1174 | t.alike( | |
| 1175 | enc.decode( | |
| 1176 | enc.stringRecord, | |
| 1177 | enc.encode(enc.stringRecord, { a: 'hello', b: 'world' }) | |
| 1178 | ), | |
| 1179 | Object.assign(Object.create(null), { | |
| 1180 | a: 'hello', | |
| 1181 | b: 'world' | |
| 1182 | }) | |
| 1183 | ) | |
| 1184 | }) |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2023 Holepunch Inc | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,109 @@ |
| 1 | # Corestore | |
| 2 | ||
| 3 | ### [See the full API docs at docs.holepunch.to](https://docs.holepunch.to/helpers/corestore) | |
| 4 | ||
| 5 | Corestore is a Hypercore factory that makes it easier to manage large collections of named Hypercores. | |
| 6 | ||
| 7 | Corestore provides: | |
| 8 | 1. __Key Derivation__ - All writable Hypercore keys are derived from a single master key and a user-provided name. | |
| 9 | 2. __Session Handling__ - If a single Hypercore is loaded multiple times through the `get` method, the underlying resources will only be opened once (using Hypercore 10's new session feature). Once all sessions are closed, the resources will be released. | |
| 10 | 3. __Storage Management__ - Hypercores can be stored in any random-access-storage instance, where they will be keyed by their discovery keys. | |
| 11 | 4. __Namespacing__ - You can share a single Corestore instance between multiple applications or components without worrying about naming collisions by creating "namespaces" (e.g. `corestore.namespace('my-app').get({ name: 'main' })`) | |
| 12 | ||
| 13 | ### Installation | |
| 14 | `npm install corestore` | |
| 15 | ||
| 16 | ### Usage | |
| 17 | A corestore instance can be constructed with a random-access-storage module, a function that returns a random-access-storage module given a path, or a string. If a string is specified, it will be assumed to be a path to a local storage directory: | |
| 18 | ```js | |
| 19 | const Corestore = require('corestore') | |
| 20 | ||
| 21 | const store = new Corestore('./my-storage') | |
| 22 | const core1 = store.get({ name: 'core-1' }) | |
| 23 | const core2 = store.get({ name: 'core-2' }) | |
| 24 | ``` | |
| 25 | ||
| 26 | ### API | |
| 27 | #### `const store = new Corestore(storage, opts = {})` | |
| 28 | Create a new Corestore instance. | |
| 29 | ||
| 30 | `storage` can be either a random-access-storage module, a string, or a function that takes a path and returns an random-access-storage instance. | |
| 31 | ||
| 32 | Opts include: | |
| 33 | ```js | |
| 34 | { | |
| 35 | inflightRange: null // Advanced option. Forwarded to the Hypercores created by corestore.get(...) | |
| 36 | } | |
| 37 | ``` | |
| 38 | ||
| 39 | #### `const core = store.get(key | { name: 'a-name', exclusive, ...hypercoreOpts})` | |
| 40 | Loads a Hypercore, either by name (if the `name` option is provided), or from the provided key (if the first argument is a Buffer or String with hex/z32 key, or if the `key` options is set). | |
| 41 | ||
| 42 | If that Hypercore has previously been loaded, subsequent calls to `get` will return a new Hypercore session on the existing core. | |
| 43 | ||
| 44 | If you set the `exclusive` option and you are opening a writable session it will wait for all other exclusive writable to close before | |
| 45 | opening the Hypercore effectively meaning any op on the core will wait until its exclusive. | |
| 46 | ||
| 47 | All other options besides `name` and `key` and `exclusive` will be forwarded to the Hypercore constructor. | |
| 48 | ||
| 49 | #### `const stream = store.replicate(optsOrStream)` | |
| 50 | Creates a replication stream that's capable of replicating all Hypercores that are managed by the Corestore, assuming the remote peer has the correct capabilities. | |
| 51 | ||
| 52 | `opts` will be forwarded to Hypercore's `replicate` function. | |
| 53 | ||
| 54 | Corestore replicates in an "all-to-all" fashion, meaning that when replication begins, it will attempt to replicate every Hypercore that's currently loaded and in memory. These attempts will fail if the remote side doesn't have a Hypercore's capability -- Corestore replication does not exchange Hypercore keys. | |
| 55 | ||
| 56 | If the remote side dynamically adds a new Hypercore to the replication stream, Corestore will load and replicate that core if possible. | |
| 57 | ||
| 58 | Using [Hyperswarm](https://github.com/holepunchto/hyperswarm) you can easily replicate corestores | |
| 59 | ||
| 60 | ``` js | |
| 61 | const swarm = new Hyperswarm() | |
| 62 | ||
| 63 | // join the relevant topic | |
| 64 | swarm.join(...) | |
| 65 | ||
| 66 | // simply pass the connection stream to corestore | |
| 67 | swarm.on('connection', (connection) => store.replicate(connection)) | |
| 68 | ``` | |
| 69 | ||
| 70 | #### `const store = store.namespace(name)` | |
| 71 | Create a new namespaced Corestore. Namespacing is useful if you're going to be sharing a single Corestore instance between many applications or components, as it prevents name collisions. | |
| 72 | ||
| 73 | Namespaces can be chained: | |
| 74 | ```js | |
| 75 | const ns1 = store.namespace('a') | |
| 76 | const ns2 = ns1.namespace('b') | |
| 77 | const core1 = ns1.get({ name: 'main' }) // These will load different Hypercores | |
| 78 | const core2 = ns2.get({ name: 'main' }) | |
| 79 | ``` | |
| 80 | ||
| 81 | #### `const storeB = storeA.session(opts)` | |
| 82 | Create a new Corestore that shares resources with the original, like cache, cores, replication streams, and storage, while optionally resetting the namespace, overriding `primaryKey`. | |
| 83 | Useful when an application wants to accept an optional Corestore, but needs to maintain a predictable key derivation. | |
| 84 | ||
| 85 | `opts` are the same as the constructor options: | |
| 86 | ||
| 87 | ```js | |
| 88 | { | |
| 89 | primaryKey, // Overrides the primaryKey for this session | |
| 90 | namespace, // If set to null it will reset to the DEFAULT_NAMESPACE | |
| 91 | detach: true, // By disabling this, closing the session will also close the store that created the session | |
| 92 | inflightRange: null // Advanced option. Forwarded to the Hypercores created by `session.get(...) | |
| 93 | } | |
| 94 | ``` | |
| 95 | ||
| 96 | #### `await store.close()` | |
| 97 | Fully close this Corestore instance. | |
| 98 | ||
| 99 | #### `store.on('core-open', core)` | |
| 100 | Emitted when the first session for a core is opened. | |
| 101 | ||
| 102 | *Note: This core may close at any time, so treat it as a weak reference* | |
| 103 | ||
| 104 | #### `store.on('core-close', core)` | |
| 105 | Emitted when the last session for a core is closed. | |
| 106 | ||
| 107 | ### License | |
| 108 | MIT | |
| 109 |
| … | … | @@ -0,0 +1,591 @@ |
| 1 | const safetyCatch = require('safety-catch') | |
| 2 | const crypto = require('hypercore-crypto') | |
| 3 | const sodium = require('sodium-universal') | |
| 4 | const Hypercore = require('hypercore') | |
| 5 | const hypercoreId = require('hypercore-id-encoding') | |
| 6 | const Xache = require('xache') | |
| 7 | const b4a = require('b4a') | |
| 8 | const ReadyResource = require('ready-resource') | |
| 9 | const RW = require('read-write-mutexify') | |
| 10 | ||
| 11 | const [NS] = crypto.namespace('corestore', 1) | |
| 12 | const DEFAULT_NAMESPACE = b4a.alloc(32) // This is meant to be 32 0-bytes | |
| 13 | ||
| 14 | const CORES_DIR = 'cores' | |
| 15 | const PRIMARY_KEY_FILE_NAME = 'primary-key' | |
| 16 | const USERDATA_NAME_KEY = 'corestore/name' | |
| 17 | const USERDATA_NAMESPACE_KEY = 'corestore/namespace' | |
| 18 | const POOL_SIZE = 512 // how many open fds to aim for before cycling them | |
| 19 | const DEFAULT_MANIFEST = 0 // bump to 1 when this is more widely deployed | |
| 20 | const DEFAULT_COMPAT = true | |
| 21 | ||
| 22 | module.exports = class Corestore extends ReadyResource { | |
| 23 | constructor (storage, opts = {}) { | |
| 24 | super() | |
| 25 | ||
| 26 | const root = opts._root | |
| 27 | ||
| 28 | this.storage = Hypercore.defaultStorage(storage, { lock: PRIMARY_KEY_FILE_NAME, poolSize: opts.poolSize || POOL_SIZE, rmdir: true }) | |
| 29 | this.cores = root ? root.cores : new Map() | |
| 30 | this.cache = !!opts.cache | |
| 31 | this.primaryKey = opts.primaryKey || null | |
| 32 | this.passive = !!opts.passive | |
| 33 | this.manifestVersion = typeof opts.manifestVersion === 'number' ? opts.manifestVersion : (root ? root.manifestVersion : DEFAULT_MANIFEST) | |
| 34 | this.compat = typeof opts.compat === 'boolean' ? opts.compat : (root ? root.compat : DEFAULT_COMPAT) | |
| 35 | this.inflightRange = opts.inflightRange || null | |
| 36 | this.globalCache = opts.globalCache || null | |
| 37 | ||
| 38 | this._keyStorage = null | |
| 39 | this._bootstrap = opts._bootstrap || null | |
| 40 | this._namespace = opts.namespace || DEFAULT_NAMESPACE | |
| 41 | this._noCoreCache = root ? root._noCoreCache : new Xache({ maxSize: 65536 }) | |
| 42 | ||
| 43 | this._root = root || this | |
| 44 | this._replicationStreams = root ? root._replicationStreams : [] | |
| 45 | this._overwrite = opts.overwrite === true | |
| 46 | this._readonly = opts.writable === false | |
| 47 | this._attached = opts._attached || null | |
| 48 | this._notDownloadingLinger = opts.notDownloadingLinger | |
| 49 | ||
| 50 | this._sessions = new Set() // sessions for THIS namespace | |
| 51 | this._rootStoreSessions = new Set() | |
| 52 | this._locks = root ? root._locks : new Map() | |
| 53 | ||
| 54 | this._findingPeersCount = 0 | |
| 55 | this._findingPeers = [] | |
| 56 | this._isCorestore = true | |
| 57 | ||
| 58 | if (this._namespace.byteLength !== 32) throw new Error('Namespace must be a 32-byte Buffer or Uint8Array') | |
| 59 | this.ready().catch(safetyCatch) | |
| 60 | } | |
| 61 | ||
| 62 | static isCorestore (obj) { | |
| 63 | return !!(typeof obj === 'object' && obj && obj._isCorestore) | |
| 64 | } | |
| 65 | ||
| 66 | static from (storage, opts) { | |
| 67 | return this.isCorestore(storage) ? storage : new this(storage, opts) | |
| 68 | } | |
| 69 | ||
| 70 | // for now just release the lock... | |
| 71 | async suspend () { | |
| 72 | if (this._root !== this) return this._root.suspend() | |
| 73 | ||
| 74 | await this.ready() | |
| 75 | ||
| 76 | if (this._keyStorage !== null) { | |
| 77 | await new Promise((resolve, reject) => { | |
| 78 | this._keyStorage.suspend((err) => { | |
| 79 | if (err) return reject(err) | |
| 80 | resolve() | |
| 81 | }) | |
| 82 | }) | |
| 83 | } | |
| 84 | } | |
| 85 | ||
| 86 | async resume () { | |
| 87 | if (this._root !== this) return this._root.resume() | |
| 88 | ||
| 89 | await this.ready() | |
| 90 | ||
| 91 | if (this._keyStorage !== null) { | |
| 92 | await new Promise((resolve, reject) => { | |
| 93 | this._keyStorage.open((err) => { | |
| 94 | if (err) return reject(err) | |
| 95 | resolve() | |
| 96 | }) | |
| 97 | }) | |
| 98 | } | |
| 99 | } | |
| 100 | ||
| 101 | findingPeers () { | |
| 102 | let done = false | |
| 103 | this._incFindingPeers() | |
| 104 | ||
| 105 | return () => { | |
| 106 | if (done) return | |
| 107 | done = true | |
| 108 | this._decFindingPeers() | |
| 109 | } | |
| 110 | } | |
| 111 | ||
| 112 | _emitCore (name, core) { | |
| 113 | this.emit(name, core) | |
| 114 | for (const session of this._root._rootStoreSessions) { | |
| 115 | if (session !== this) { | |
| 116 | session.emit(name, core) | |
| 117 | } | |
| 118 | } | |
| 119 | if (this !== this._root) this._root.emit(name, core) | |
| 120 | } | |
| 121 | ||
| 122 | _incFindingPeers () { | |
| 123 | if (++this._findingPeersCount !== 1) return | |
| 124 | ||
| 125 | for (const core of this._sessions) { | |
| 126 | this._findingPeers.push(core.findingPeers()) | |
| 127 | } | |
| 128 | } | |
| 129 | ||
| 130 | _decFindingPeers () { | |
| 131 | if (--this._findingPeersCount !== 0) return | |
| 132 | ||
| 133 | while (this._findingPeers.length > 0) { | |
| 134 | this._findingPeers.pop()() | |
| 135 | } | |
| 136 | } | |
| 137 | ||
| 138 | async _openNamespaceFromBootstrap () { | |
| 139 | const ns = await this._bootstrap.getUserData(USERDATA_NAMESPACE_KEY) | |
| 140 | if (ns) { | |
| 141 | this._namespace = ns | |
| 142 | } | |
| 143 | } | |
| 144 | ||
| 145 | async _open () { | |
| 146 | if (this._root !== this) { | |
| 147 | await this._root.ready() | |
| 148 | if (!this.primaryKey) this.primaryKey = this._root.primaryKey | |
| 149 | if (this._bootstrap) await this._openNamespaceFromBootstrap() | |
| 150 | return | |
| 151 | } | |
| 152 | ||
| 153 | this._keyStorage = this.storage(PRIMARY_KEY_FILE_NAME) | |
| 154 | ||
| 155 | this.primaryKey = await new Promise((resolve, reject) => { | |
| 156 | this._keyStorage.stat((err, st) => { | |
| 157 | if (err && err.code !== 'ENOENT') return reject(err) | |
| 158 | if (err || st.size < 32 || this._overwrite) { | |
| 159 | const key = this.primaryKey || crypto.randomBytes(32) | |
| 160 | return this._keyStorage.write(0, key, err => { | |
| 161 | if (err) return reject(err) | |
| 162 | return resolve(key) | |
| 163 | }) | |
| 164 | } | |
| 165 | this._keyStorage.read(0, 32, (err, key) => { | |
| 166 | if (err) return reject(err) | |
| 167 | if (this.primaryKey) return resolve(this.primaryKey) | |
| 168 | return resolve(key) | |
| 169 | }) | |
| 170 | }) | |
| 171 | }) | |
| 172 | ||
| 173 | if (this._bootstrap) await this._openNamespaceFromBootstrap() | |
| 174 | } | |
| 175 | ||
| 176 | async _exists (discoveryKey) { | |
| 177 | const id = b4a.toString(discoveryKey, 'hex') | |
| 178 | const storageRoot = getStorageRoot(id) | |
| 179 | ||
| 180 | const st = this.storage(storageRoot + '/oplog') | |
| 181 | ||
| 182 | const exists = await new Promise((resolve) => st.stat((err, st) => resolve(!err && st.size > 0))) | |
| 183 | await new Promise(resolve => st.close(resolve)) | |
| 184 | ||
| 185 | return exists | |
| 186 | } | |
| 187 | ||
| 188 | async _generateKeys (opts) { | |
| 189 | if (opts._discoveryKey) { | |
| 190 | return { | |
| 191 | manifest: null, | |
| 192 | keyPair: null, | |
| 193 | key: null, | |
| 194 | discoveryKey: opts._discoveryKey | |
| 195 | } | |
| 196 | } | |
| 197 | ||
| 198 | const keyPair = opts.name | |
| 199 | ? await this.createKeyPair(opts.name) | |
| 200 | : (opts.secretKey) | |
| 201 | ? { secretKey: opts.secretKey, publicKey: opts.publicKey } | |
| 202 | : null | |
| 203 | ||
| 204 | if (opts.manifest) { | |
| 205 | const key = Hypercore.key(opts.manifest) | |
| 206 | ||
| 207 | return { | |
| 208 | manifest: opts.manifest, | |
| 209 | keyPair, | |
| 210 | key, | |
| 211 | discoveryKey: crypto.discoveryKey(key) | |
| 212 | } | |
| 213 | } | |
| 214 | ||
| 215 | if (opts.key) { | |
| 216 | return { | |
| 217 | manifest: null, | |
| 218 | keyPair, | |
| 219 | key: opts.key, | |
| 220 | discoveryKey: crypto.discoveryKey(opts.key) | |
| 221 | } | |
| 222 | } | |
| 223 | ||
| 224 | const publicKey = opts.publicKey || keyPair.publicKey | |
| 225 | ||
| 226 | if (opts.compat === false || (opts.compat !== true && !this.compat)) { | |
| 227 | let manifest = { version: this.manifestVersion, signers: [{ publicKey }] } // default manifest | |
| 228 | let key = Hypercore.key(manifest) | |
| 229 | let discoveryKey = crypto.discoveryKey(key) | |
| 230 | ||
| 231 | if (!(await this._exists(discoveryKey)) && manifest.version !== 0) { | |
| 232 | const manifestV0 = { version: 0, signers: [{ publicKey }] } | |
| 233 | const keyV0 = Hypercore.key(manifestV0) | |
| 234 | const discoveryKeyV0 = crypto.discoveryKey(keyV0) | |
| 235 | ||
| 236 | if (await this._exists(discoveryKeyV0)) { | |
| 237 | manifest = manifestV0 | |
| 238 | key = keyV0 | |
| 239 | discoveryKey = discoveryKeyV0 | |
| 240 | } | |
| 241 | } | |
| 242 | ||
| 243 | return { | |
| 244 | manifest, | |
| 245 | keyPair, | |
| 246 | key, | |
| 247 | discoveryKey | |
| 248 | } | |
| 249 | } | |
| 250 | ||
| 251 | return { | |
| 252 | manifest: null, | |
| 253 | keyPair, | |
| 254 | key: publicKey, | |
| 255 | discoveryKey: crypto.discoveryKey(publicKey) | |
| 256 | } | |
| 257 | } | |
| 258 | ||
| 259 | _getPrereadyUserData (core, key) { | |
| 260 | // Need to manually read the header values before the Hypercore is ready, hence the ugliness. | |
| 261 | for (const { key: savedKey, value } of core.core.header.userData) { | |
| 262 | if (key === savedKey) return value | |
| 263 | } | |
| 264 | return null | |
| 265 | } | |
| 266 | ||
| 267 | async _preready (core) { | |
| 268 | const name = this._getPrereadyUserData(core, USERDATA_NAME_KEY) | |
| 269 | if (!name) return | |
| 270 | ||
| 271 | const namespace = this._getPrereadyUserData(core, USERDATA_NAMESPACE_KEY) | |
| 272 | const keyPair = await this.createKeyPair(b4a.toString(name), namespace) | |
| 273 | core.setKeyPair(keyPair) | |
| 274 | } | |
| 275 | ||
| 276 | _getLock (id) { | |
| 277 | let rw = this._locks.get(id) | |
| 278 | ||
| 279 | if (!rw) { | |
| 280 | rw = new RW() | |
| 281 | this._locks.set(id, rw) | |
| 282 | } | |
| 283 | ||
| 284 | return rw | |
| 285 | } | |
| 286 | ||
| 287 | async _preload (id, keys, opts) { | |
| 288 | const { manifest, keyPair, key } = keys | |
| 289 | ||
| 290 | while (this.cores.has(id)) { | |
| 291 | const existing = this.cores.get(id) | |
| 292 | if (existing.opened && !existing.closing) return { from: existing, keyPair, manifest, cache: !!opts.cache } | |
| 293 | if (existing.closing) { | |
| 294 | await existing.close() | |
| 295 | } else { | |
| 296 | await existing.ready().catch(safetyCatch) | |
| 297 | } | |
| 298 | } | |
| 299 | ||
| 300 | const hasKeyPair = !!(keyPair && keyPair.secretKey) | |
| 301 | const userData = {} | |
| 302 | if (opts.name) { | |
| 303 | userData[USERDATA_NAME_KEY] = b4a.from(opts.name) | |
| 304 | userData[USERDATA_NAMESPACE_KEY] = this._namespace | |
| 305 | } | |
| 306 | ||
| 307 | // No more async ticks allowed after this point -- necessary for caching | |
| 308 | ||
| 309 | const storageRoot = getStorageRoot(id) | |
| 310 | const core = new Hypercore(p => this.storage(storageRoot + '/' + p), { | |
| 311 | _preready: this._preready.bind(this), | |
| 312 | notDownloadingLinger: this._notDownloadingLinger, | |
| 313 | inflightRange: this.inflightRange, | |
| 314 | autoClose: true, | |
| 315 | active: false, | |
| 316 | encryptionKey: opts.encryptionKey || null, | |
| 317 | isBlockKey: !!opts.isBlockKey, | |
| 318 | userData, | |
| 319 | manifest, | |
| 320 | key, | |
| 321 | compat: opts.compat, | |
| 322 | cache: opts.cache, | |
| 323 | globalCache: this.globalCache, | |
| 324 | createIfMissing: opts.createIfMissing === false ? false : !opts._discoveryKey, | |
| 325 | keyPair: hasKeyPair ? keyPair : null | |
| 326 | }) | |
| 327 | ||
| 328 | if (this._root.closing) { | |
| 329 | try { | |
| 330 | await core.close() | |
| 331 | } catch {} | |
| 332 | throw new Error('The corestore is closed') | |
| 333 | } | |
| 334 | ||
| 335 | this.cores.set(id, core) | |
| 336 | this._noCoreCache.delete(id) | |
| 337 | core.ready().then(() => { | |
| 338 | if (core.closing) return // extra safety here as ready is a tick after open | |
| 339 | if (hasKeyPair) core.setKeyPair(keyPair) | |
| 340 | this._emitCore('core-open', core) | |
| 341 | if (this.passive) return | |
| 342 | ||
| 343 | const ondownloading = () => { | |
| 344 | for (const { stream } of this._replicationStreams) { | |
| 345 | core.replicate(stream, { session: true }) | |
| 346 | } | |
| 347 | } | |
| 348 | // when the replicator says we are downloading, answer the call | |
| 349 | core.replicator.ondownloading = ondownloading | |
| 350 | // trigger once if the condition is already true | |
| 351 | if (core.replicator.downloading) ondownloading() | |
| 352 | }, () => { | |
| 353 | this._noCoreCache.set(id, true) | |
| 354 | this.cores.delete(id) | |
| 355 | }) | |
| 356 | core.once('close', () => { | |
| 357 | this._emitCore('core-close', core) | |
| 358 | this.cores.delete(id) | |
| 359 | }) | |
| 360 | core.on('conflict', (len, fork, proof) => { | |
| 361 | this.emit('conflict', core, len, fork, proof) | |
| 362 | }) | |
| 363 | ||
| 364 | return { from: core, keyPair, manifest, cache: !!opts.cache } | |
| 365 | } | |
| 366 | ||
| 367 | async createKeyPair (name, namespace = this._namespace) { | |
| 368 | if (!this.opened) await this.ready() | |
| 369 | ||
| 370 | const keyPair = { | |
| 371 | publicKey: b4a.allocUnsafeSlow(sodium.crypto_sign_PUBLICKEYBYTES), | |
| 372 | secretKey: b4a.alloc(sodium.crypto_sign_SECRETKEYBYTES) | |
| 373 | } | |
| 374 | ||
| 375 | const seed = deriveSeed(this.primaryKey, namespace, name) | |
| 376 | sodium.crypto_sign_seed_keypair(keyPair.publicKey, keyPair.secretKey, seed) | |
| 377 | ||
| 378 | return keyPair | |
| 379 | } | |
| 380 | ||
| 381 | get (opts = {}) { | |
| 382 | if (this.closing || this._root.closing) throw new Error('The corestore is closed') | |
| 383 | opts = validateGetOptions(opts) | |
| 384 | ||
| 385 | if (opts.cache !== false) { | |
| 386 | opts.cache = opts.cache === true || (this.cache && !opts.cache) ? defaultCache() : opts.cache | |
| 387 | } | |
| 388 | if (this._readonly && opts.writable !== false) { | |
| 389 | opts.writable = false | |
| 390 | } | |
| 391 | ||
| 392 | let rw = null | |
| 393 | let id = null | |
| 394 | ||
| 395 | const core = new Hypercore(null, { | |
| 396 | ...opts, | |
| 397 | globalCache: this.globalCache, | |
| 398 | name: null, | |
| 399 | preload: async () => { | |
| 400 | if (opts.preload) opts = { ...opts, ...(await opts.preload()) } | |
| 401 | if (!this.opened) await this.ready() | |
| 402 | ||
| 403 | const keys = await this._generateKeys(opts) | |
| 404 | ||
| 405 | id = b4a.toString(keys.discoveryKey, 'hex') | |
| 406 | rw = (opts.exclusive && opts.writable !== false) ? this._getLock(id) : null | |
| 407 | ||
| 408 | if (rw) await rw.write.lock() | |
| 409 | return await this._preload(id, keys, opts) | |
| 410 | } | |
| 411 | }) | |
| 412 | ||
| 413 | this._sessions.add(core) | |
| 414 | if (this._findingPeersCount > 0) { | |
| 415 | this._findingPeers.push(core.findingPeers()) | |
| 416 | } | |
| 417 | ||
| 418 | const gc = () => { | |
| 419 | // technically better to also clear _findingPeers if we added it, | |
| 420 | // but the lifecycle for those are pretty short so prob not worth the complexity | |
| 421 | // as _decFindingPeers clear them all. | |
| 422 | this._sessions.delete(core) | |
| 423 | ||
| 424 | if (!rw) return | |
| 425 | rw.write.unlock() | |
| 426 | if (!rw.write.locked) this._locks.delete(id) | |
| 427 | } | |
| 428 | ||
| 429 | core.ready().catch(gc) | |
| 430 | core.once('close', gc) | |
| 431 | ||
| 432 | return core | |
| 433 | } | |
| 434 | ||
| 435 | replicate (isInitiator, opts) { | |
| 436 | const isExternal = isStream(isInitiator) || !!(opts && opts.stream) | |
| 437 | const stream = Hypercore.createProtocolStream(isInitiator, { | |
| 438 | ...opts, | |
| 439 | ondiscoverykey: async discoveryKey => { | |
| 440 | if (this.closing) return | |
| 441 | ||
| 442 | const id = b4a.toString(discoveryKey, 'hex') | |
| 443 | if (this._noCoreCache.get(id)) return | |
| 444 | ||
| 445 | const core = this.get({ _discoveryKey: discoveryKey, active: false }) | |
| 446 | ||
| 447 | try { | |
| 448 | await core.ready() | |
| 449 | } catch { | |
| 450 | return | |
| 451 | } | |
| 452 | ||
| 453 | // remote is asking for the core so we HAVE to answer even if not downloading | |
| 454 | if (!core.closing) core.replicate(stream, { session: true }) | |
| 455 | await core.close() | |
| 456 | } | |
| 457 | }) | |
| 458 | ||
| 459 | if (!this.passive) { | |
| 460 | const muxer = stream.noiseStream.userData | |
| 461 | muxer.cork() | |
| 462 | for (const core of this.cores.values()) { | |
| 463 | // If the core is not opened, it will be replicated in preload. | |
| 464 | if (!core.opened || core.closing || !core.replicator.downloading) continue | |
| 465 | core.replicate(stream, { session: true }) | |
| 466 | } | |
| 467 | stream.noiseStream.opened.then(() => muxer.uncork()) | |
| 468 | } | |
| 469 | ||
| 470 | const streamRecord = { stream, isExternal } | |
| 471 | this._replicationStreams.push(streamRecord) | |
| 472 | ||
| 473 | stream.once('close', () => { | |
| 474 | this._replicationStreams.splice(this._replicationStreams.indexOf(streamRecord), 1) | |
| 475 | }) | |
| 476 | ||
| 477 | return stream | |
| 478 | } | |
| 479 | ||
| 480 | namespace (name, opts) { | |
| 481 | if (name instanceof Hypercore) { | |
| 482 | return this.session({ ...opts, _bootstrap: name }) | |
| 483 | } | |
| 484 | return this.session({ ...opts, namespace: generateNamespace(this._namespace, name) }) | |
| 485 | } | |
| 486 | ||
| 487 | session (opts) { | |
| 488 | const session = new Corestore(this.storage, { | |
| 489 | namespace: this._namespace, | |
| 490 | cache: this.cache, | |
| 491 | writable: !this._readonly, | |
| 492 | _attached: opts && opts.detach === false ? this : null, | |
| 493 | _root: this._root, | |
| 494 | inflightRange: this.inflightRange, | |
| 495 | globalCache: this.globalCache, | |
| 496 | ...opts | |
| 497 | }) | |
| 498 | if (this === this._root) this._rootStoreSessions.add(session) | |
| 499 | return session | |
| 500 | } | |
| 501 | ||
| 502 | _closeNamespace () { | |
| 503 | const closePromises = [] | |
| 504 | for (const session of this._sessions) { | |
| 505 | closePromises.push(session.close()) | |
| 506 | } | |
| 507 | return Promise.allSettled(closePromises) | |
| 508 | } | |
| 509 | ||
| 510 | async _closePrimaryNamespace () { | |
| 511 | const closePromises = [] | |
| 512 | // At this point, the primary namespace is closing. | |
| 513 | for (const { stream, isExternal } of this._replicationStreams) { | |
| 514 | // Only close streams that were created by the Corestore | |
| 515 | if (!isExternal) stream.destroy() | |
| 516 | } | |
| 517 | for (const core of this.cores.values()) { | |
| 518 | closePromises.push(forceClose(core)) | |
| 519 | } | |
| 520 | await Promise.allSettled(closePromises) | |
| 521 | await new Promise((resolve, reject) => { | |
| 522 | this._keyStorage.close(err => { | |
| 523 | if (err) return reject(err) | |
| 524 | return resolve(null) | |
| 525 | }) | |
| 526 | }) | |
| 527 | } | |
| 528 | ||
| 529 | async _close () { | |
| 530 | this._root._rootStoreSessions.delete(this) | |
| 531 | ||
| 532 | await this._closeNamespace() | |
| 533 | ||
| 534 | if (this._root === this) { | |
| 535 | await this._closePrimaryNamespace() | |
| 536 | } else if (this._attached) { | |
| 537 | await this._attached.close() | |
| 538 | } | |
| 539 | } | |
| 540 | } | |
| 541 | ||
| 542 | function validateGetOptions (opts) { | |
| 543 | const key = (b4a.isBuffer(opts) || typeof opts === 'string') ? hypercoreId.decode(opts) : null | |
| 544 | if (key) return { key } | |
| 545 | ||
| 546 | if (opts.key) { | |
| 547 | opts.key = hypercoreId.decode(opts.key) | |
| 548 | } | |
| 549 | if (opts.keyPair) { | |
| 550 | opts.publicKey = opts.keyPair.publicKey | |
| 551 | opts.secretKey = opts.keyPair.secretKey | |
| 552 | } | |
| 553 | ||
| 554 | if (opts.name && typeof opts.name !== 'string') throw new Error('name option must be a String') | |
| 555 | if (opts.name && opts.secretKey) throw new Error('Cannot provide both a name and a secret key') | |
| 556 | if (opts.publicKey && !b4a.isBuffer(opts.publicKey)) throw new Error('publicKey option must be a Buffer or Uint8Array') | |
| 557 | if (opts.secretKey && !b4a.isBuffer(opts.secretKey)) throw new Error('secretKey option must be a Buffer or Uint8Array') | |
| 558 | if (!opts._discoveryKey && (!opts.name && !opts.publicKey && !opts.manifest && !opts.key && !opts.preload)) throw new Error('Must provide either a name or a publicKey') | |
| 559 | return opts | |
| 560 | } | |
| 561 | ||
| 562 | function generateNamespace (namespace, name) { | |
| 563 | if (!b4a.isBuffer(name)) name = b4a.from(name) | |
| 564 | const out = b4a.allocUnsafeSlow(32) | |
| 565 | sodium.crypto_generichash_batch(out, [namespace, name]) | |
| 566 | return out | |
| 567 | } | |
| 568 | ||
| 569 | function deriveSeed (primaryKey, namespace, name) { | |
| 570 | if (!b4a.isBuffer(name)) name = b4a.from(name) | |
| 571 | const out = b4a.alloc(32) | |
| 572 | sodium.crypto_generichash_batch(out, [NS, namespace, name], primaryKey) | |
| 573 | return out | |
| 574 | } | |
| 575 | ||
| 576 | function defaultCache () { | |
| 577 | return new Xache({ maxSize: 65536, maxAge: 0 }) | |
| 578 | } | |
| 579 | ||
| 580 | function isStream (s) { | |
| 581 | return typeof s === 'object' && s && typeof s.pipe === 'function' | |
| 582 | } | |
| 583 | ||
| 584 | async function forceClose (core) { | |
| 585 | await core.ready() | |
| 586 | return Promise.all(core.sessions.map(s => s.close())) | |
| 587 | } | |
| 588 | ||
| 589 | function getStorageRoot (id) { | |
| 590 | return CORES_DIR + '/' + id.slice(0, 2) + '/' + id.slice(2, 4) + '/' + id | |
| 591 | } |
| … | … | @@ -0,0 +1,44 @@ |
| 1 | { | |
| 2 | "name": "corestore", | |
| 3 | "version": "6.18.4", | |
| 4 | "description": "A Hypercore factory that simplifies managing collections of cores.", | |
| 5 | "main": "index.js", | |
| 6 | "scripts": { | |
| 7 | "test": "standard && brittle test/*.js" | |
| 8 | }, | |
| 9 | "repository": { | |
| 10 | "type": "git", | |
| 11 | "url": "git+https://github.com/holepunchto/corestore.git" | |
| 12 | }, | |
| 13 | "keywords": [ | |
| 14 | "corestore" | |
| 15 | ], | |
| 16 | "author": "Andrew Osheroff <andrewosh@gmail.com>", | |
| 17 | "license": "MIT", | |
| 18 | "bugs": { | |
| 19 | "url": "https://github.com/holepunchto/corestore/issues" | |
| 20 | }, | |
| 21 | "homepage": "https://github.com/holepunchto/corestore#readme", | |
| 22 | "files": [ | |
| 23 | "index.js", | |
| 24 | "lib/**.js" | |
| 25 | ], | |
| 26 | "devDependencies": { | |
| 27 | "brittle": "^3.2.2", | |
| 28 | "rache": "^1.0.0", | |
| 29 | "random-access-memory": "^6.2.0", | |
| 30 | "standard": "^17.1.0", | |
| 31 | "test-tmp": "^1.0.2" | |
| 32 | }, | |
| 33 | "dependencies": { | |
| 34 | "b4a": "^1.6.4", | |
| 35 | "hypercore": "^10.37.10", | |
| 36 | "hypercore-crypto": "^3.4.0", | |
| 37 | "hypercore-id-encoding": "^1.2.0", | |
| 38 | "read-write-mutexify": "^2.1.0", | |
| 39 | "ready-resource": "^1.0.0", | |
| 40 | "safety-catch": "^1.0.1", | |
| 41 | "sodium-universal": "^4.0.0", | |
| 42 | "xache": "^1.1.0" | |
| 43 | } | |
| 44 | } |
| … | … | @@ -0,0 +1,47 @@ |
| 1 | cmake_minimum_required(VERSION 3.25) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) | |
| 5 | find_package(cmake-napi REQUIRED PATHS node_modules/cmake-napi) | |
| 6 | ||
| 7 | project(crc_native C) | |
| 8 | ||
| 9 | bare_target(target) | |
| 10 | ||
| 11 | if(target MATCHES "win32") | |
| 12 | add_compile_options(/MT$<$<CONFIG:Debug>:d>) | |
| 13 | endif() | |
| 14 | ||
| 15 | fetch_package("github:holepunchto/libcrc#9a0c969") | |
| 16 | ||
| 17 | add_bare_module(crc_native_bare) | |
| 18 | ||
| 19 | target_sources( | |
| 20 | ${crc_native_bare} | |
| 21 | PRIVATE | |
| 22 | binding.c | |
| 23 | ) | |
| 24 | ||
| 25 | target_link_libraries( | |
| 26 | ${crc_native_bare} | |
| 27 | PRIVATE | |
| 28 | $<TARGET_OBJECTS:crc> | |
| 29 | PUBLIC | |
| 30 | crc | |
| 31 | ) | |
| 32 | ||
| 33 | add_napi_module(crc_native_node) | |
| 34 | ||
| 35 | target_sources( | |
| 36 | ${crc_native_node} | |
| 37 | PRIVATE | |
| 38 | binding.c | |
| 39 | ) | |
| 40 | ||
| 41 | target_link_libraries( | |
| 42 | ${crc_native_node} | |
| 43 | PRIVATE | |
| 44 | $<TARGET_OBJECTS:crc> | |
| 45 | PUBLIC | |
| 46 | crc | |
| 47 | ) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | # crc-native | |
| 2 | ||
| 3 | https://github.com/holepunchto/libcrc JavaScript bindings for Node.js. | |
| 4 | ||
| 5 | ```sh | |
| 6 | npm install crc-native | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const { crc32 } = require('crc-native') | |
| 13 | ||
| 14 | const crc = crc32(Buffer.from('hello world')) | |
| 15 | // 0xd4a1185 | |
| 16 | ``` | |
| 17 | ||
| 18 | ## API | |
| 19 | ||
| 20 | #### `const crc = crc32(buffer)` | |
| 21 | ||
| 22 | Compute the CRC32 of the given buffer. | |
| 23 | ||
| 24 | ## License | |
| 25 | ||
| 26 | Apache-2.0 |
| … | … | @@ -0,0 +1,15 @@ |
| 1 | #include <crc.h> | |
| 2 | #include <node_api.h> | |
| 3 | ||
| 4 | #include "macros.h" | |
| 5 | ||
| 6 | NAPI_METHOD(crc_u32_napi) { | |
| 7 | NAPI_ARGV(1); | |
| 8 | NAPI_ARGV_BUFFER_CAST(uint8_t *, buf, 0); | |
| 9 | ||
| 10 | NAPI_RETURN_UINT32(crc_u32(buf, buf_len)); | |
| 11 | } | |
| 12 | ||
| 13 | NAPI_INIT() { | |
| 14 | NAPI_EXPORT_FUNCTION(crc_u32_napi); | |
| 15 | } |
| … | … | @@ -0,0 +1,3 @@ |
| 1 | require.addon = require('require-addon') | |
| 2 | ||
| 3 | module.exports = require.addon('.', __filename) |
| … | … | @@ -0,0 +1,5 @@ |
| 1 | const binding = require('./binding') | |
| 2 | ||
| 3 | exports.crc32 = function crc32 (buffer) { | |
| 4 | return binding.crc_u32_napi(buffer) | |
| 5 | } |
| … | … | @@ -0,0 +1,255 @@ |
| 1 | #ifndef NAPI_MACROS | |
| 2 | #define NAPI_MACROS | |
| 3 | ||
| 4 | #define NAPI_INIT() \ | |
| 5 | static void napi_macros_init(napi_env env, napi_value exports); \ | |
| 6 | static napi_value napi_macros_init_wrap(napi_env env, napi_value exports) { \ | |
| 7 | napi_macros_init(env, exports); \ | |
| 8 | return exports; \ | |
| 9 | } \ | |
| 10 | NAPI_MODULE(NODE_GYP_MODULE_NAME, napi_macros_init_wrap) \ | |
| 11 | static void napi_macros_init(napi_env env, napi_value exports) | |
| 12 | ||
| 13 | #define NAPI_TEST_GC(env) \ | |
| 14 | { \ | |
| 15 | napi_handle_scope scope; \ | |
| 16 | napi_open_handle_scope(env, &scope); \ | |
| 17 | napi_value s; \ | |
| 18 | napi_value r; \ | |
| 19 | napi_create_string_utf8(env, "try { global.gc() } catch {}", NAPI_AUTO_LENGTH, &s); \ | |
| 20 | napi_run_script(env, s, &r); \ | |
| 21 | napi_close_handle_scope(env, scope); \ | |
| 22 | } | |
| 23 | ||
| 24 | #define NAPI_UV_ERROR_MAP_ITER(NAME, DESC) \ | |
| 25 | { \ | |
| 26 | napi_create_array(env, &entry); \ | |
| 27 | napi_create_array(env, &val); \ | |
| 28 | napi_value name; \ | |
| 29 | napi_create_string_utf8(env, #NAME, NAPI_AUTO_LENGTH, &name); \ | |
| 30 | napi_set_element(env, val, 0, name); \ | |
| 31 | napi_value desc; \ | |
| 32 | napi_create_string_utf8(env, DESC, NAPI_AUTO_LENGTH, &desc); \ | |
| 33 | napi_set_element(env, val, 1, desc); \ | |
| 34 | napi_create_int32(env, UV_##NAME, &key); \ | |
| 35 | napi_set_element(env, entry, 0, key); \ | |
| 36 | napi_set_element(env, entry, 1, val); \ | |
| 37 | napi_set_element(env, arr, i++, entry); \ | |
| 38 | } | |
| 39 | ||
| 40 | #define NAPI_MAKE_CALLBACK(env, nil, ctx, cb, n, argv, res) \ | |
| 41 | if (napi_make_callback(env, nil, ctx, cb, n, argv, res) == napi_pending_exception) { \ | |
| 42 | napi_value fatal_exception; \ | |
| 43 | napi_get_and_clear_last_exception(env, &fatal_exception); \ | |
| 44 | napi_fatal_exception(env, fatal_exception); \ | |
| 45 | } | |
| 46 | ||
| 47 | #define NAPI_STATUS_THROWS_VOID(call) \ | |
| 48 | if ((call) != napi_ok) { \ | |
| 49 | napi_throw_error(env, NULL, #call " failed!"); \ | |
| 50 | return; \ | |
| 51 | } | |
| 52 | ||
| 53 | #define NAPI_STATUS_THROWS(call) \ | |
| 54 | if ((call) != napi_ok) { \ | |
| 55 | napi_throw_error(env, NULL, #call " failed!"); \ | |
| 56 | return NULL; \ | |
| 57 | } | |
| 58 | ||
| 59 | #define NAPI_METHOD(name) \ | |
| 60 | napi_value name(napi_env env, napi_callback_info info) | |
| 61 | ||
| 62 | #define NAPI_UV_THROWS(err, fn) \ | |
| 63 | err = fn; \ | |
| 64 | if (err < 0) { \ | |
| 65 | napi_throw_error(env, uv_err_name(err), uv_strerror(err)); \ | |
| 66 | return NULL; \ | |
| 67 | } | |
| 68 | ||
| 69 | #define NAPI_EXPORT_OFFSETOF(type, name) \ | |
| 70 | { \ | |
| 71 | napi_value name##_offsetof; \ | |
| 72 | type tmp; \ | |
| 73 | void *ptr = &(tmp.name); \ | |
| 74 | void *ptr_base = &tmp; \ | |
| 75 | int offset = (char *) ptr - (char *) ptr_base; \ | |
| 76 | NAPI_STATUS_THROWS_VOID(napi_create_uint32(env, offset, &name##_offsetof)) \ | |
| 77 | NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, "offsetof_" #type "_" #name, name##_offsetof)) \ | |
| 78 | } | |
| 79 | ||
| 80 | #define NAPI_EXPORT_OFFSETOF_STRUCT(type, name) \ | |
| 81 | { \ | |
| 82 | napi_value name##_offsetof; \ | |
| 83 | struct type tmp; \ | |
| 84 | void *ptr = &(tmp.name); \ | |
| 85 | void *ptr_base = &tmp; \ | |
| 86 | int offset = (char *) ptr - (char *) ptr_base; \ | |
| 87 | NAPI_STATUS_THROWS_VOID(napi_create_uint32(env, offset, &name##_offsetof)) \ | |
| 88 | NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, "offsetof_struct_" #type "_" #name, name##_offsetof)) \ | |
| 89 | } | |
| 90 | ||
| 91 | #define NAPI_EXPORT_ALIGNMENTOF(name) \ | |
| 92 | { \ | |
| 93 | napi_value name##_alignmentof; \ | |
| 94 | struct tmp { \ | |
| 95 | char a; \ | |
| 96 | name b; \ | |
| 97 | }; \ | |
| 98 | NAPI_STATUS_THROWS_VOID(napi_create_uint32(env, sizeof(struct tmp) - sizeof(name), &name##_alignmentof)) \ | |
| 99 | NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, "alignmentof_" #name, name##_alignmentof)) \ | |
| 100 | } | |
| 101 | ||
| 102 | #define NAPI_EXPORT_ALIGNMENTOF_STRUCT(name) \ | |
| 103 | { \ | |
| 104 | napi_value name##_alignmentof; \ | |
| 105 | struct tmp { \ | |
| 106 | char a; \ | |
| 107 | struct name b; \ | |
| 108 | }; \ | |
| 109 | NAPI_STATUS_THROWS_VOID(napi_create_uint32(env, sizeof(struct tmp) - sizeof(struct name), &name##_alignmentof)) \ | |
| 110 | NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, "alignmentof_" #name, name##_alignmentof)) \ | |
| 111 | } | |
| 112 | ||
| 113 | #define NAPI_EXPORT_UV_ERROR_MAP() \ | |
| 114 | { \ | |
| 115 | napi_value arr; \ | |
| 116 | napi_value key; \ | |
| 117 | napi_value val; \ | |
| 118 | napi_value entry; \ | |
| 119 | napi_create_array(env, &arr); \ | |
| 120 | int i = 0; \ | |
| 121 | UV_ERRNO_MAP(NAPI_UV_ERROR_MAP_ITER) \ | |
| 122 | NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, "uv_error_map", arr)) \ | |
| 123 | } | |
| 124 | ||
| 125 | #define NAPI_EXPORT_SIZEOF(name) \ | |
| 126 | { \ | |
| 127 | napi_value name##_sizeof; \ | |
| 128 | NAPI_STATUS_THROWS_VOID(napi_create_uint32(env, sizeof(name), &name##_sizeof)) \ | |
| 129 | NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, "sizeof_" #name, name##_sizeof)) \ | |
| 130 | } | |
| 131 | ||
| 132 | #define NAPI_EXPORT_SIZEOF_STRUCT(name) \ | |
| 133 | { \ | |
| 134 | napi_value name##_sizeof; \ | |
| 135 | NAPI_STATUS_THROWS_VOID(napi_create_uint32(env, sizeof(struct name), &name##_sizeof)) \ | |
| 136 | NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, "sizeof_" #name, name##_sizeof)) \ | |
| 137 | } | |
| 138 | ||
| 139 | #define NAPI_EXPORT_UINT32(name) \ | |
| 140 | { \ | |
| 141 | napi_value name##_uint32; \ | |
| 142 | NAPI_STATUS_THROWS_VOID(napi_create_uint32(env, name, &name##_uint32)) \ | |
| 143 | NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, #name, name##_uint32)) \ | |
| 144 | } | |
| 145 | ||
| 146 | #define NAPI_EXPORT_INT32(name) \ | |
| 147 | { \ | |
| 148 | napi_value name##_int32; \ | |
| 149 | NAPI_STATUS_THROWS_VOID(napi_create_int32(env, name, &name##_int32)) \ | |
| 150 | NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, #name, name##_int32)) \ | |
| 151 | } | |
| 152 | ||
| 153 | #define NAPI_EXPORT_FUNCTION(name) \ | |
| 154 | { \ | |
| 155 | napi_value name##_fn; \ | |
| 156 | NAPI_STATUS_THROWS_VOID(napi_create_function(env, NULL, 0, name, NULL, &name##_fn)) \ | |
| 157 | NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, #name, name##_fn)) \ | |
| 158 | } | |
| 159 | ||
| 160 | #define NAPI_EXPORT_UTF8(name, len) \ | |
| 161 | { \ | |
| 162 | napi_value name##_utf8; \ | |
| 163 | NAPI_STATUS_THROWS_VOID(napi_create_string_utf8(env, name, len, &name##_utf8)) \ | |
| 164 | NAPI_STATUS_THROWS_VOID(napi_set_named_property(env, exports, #name, name##_utf8)) \ | |
| 165 | } | |
| 166 | ||
| 167 | #define NAPI_EXPORT_STRING(name) \ | |
| 168 | NAPI_EXPORT_UTF8(name, NAPI_AUTO_LENGTH) | |
| 169 | ||
| 170 | #define NAPI_RETURN_INT32(name) \ | |
| 171 | napi_value return_int32; \ | |
| 172 | NAPI_STATUS_THROWS(napi_create_int32(env, name, &return_int32)) \ | |
| 173 | return return_int32; | |
| 174 | ||
| 175 | #define NAPI_RETURN_UINT32(name) \ | |
| 176 | napi_value return_uint32; \ | |
| 177 | NAPI_STATUS_THROWS(napi_create_uint32(env, name, &return_uint32)) \ | |
| 178 | return return_uint32; | |
| 179 | ||
| 180 | #define NAPI_RETURN_UTF8(name, len) \ | |
| 181 | napi_value return_utf8; \ | |
| 182 | NAPI_STATUS_THROWS(napi_create_string_utf8(env, name, len, &return_utf8)) \ | |
| 183 | return return_utf8; | |
| 184 | ||
| 185 | #define NAPI_RETURN_STRING(name) \ | |
| 186 | NAPI_RETURN_UTF8(name, NAPI_AUTO_LENGTH) | |
| 187 | ||
| 188 | #define NAPI_UTF8(name, size, val) \ | |
| 189 | char name[size]; \ | |
| 190 | size_t name##_len; \ | |
| 191 | if (napi_get_value_string_utf8(env, val, (char *) &name, size, &name##_len) != napi_ok) { \ | |
| 192 | napi_throw_error(env, "EINVAL", "Expected string"); \ | |
| 193 | return NULL; \ | |
| 194 | } | |
| 195 | ||
| 196 | #define NAPI_UTF8_MALLOC(name, val) \ | |
| 197 | size_t name##_size = 0; \ | |
| 198 | NAPI_STATUS_THROWS(napi_get_value_string_utf8(env, val, NULL, 0, &name##_size)) \ | |
| 199 | char *name = (char *) malloc((name##_size + 1) * sizeof(char)); \ | |
| 200 | size_t name##_len; \ | |
| 201 | NAPI_STATUS_THROWS(napi_get_value_string_utf8(env, val, name, name##_size + 1, &name##_len)) \ | |
| 202 | name[name##_size] = '\0'; | |
| 203 | ||
| 204 | #define NAPI_UINT32(name, val) \ | |
| 205 | uint32_t name; \ | |
| 206 | if (napi_get_value_uint32(env, val, &name) != napi_ok) { \ | |
| 207 | napi_throw_error(env, "EINVAL", "Expected unsigned number"); \ | |
| 208 | return NULL; \ | |
| 209 | } | |
| 210 | ||
| 211 | #define NAPI_INT32(name, val) \ | |
| 212 | int32_t name; \ | |
| 213 | if (napi_get_value_int32(env, val, &name) != napi_ok) { \ | |
| 214 | napi_throw_error(env, "EINVAL", "Expected number"); \ | |
| 215 | return NULL; \ | |
| 216 | } | |
| 217 | ||
| 218 | #define NAPI_BUFFER_CAST(type, name, val) \ | |
| 219 | type name; \ | |
| 220 | size_t name##_len; \ | |
| 221 | NAPI_STATUS_THROWS(napi_get_buffer_info(env, val, (void **) &name, &name##_len)) | |
| 222 | ||
| 223 | #define NAPI_BUFFER(name, val) \ | |
| 224 | NAPI_BUFFER_CAST(char *, name, val) | |
| 225 | ||
| 226 | #define NAPI_FOR_EACH(arr, element) \ | |
| 227 | uint32_t arr##_len; \ | |
| 228 | napi_get_array_length(env, arr, &arr##_len); \ | |
| 229 | napi_value element; \ | |
| 230 | for (uint32_t i = 0; i < arr##_len && napi_get_element(env, arr, i, &element) == napi_ok; i++) | |
| 231 | ||
| 232 | #define NAPI_ARGV(n) \ | |
| 233 | napi_value argv[n]; \ | |
| 234 | size_t argc = n; \ | |
| 235 | NAPI_STATUS_THROWS(napi_get_cb_info(env, info, &argc, argv, NULL, NULL)) | |
| 236 | ||
| 237 | #define NAPI_ARGV_UTF8(name, size, i) \ | |
| 238 | NAPI_UTF8(name, size, argv[i]) | |
| 239 | ||
| 240 | #define NAPI_ARGV_UTF8_MALLOC(name, i) \ | |
| 241 | NAPI_UTF8_MALLOC(name, argv[i]) | |
| 242 | ||
| 243 | #define NAPI_ARGV_UINT32(name, i) \ | |
| 244 | NAPI_UINT32(name, argv[i]) | |
| 245 | ||
| 246 | #define NAPI_ARGV_INT32(name, i) \ | |
| 247 | NAPI_INT32(name, argv[i]) | |
| 248 | ||
| 249 | #define NAPI_ARGV_BUFFER_CAST(type, name, i) \ | |
| 250 | NAPI_BUFFER_CAST(type, name, argv[i]) | |
| 251 | ||
| 252 | #define NAPI_ARGV_BUFFER(name, i) \ | |
| 253 | NAPI_ARGV_BUFFER_CAST(char *, name, i) | |
| 254 | ||
| 255 | #endif |
| … | … | @@ -0,0 +1,41 @@ |
| 1 | { | |
| 2 | "name": "crc-native", | |
| 3 | "version": "1.1.8", | |
| 4 | "description": "libcrc JavaScript bindings for Node.js", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js", | |
| 8 | "macros.h", | |
| 9 | "binding.c", | |
| 10 | "binding.js", | |
| 11 | "CMakeLists.txt", | |
| 12 | "prebuilds" | |
| 13 | ], | |
| 14 | "addon": true, | |
| 15 | "scripts": { | |
| 16 | "test": "npm run lint && npm run test:bare && npm run test:node", | |
| 17 | "test:bare": "bare test.mjs", | |
| 18 | "test:node": "node test.mjs", | |
| 19 | "lint": "standard" | |
| 20 | }, | |
| 21 | "repository": { | |
| 22 | "type": "git", | |
| 23 | "url": "git+https://github.com/holepunchto/crc-native.git" | |
| 24 | }, | |
| 25 | "author": "Holepunch", | |
| 26 | "license": "Apache-2.0", | |
| 27 | "bugs": { | |
| 28 | "url": "https://github.com/holepunchto/crc-native/issues" | |
| 29 | }, | |
| 30 | "homepage": "https://github.com/holepunchto/crc-native#readme", | |
| 31 | "dependencies": { | |
| 32 | "require-addon": "^1.1.0" | |
| 33 | }, | |
| 34 | "devDependencies": { | |
| 35 | "brittle": "^3.1.0", | |
| 36 | "cmake-bare": "^1.1.7", | |
| 37 | "cmake-fetch": "^1.1.0", | |
| 38 | "cmake-napi": "^1.0.2", | |
| 39 | "standard": "^17.0.0" | |
| 40 | } | |
| 41 | } |
| … | … | @@ -0,0 +1,15 @@ |
| 1 | ISC License | |
| 2 | ||
| 3 | Copyright (c) 2022 Contributors | |
| 4 | ||
| 5 | Permission to use, copy, modify, and/or distribute this software for any | |
| 6 | purpose with or without fee is hereby granted, provided that the above | |
| 7 | copyright notice and this permission notice appear in all copies. | |
| 8 | ||
| 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | |
| 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | |
| 11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | |
| 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | |
| 13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | |
| 14 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
| 15 | PERFORMANCE OF THIS SOFTWARE. |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | # crc-universal | |
| 2 | ||
| 3 | Universal wrapper for https://github.com/holepunchto/libcrc with a JavaScript fallback. | |
| 4 | ||
| 5 | ```sh | |
| 6 | npm install crc-universal | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const { crc32 } = require('crc-universal') | |
| 13 | ||
| 14 | const crc = crc32(Buffer.from('hello world')) | |
| 15 | // 0xd4a1185 | |
| 16 | ``` | |
| 17 | ||
| 18 | ## API | |
| 19 | ||
| 20 | #### `const crc = crc32(buffer)` | |
| 21 | ||
| 22 | Compute the CRC32 of the given buffer. | |
| 23 | ||
| 24 | ## License | |
| 25 | ||
| 26 | ISC |
| … | … | @@ -0,0 +1,57 @@ |
| 1 | /** | |
| 2 | * The JavaScript implementation of CRC32 is a version of the slice-by-16 algorithm | |
| 3 | * as implemented by Stephan Brumme, see https://github.com/stbrumme/crc32. | |
| 4 | * | |
| 5 | * Copyright (c) 2011-2016 Stephan Brumme | |
| 6 | * | |
| 7 | * This software is provided 'as-is', without any express or implied warranty. | |
| 8 | * In no event will the authors be held liable for any damages arising from the | |
| 9 | * use of this software. | |
| 10 | * | |
| 11 | * Permission is granted to anyone to use this software for any purpose, | |
| 12 | * including commercial applications, and to alter it and redistribute it freely, | |
| 13 | * subject to the following restrictions: | |
| 14 | * | |
| 15 | * 1. The origin of this software must not be misrepresented; you must not claim | |
| 16 | * that you wrote the original software. | |
| 17 | * If you use this software in a product, an acknowledgment in the product | |
| 18 | * documentation would be appreciated but is not required. | |
| 19 | * 2. Altered source versions must be plainly marked as such, and must not be | |
| 20 | * misrepresented as being the original software. | |
| 21 | * 3. This notice may not be removed or altered from any source distribution. | |
| 22 | */ | |
| 23 | ||
| 24 | const lookup = require('./lookup') | |
| 25 | ||
| 26 | exports.crc32 = function crc32 (buffer) { | |
| 27 | let crc = ~0 | |
| 28 | let i = 0 | |
| 29 | let length = buffer.byteLength | |
| 30 | ||
| 31 | while (length >= 16) { | |
| 32 | crc = lookup[15][buffer[i++] ^ (crc & 0xff)] ^ | |
| 33 | lookup[14][buffer[i++] ^ ((crc >>> 8) & 0xff)] ^ | |
| 34 | lookup[13][buffer[i++] ^ ((crc >>> 16) & 0xff)] ^ | |
| 35 | lookup[12][buffer[i++] ^ (crc >>> 24)] ^ | |
| 36 | lookup[11][buffer[i++]] ^ | |
| 37 | lookup[10][buffer[i++]] ^ | |
| 38 | lookup[9][buffer[i++]] ^ | |
| 39 | lookup[8][buffer[i++]] ^ | |
| 40 | lookup[7][buffer[i++]] ^ | |
| 41 | lookup[6][buffer[i++]] ^ | |
| 42 | lookup[5][buffer[i++]] ^ | |
| 43 | lookup[4][buffer[i++]] ^ | |
| 44 | lookup[3][buffer[i++]] ^ | |
| 45 | lookup[2][buffer[i++]] ^ | |
| 46 | lookup[1][buffer[i++]] ^ | |
| 47 | lookup[0][buffer[i++]] | |
| 48 | ||
| 49 | length -= 16 | |
| 50 | } | |
| 51 | ||
| 52 | while (length-- > 0) { | |
| 53 | crc = (crc >>> 8) ^ lookup[0][(crc & 0xff) ^ buffer[i++]] | |
| 54 | } | |
| 55 | ||
| 56 | return ~crc >>> 0 | |
| 57 | } |
| … | … | @@ -0,0 +1,11 @@ |
| 1 | const fallback = require('./fallback') | |
| 2 | ||
| 3 | try { | |
| 4 | const native = require('crc-native') | |
| 5 | ||
| 6 | exports.crc32 = function crc32 (buffer) { | |
| 7 | return buffer.byteLength <= 24 ? fallback.crc32(buffer) : native.crc32(buffer) | |
| 8 | } | |
| 9 | } catch { | |
| 10 | module.exports = fallback | |
| 11 | } |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | const lookup = new Array(16) | |
| 2 | ||
| 3 | for (let i = 0; i < 16; i++) { | |
| 4 | lookup[i] = new Uint32Array(0x100) | |
| 5 | } | |
| 6 | ||
| 7 | for (let i = 0; i <= 0xff; i++) { | |
| 8 | let crc = i | |
| 9 | ||
| 10 | for (let j = 0; j < 8; j++) { | |
| 11 | crc = (crc >>> 1) ^ ((crc & 1) * 0xedb88320) | |
| 12 | } | |
| 13 | ||
| 14 | lookup[0][i] = crc | |
| 15 | } | |
| 16 | ||
| 17 | for (let i = 0; i <= 0xff; i++) { | |
| 18 | for (let j = 1; j < 16; j++) { | |
| 19 | lookup[j][i] = (lookup[j - 1][i] >>> 8) ^ lookup[0][lookup[j - 1][i] & 0xff] | |
| 20 | } | |
| 21 | } | |
| 22 | ||
| 23 | module.exports = lookup |
| … | … | @@ -0,0 +1,34 @@ |
| 1 | { | |
| 2 | "name": "crc-universal", | |
| 3 | "version": "1.0.4", | |
| 4 | "description": "Universal wrapper for libcrc with a JavaScript fallback", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "fallback.js", | |
| 8 | "index.js", | |
| 9 | "lookup.js" | |
| 10 | ], | |
| 11 | "browser": { | |
| 12 | "./index.js": "./fallback.js" | |
| 13 | }, | |
| 14 | "scripts": { | |
| 15 | "test": "standard && brittle test.mjs" | |
| 16 | }, | |
| 17 | "repository": { | |
| 18 | "type": "git", | |
| 19 | "url": "git+https://github.com/holepunchto/crc-universal.git" | |
| 20 | }, | |
| 21 | "author": "Kasper Isager Dalsgarð <kasper@funktionel.co>", | |
| 22 | "license": "ISC", | |
| 23 | "bugs": { | |
| 24 | "url": "https://github.com/holepunchto/crc-universal/issues" | |
| 25 | }, | |
| 26 | "homepage": "https://github.com/holepunchto/crc-universal#readme", | |
| 27 | "optionalDependencies": { | |
| 28 | "crc-native": "^1.0.3" | |
| 29 | }, | |
| 30 | "devDependencies": { | |
| 31 | "brittle": "^3.1.0", | |
| 32 | "standard": "^17.0.0" | |
| 33 | } | |
| 34 | } |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2020 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,47 @@ |
| 1 | # debounceify | |
| 2 | ||
| 3 | Tiny async debouncer | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install debounceify | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const debounceify = require('debounceify') | |
| 13 | ||
| 14 | const debounced = debounceify(async function () { | |
| 15 | console.log('Running...') | |
| 16 | await new Promise(resolve => setTimeout(1000, resolve)) | |
| 17 | }) | |
| 18 | ||
| 19 | const a = debounced() // triggers the about function | |
| 20 | const b = debounced() | |
| 21 | const c = debounced() | |
| 22 | const d = debounced() // these last 3 wait for the first one to finish | |
| 23 | // then trigger one more. | |
| 24 | ||
| 25 | await a | |
| 26 | await b | |
| 27 | await c | |
| 28 | await d | |
| 29 | ``` | |
| 30 | ||
| 31 | ## API | |
| 32 | ||
| 33 | #### `fn = debounceify(asyncFunction)` | |
| 34 | ||
| 35 | Debounces the `asyncFunction` so that | |
| 36 | ||
| 37 | 1. No parallel executions of `asyncFunction` will happen | |
| 38 | 2. Debounces the stack, so if 10 calls happen while `asyncFunction` is running only one more will be triggered when it finishes. | |
| 39 | 3. Forwards errors and return values. | |
| 40 | ||
| 41 | #### `debounceify.running` | |
| 42 | ||
| 43 | The active running job, `null` if none. | |
| 44 | ||
| 45 | ## License | |
| 46 | ||
| 47 | MIT |
| … | … | @@ -0,0 +1,25 @@ |
| 1 | module.exports = function debounce (worker, context = null) { | |
| 2 | debounced.running = null | |
| 3 | return debounced | |
| 4 | ||
| 5 | async function debounced () { | |
| 6 | if (debounced.running !== null) { | |
| 7 | try { | |
| 8 | await debounced.running | |
| 9 | } catch (_) { | |
| 10 | // ignore - do not fail on old errors | |
| 11 | } | |
| 12 | } | |
| 13 | ||
| 14 | // another "thread" beat us to it, just piggy pack on that one | |
| 15 | if (debounced.running !== null) return debounced.running | |
| 16 | ||
| 17 | debounced.running = worker.call(context) | |
| 18 | ||
| 19 | try { | |
| 20 | return await debounced.running | |
| 21 | } finally { | |
| 22 | debounced.running = null | |
| 23 | } | |
| 24 | } | |
| 25 | } |
| … | … | @@ -0,0 +1,24 @@ |
| 1 | { | |
| 2 | "name": "debounceify", | |
| 3 | "version": "1.1.0", | |
| 4 | "description": "Tiny async debouncer", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": {}, | |
| 7 | "devDependencies": { | |
| 8 | "standard": "^16.0.3", | |
| 9 | "tape": "^5.1.1" | |
| 10 | }, | |
| 11 | "repository": { | |
| 12 | "type": "git", | |
| 13 | "url": "https://github.com/mafintosh/debounceify.git" | |
| 14 | }, | |
| 15 | "scripts": { | |
| 16 | "test": "standard && tape test.js" | |
| 17 | }, | |
| 18 | "author": "Mathias Buus (@mafintosh)", | |
| 19 | "license": "MIT", | |
| 20 | "bugs": { | |
| 21 | "url": "https://github.com/mafintosh/debounceify/issues" | |
| 22 | }, | |
| 23 | "homepage": "https://github.com/mafintosh/debounceify" | |
| 24 | } |
| … | … | @@ -0,0 +1,45 @@ |
| 1 | const tape = require('tape') | |
| 2 | const debounce = require('./') | |
| 3 | ||
| 4 | tape('basic', async function (t) { | |
| 5 | t.plan(2) | |
| 6 | const d = debounce(() => { | |
| 7 | t.pass('called') | |
| 8 | }) | |
| 9 | ||
| 10 | for (let i = 0; i < 100; i++) d() | |
| 11 | await d() | |
| 12 | }) | |
| 13 | ||
| 14 | tape('debounced returns', async function (t) { | |
| 15 | t.plan(3) | |
| 16 | ||
| 17 | let cnt = 0 | |
| 18 | const d = debounce(() => { | |
| 19 | t.pass('called') | |
| 20 | return cnt++ | |
| 21 | }) | |
| 22 | ||
| 23 | for (let i = 0; i < 100; i++) d() | |
| 24 | t.same(await d(), 1) | |
| 25 | }) | |
| 26 | ||
| 27 | tape('debounced throws', async function (t) { | |
| 28 | t.plan(4) | |
| 29 | ||
| 30 | let cnt = 0 | |
| 31 | const d = debounce(async () => { | |
| 32 | t.pass('called') | |
| 33 | cnt++ | |
| 34 | if (cnt === 1) { | |
| 35 | throw new Error('sup') | |
| 36 | } | |
| 37 | return cnt | |
| 38 | }) | |
| 39 | ||
| 40 | d().catch(err => t.same(err, new Error('sup'))) | |
| 41 | d() | |
| 42 | d() | |
| 43 | d() | |
| 44 | t.same(await d(), 2) | |
| 45 | }) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,17 @@ |
| 1 | # events-universal | |
| 2 | ||
| 3 | Universal wrapper for the Node.js `events` module. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i events-universal | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const EventEmitter = require('events-universal') | |
| 13 | ``` | |
| 14 | ||
| 15 | ## License | |
| 16 | ||
| 17 | Apache-2.0 |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('bare-events') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('events') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('bare-events') |
| … | … | @@ -0,0 +1,39 @@ |
| 1 | { | |
| 2 | "name": "events-universal", | |
| 3 | "version": "1.0.1", | |
| 4 | "description": "Universal wrapper for the Node.js events module", | |
| 5 | "exports": { | |
| 6 | "./package": "./package.json", | |
| 7 | ".": { | |
| 8 | "bare": "./bare.js", | |
| 9 | "react-native": "./react-native.js", | |
| 10 | "default": "./default.js" | |
| 11 | } | |
| 12 | }, | |
| 13 | "files": [ | |
| 14 | "index.js", | |
| 15 | "default.js", | |
| 16 | "bare.js", | |
| 17 | "react-native.js" | |
| 18 | ], | |
| 19 | "scripts": { | |
| 20 | "test": "prettier . --check" | |
| 21 | }, | |
| 22 | "repository": { | |
| 23 | "type": "git", | |
| 24 | "url": "git+https://github.com/holepunchto/events-universal.git" | |
| 25 | }, | |
| 26 | "author": "Holepunch", | |
| 27 | "license": "Apache-2.0", | |
| 28 | "bugs": { | |
| 29 | "url": "https://github.com/holepunchto/events-universal/issues" | |
| 30 | }, | |
| 31 | "homepage": "https://github.com/holepunchto/events-universal#readme", | |
| 32 | "dependencies": { | |
| 33 | "bare-events": "^2.7.0" | |
| 34 | }, | |
| 35 | "devDependencies": { | |
| 36 | "prettier": "^3.4.2", | |
| 37 | "prettier-config-holepunch": "^1.0.0" | |
| 38 | } | |
| 39 | } |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('bare-events') |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2019 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,78 @@ |
| 1 | # fast-fifo | |
| 2 | ||
| 3 | A fast fifo implementation similar to the one powering nextTick in Node.js core | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install fast-fifo | |
| 7 | ``` | |
| 8 | ||
| 9 | Uses a linked list of growing fixed sized arrays to implement the FIFO to avoid | |
| 10 | allocating a wrapper object for each item. | |
| 11 | ||
| 12 | ## Usage | |
| 13 | ||
| 14 | ``` js | |
| 15 | const FIFO = require('fast-fifo') | |
| 16 | ||
| 17 | const q = new FIFO() | |
| 18 | ||
| 19 | q.push('hello') | |
| 20 | q.push('world') | |
| 21 | ||
| 22 | q.shift() // returns hello | |
| 23 | q.shift() // returns world | |
| 24 | ``` | |
| 25 | ||
| 26 | ## API | |
| 27 | ||
| 28 | #### `q = new FIFO()` | |
| 29 | ||
| 30 | Create a new FIFO. | |
| 31 | ||
| 32 | #### `q.push(value)` | |
| 33 | ||
| 34 | Push a value to the FIFO. `value` can be anything other than undefined. | |
| 35 | ||
| 36 | #### `value = q.shift()` | |
| 37 | ||
| 38 | Return the oldest value from the FIFO. | |
| 39 | ||
| 40 | #### `q.clear()` | |
| 41 | ||
| 42 | Remove all values from the FIFO. | |
| 43 | ||
| 44 | #### `bool = q.isEmpty()` | |
| 45 | ||
| 46 | Returns `true` if the FIFO is empty and false otherwise. | |
| 47 | ||
| 48 | #### `value = q.peek()` | |
| 49 | ||
| 50 | Return the oldest value from the FIFO without shifting it out. | |
| 51 | ||
| 52 | #### `len = q.length` | |
| 53 | ||
| 54 | Get the number of entries remaining in the FIFO. | |
| 55 | ||
| 56 | ## Benchmarks | |
| 57 | ||
| 58 | Included in bench.js is a simple benchmark that benchmarks this against a simple | |
| 59 | linked list based FIFO. | |
| 60 | ||
| 61 | On my machine the benchmark looks like this: | |
| 62 | ||
| 63 | ``` | |
| 64 | fifo bulk push and shift: 2881.508ms | |
| 65 | fifo individual push and shift: 3248.437ms | |
| 66 | fast-fifo bulk push and shift: 1606.972ms | |
| 67 | fast-fifo individual push and shift: 1328.064ms | |
| 68 | fifo bulk push and shift: 3266.902ms | |
| 69 | fifo individual push and shift: 3320.944ms | |
| 70 | fast-fifo bulk push and shift: 1858.307ms | |
| 71 | fast-fifo individual push and shift: 1516.983ms | |
| 72 | ``` | |
| 73 | ||
| 74 | YMMV | |
| 75 | ||
| 76 | ## License | |
| 77 | ||
| 78 | MIT |
| … | … | @@ -0,0 +1,39 @@ |
| 1 | module.exports = class FixedFIFO { | |
| 2 | constructor (hwm) { | |
| 3 | if (!(hwm > 0) || ((hwm - 1) & hwm) !== 0) throw new Error('Max size for a FixedFIFO should be a power of two') | |
| 4 | this.buffer = new Array(hwm) | |
| 5 | this.mask = hwm - 1 | |
| 6 | this.top = 0 | |
| 7 | this.btm = 0 | |
| 8 | this.next = null | |
| 9 | } | |
| 10 | ||
| 11 | clear () { | |
| 12 | this.top = this.btm = 0 | |
| 13 | this.next = null | |
| 14 | this.buffer.fill(undefined) | |
| 15 | } | |
| 16 | ||
| 17 | push (data) { | |
| 18 | if (this.buffer[this.top] !== undefined) return false | |
| 19 | this.buffer[this.top] = data | |
| 20 | this.top = (this.top + 1) & this.mask | |
| 21 | return true | |
| 22 | } | |
| 23 | ||
| 24 | shift () { | |
| 25 | const last = this.buffer[this.btm] | |
| 26 | if (last === undefined) return undefined | |
| 27 | this.buffer[this.btm] = undefined | |
| 28 | this.btm = (this.btm + 1) & this.mask | |
| 29 | return last | |
| 30 | } | |
| 31 | ||
| 32 | peek () { | |
| 33 | return this.buffer[this.btm] | |
| 34 | } | |
| 35 | ||
| 36 | isEmpty () { | |
| 37 | return this.buffer[this.btm] === undefined | |
| 38 | } | |
| 39 | } |
| … | … | @@ -0,0 +1,48 @@ |
| 1 | const FixedFIFO = require('./fixed-size') | |
| 2 | ||
| 3 | module.exports = class FastFIFO { | |
| 4 | constructor (hwm) { | |
| 5 | this.hwm = hwm || 16 | |
| 6 | this.head = new FixedFIFO(this.hwm) | |
| 7 | this.tail = this.head | |
| 8 | this.length = 0 | |
| 9 | } | |
| 10 | ||
| 11 | clear () { | |
| 12 | this.head = this.tail | |
| 13 | this.head.clear() | |
| 14 | this.length = 0 | |
| 15 | } | |
| 16 | ||
| 17 | push (val) { | |
| 18 | this.length++ | |
| 19 | if (!this.head.push(val)) { | |
| 20 | const prev = this.head | |
| 21 | this.head = prev.next = new FixedFIFO(2 * this.head.buffer.length) | |
| 22 | this.head.push(val) | |
| 23 | } | |
| 24 | } | |
| 25 | ||
| 26 | shift () { | |
| 27 | if (this.length !== 0) this.length-- | |
| 28 | const val = this.tail.shift() | |
| 29 | if (val === undefined && this.tail.next) { | |
| 30 | const next = this.tail.next | |
| 31 | this.tail.next = null | |
| 32 | this.tail = next | |
| 33 | return this.tail.shift() | |
| 34 | } | |
| 35 | ||
| 36 | return val | |
| 37 | } | |
| 38 | ||
| 39 | peek () { | |
| 40 | const val = this.tail.peek() | |
| 41 | if (val === undefined && this.tail.next) return this.tail.next.peek() | |
| 42 | return val | |
| 43 | } | |
| 44 | ||
| 45 | isEmpty () { | |
| 46 | return this.length === 0 | |
| 47 | } | |
| 48 | } |
| … | … | @@ -0,0 +1,28 @@ |
| 1 | { | |
| 2 | "name": "fast-fifo", | |
| 3 | "version": "1.3.2", | |
| 4 | "description": "A fast fifo implementation similar to the one powering nextTick in Node.js core", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "./index.js", | |
| 8 | "./fixed-size.js" | |
| 9 | ], | |
| 10 | "dependencies": {}, | |
| 11 | "devDependencies": { | |
| 12 | "standard": "^17.1.0", | |
| 13 | "brittle": "^3.3.2" | |
| 14 | }, | |
| 15 | "scripts": { | |
| 16 | "test": "standard && brittle test.js" | |
| 17 | }, | |
| 18 | "repository": { | |
| 19 | "type": "git", | |
| 20 | "url": "https://github.com/mafintosh/fast-fifo.git" | |
| 21 | }, | |
| 22 | "author": "Mathias Buus (@mafintosh)", | |
| 23 | "license": "MIT", | |
| 24 | "bugs": { | |
| 25 | "url": "https://github.com/mafintosh/fast-fifo/issues" | |
| 26 | }, | |
| 27 | "homepage": "https://github.com/mafintosh/fast-fifo" | |
| 28 | } |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2015 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,167 @@ |
| 1 | # flat-tree | |
| 2 | ||
| 3 | A series of functions to map a binary tree to a list | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install flat-tree | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | You can represent a binary tree in a simple flat list using the following structure | |
| 12 | ||
| 13 | ``` | |
| 14 | 3 | |
| 15 | 1 5 | |
| 16 | 0 2 4 6 ... | |
| 17 | ``` | |
| 18 | ||
| 19 | This module exposes a series of functions to help you build and maintain this data structure | |
| 20 | ||
| 21 | ``` js | |
| 22 | var tree = require('flat-tree') | |
| 23 | var list = [] | |
| 24 | ||
| 25 | var i = tree.index(0, 0) // get array index for depth: 0, offset: 0 | |
| 26 | var j = tree.index(1, 0) // get array index for depth: 1, offset: 0 | |
| 27 | ||
| 28 | // use these indexes to store some data | |
| 29 | ||
| 30 | list[i] = 'a' | |
| 31 | list[j] = 'b' | |
| 32 | list[tree.parent(i)] = 'parent of a and b' | |
| 33 | ``` | |
| 34 | ||
| 35 | ## API | |
| 36 | ||
| 37 | #### `index = tree.index(depth, offset)` | |
| 38 | ||
| 39 | Returns an array index for the tree element at the given depth and offset. | |
| 40 | ||
| 41 | #### `parentIndex = tree.parent(index)` | |
| 42 | ||
| 43 | Returns the index of the parent element in tree. | |
| 44 | ||
| 45 | #### `siblingIndex = tree.sibling(index)` | |
| 46 | ||
| 47 | Returns the index of this elements sibling. | |
| 48 | ||
| 49 | #### `children = tree.children(index)` | |
| 50 | ||
| 51 | Returns an array `[leftChild, rightChild]` with the indexes of this elements children. | |
| 52 | If this element does not have any children it returns `null`. | |
| 53 | ||
| 54 | #### `range = tree.spans(index)` | |
| 55 | ||
| 56 | Returns the range (inclusive) the tree root at `index` spans. | |
| 57 | For example `tree.spans(3)` would return `[0, 6]` (see the usage example). | |
| 58 | ||
| 59 | #### `index = tree.leftSpan(index)` | |
| 60 | ||
| 61 | Returns the left spanning in index in the tree `index` spans. | |
| 62 | ||
| 63 | #### `index = tree.rightSpan(index)` | |
| 64 | ||
| 65 | Returns the right spanning in index in the tree `index` spans. | |
| 66 | ||
| 67 | #### `count = tree.count(index)` | |
| 68 | ||
| 69 | Returns how many nodes (including parent nodes) a tree contains. | |
| 70 | ||
| 71 | #### `count = tree.countLeaves(index)` | |
| 72 | ||
| 73 | Returns how many nodes (excluding parent nodes) a tree contains. | |
| 74 | ||
| 75 | #### `depth = tree.depth(index)` | |
| 76 | ||
| 77 | Returns the depth of an element. | |
| 78 | ||
| 79 | #### `offset = tree.offset(index, [depth])` | |
| 80 | ||
| 81 | Returns the relative offset of an element. | |
| 82 | ||
| 83 | #### `roots = tree.fullRoots(index)` | |
| 84 | ||
| 85 | Returns a list of all the full roots (subtrees where all nodes have either 2 or 0 children) `<` index. | |
| 86 | For example `fullRoots(8)` returns `[3]` since the subtree rooted at `3` spans `0 -> 6` and the tree | |
| 87 | rooted at `7` has a child located at `9` which is `>= 8`. | |
| 88 | ||
| 89 | #### `iterator = tree.iterator(index)` | |
| 90 | ||
| 91 | Create a stateful tree iterator starting at a given index. | |
| 92 | The iterator exposes the following methods. | |
| 93 | ||
| 94 | #### `index = iterator.next()` | |
| 95 | ||
| 96 | Move the iterator the next item in the tree. | |
| 97 | ||
| 98 | #### `index = iterator.prev()` | |
| 99 | ||
| 100 | Move the iterator the prev item in the tree. | |
| 101 | ||
| 102 | #### `iterator.seek(index)` | |
| 103 | ||
| 104 | Move the iterator the this specific tree index. | |
| 105 | ||
| 106 | #### `index = iterator.parent()` | |
| 107 | ||
| 108 | Move the iterator to the current parent index. | |
| 109 | ||
| 110 | #### `index = iterator.leftChild()` | |
| 111 | ||
| 112 | Move the iterator to the current left child index. | |
| 113 | ||
| 114 | #### `index = iterator.rightChild()` | |
| 115 | ||
| 116 | Move the iterator to the current right child index. | |
| 117 | ||
| 118 | #### `index = iterator.leftSpan()` | |
| 119 | ||
| 120 | Move the iterator to the current left span index. | |
| 121 | ||
| 122 | #### `index = iterator.rightSpan()` | |
| 123 | ||
| 124 | Move the iterator to the current right span index. | |
| 125 | ||
| 126 | #### `bool = iterator.isLeft()` | |
| 127 | ||
| 128 | Is the iterator at a left sibling? | |
| 129 | ||
| 130 | #### `bool = iterator.isRight()` | |
| 131 | ||
| 132 | Is the iterator at a right sibling? | |
| 133 | ||
| 134 | #### `index = iterator.sibling()` | |
| 135 | ||
| 136 | Move the iterator to the current sibling. | |
| 137 | ||
| 138 | #### `count = iterator.count()` | |
| 139 | ||
| 140 | Returns how many nodes (including parent nodes) the current tree contains. | |
| 141 | ||
| 142 | #### `count = iterator.countLeaves()` | |
| 143 | ||
| 144 | Returns how many nodes (excluding parent nodes) the current tree contains. | |
| 145 | ||
| 146 | ## See also | |
| 147 | ||
| 148 | - [mafintosh/print-flat-tree][print]: A cli that can pretty print flat-trees. | |
| 149 | - [bcomnes/flattree][ftg]: A port of the node module to Go. | |
| 150 | - [arablocks/flat-tree.c](c): A port of the module to C | |
| 151 | - [datkt/flat-tree][kt]: A port of the module to Kotlin | |
| 152 | - [datrs/flat-tree][rs]: A port to Rust. | |
| 153 | - [bcomnes/flattree][ftg]: A port to Go. | |
| 154 | - [noffle/cl-flat-tree][clft]: A port to Common Lisp. | |
| 155 | - [dukeraphaelng/flat_tree][cr]: A port to Crystal. | |
| 156 | ||
| 157 | ## License | |
| 158 | ||
| 159 | MIT | |
| 160 | ||
| 161 | [print]: https://github.com/mafintosh/print-flat-tree | |
| 162 | [rs]: https://github.com/datrs/flat-tree | |
| 163 | [ftg]: https://github.com/bcomnes/flattree | |
| 164 | [c]: https://github.com/arablocks/flat-tree.c | |
| 165 | [kt]: https://github.com/datkt/flat-tree | |
| 166 | [clft]: https://github.com/noffle/cl-flat-tree | |
| 167 | [cr]: https://github.com/dukeraphaelng/flat_tree |
| … | … | @@ -0,0 +1,362 @@ |
| 1 | exports.fullRoots = function (index, result) { | |
| 2 | if (index & 1) throw new Error('You can only look up roots for depth(0) blocks') | |
| 3 | if (!result) result = [] | |
| 4 | ||
| 5 | index /= 2 | |
| 6 | ||
| 7 | let offset = 0 | |
| 8 | let factor = 1 | |
| 9 | ||
| 10 | while (true) { | |
| 11 | if (!index) return result | |
| 12 | while (factor * 2 <= index) factor *= 2 | |
| 13 | result.push(offset + factor - 1) | |
| 14 | offset = offset + 2 * factor | |
| 15 | index -= factor | |
| 16 | factor = 1 | |
| 17 | } | |
| 18 | } | |
| 19 | ||
| 20 | exports.futureRoots = function (index, result) { | |
| 21 | if (index & 1) throw new Error('You can only look up future roots for depth(0) blocks') | |
| 22 | if (!result) result = [] | |
| 23 | ||
| 24 | let factor = 1 | |
| 25 | ||
| 26 | // make first root | |
| 27 | while (factor * 2 <= index) factor *= 2 | |
| 28 | ||
| 29 | // full factor of 2 - done | |
| 30 | if (factor * 2 - 2 === index) return result | |
| 31 | ||
| 32 | let pos = factor / 2 - 1 | |
| 33 | ||
| 34 | // while its not a full tree | |
| 35 | while ((pos + factor / 2 - 1) !== index) { | |
| 36 | pos += factor | |
| 37 | ||
| 38 | // read too far, to to left child | |
| 39 | while ((pos + factor / 2 - 1) > index) { | |
| 40 | factor /= 2 | |
| 41 | pos -= factor / 2 | |
| 42 | } | |
| 43 | ||
| 44 | // the "gap" is a future root | |
| 45 | result.push(pos - factor / 2) | |
| 46 | } | |
| 47 | ||
| 48 | return result | |
| 49 | } | |
| 50 | ||
| 51 | exports.patch = function (from, to) { | |
| 52 | if (from === 0 || from >= to) return [] | |
| 53 | ||
| 54 | const roots = exports.fullRoots(from) | |
| 55 | const target = exports.fullRoots(to) | |
| 56 | ||
| 57 | // first find the first root that is different | |
| 58 | ||
| 59 | let i = 0 | |
| 60 | for (; i < target.length; i++) { | |
| 61 | if (i >= roots.length || roots[i] !== target[i]) break | |
| 62 | } | |
| 63 | ||
| 64 | const patch = [] | |
| 65 | ||
| 66 | if (i < roots.length) { | |
| 67 | // now we need to grow the newest root until it hits the diff one | |
| 68 | let prev = roots.length - 1 | |
| 69 | ||
| 70 | const ite = exports.iterator(roots[prev--]) | |
| 71 | ||
| 72 | while (ite.index !== target[i]) { | |
| 73 | ite.sibling() | |
| 74 | ||
| 75 | if (prev >= 0 && ite.index === roots[prev]) { | |
| 76 | prev-- | |
| 77 | } else { | |
| 78 | patch.push(ite.index) | |
| 79 | } | |
| 80 | ||
| 81 | patch.push(ite.parent()) | |
| 82 | } | |
| 83 | ||
| 84 | i++ // patched to next root, so inc | |
| 85 | } | |
| 86 | ||
| 87 | // include the rest | |
| 88 | ||
| 89 | for (; i < target.length; i++) patch.push(target[i]) | |
| 90 | ||
| 91 | return patch | |
| 92 | } | |
| 93 | ||
| 94 | exports.depth = function (index) { | |
| 95 | let depth = 0 | |
| 96 | ||
| 97 | index += 1 | |
| 98 | while (!(index & 1)) { | |
| 99 | depth++ | |
| 100 | index = rightShift(index) | |
| 101 | } | |
| 102 | ||
| 103 | return depth | |
| 104 | } | |
| 105 | ||
| 106 | exports.sibling = function (index, depth) { | |
| 107 | if (!depth) depth = exports.depth(index) | |
| 108 | const offset = exports.offset(index, depth) | |
| 109 | ||
| 110 | return exports.index(depth, offset & 1 ? offset - 1 : offset + 1) | |
| 111 | } | |
| 112 | ||
| 113 | exports.parent = function (index, depth) { | |
| 114 | if (!depth) depth = exports.depth(index) | |
| 115 | const offset = exports.offset(index, depth) | |
| 116 | ||
| 117 | return exports.index(depth + 1, rightShift(offset)) | |
| 118 | } | |
| 119 | ||
| 120 | exports.leftChild = function (index, depth) { | |
| 121 | if (!(index & 1)) return -1 | |
| 122 | if (!depth) depth = exports.depth(index) | |
| 123 | return exports.index(depth - 1, exports.offset(index, depth) * 2) | |
| 124 | } | |
| 125 | ||
| 126 | exports.rightChild = function (index, depth) { | |
| 127 | if (!(index & 1)) return -1 | |
| 128 | if (!depth) depth = exports.depth(index) | |
| 129 | return exports.index(depth - 1, 1 + (exports.offset(index, depth) * 2)) | |
| 130 | } | |
| 131 | ||
| 132 | exports.children = function (index, depth) { | |
| 133 | if (!(index & 1)) return null | |
| 134 | ||
| 135 | if (!depth) depth = exports.depth(index) | |
| 136 | const offset = exports.offset(index, depth) * 2 | |
| 137 | ||
| 138 | return [ | |
| 139 | exports.index(depth - 1, offset), | |
| 140 | exports.index(depth - 1, offset + 1) | |
| 141 | ] | |
| 142 | } | |
| 143 | ||
| 144 | exports.leftSpan = function (index, depth) { | |
| 145 | if (!(index & 1)) return index | |
| 146 | if (!depth) depth = exports.depth(index) | |
| 147 | return exports.offset(index, depth) * twoPow(depth + 1) | |
| 148 | } | |
| 149 | ||
| 150 | exports.rightSpan = function (index, depth) { | |
| 151 | if (!(index & 1)) return index | |
| 152 | if (!depth) depth = exports.depth(index) | |
| 153 | return (exports.offset(index, depth) + 1) * twoPow(depth + 1) - 2 | |
| 154 | } | |
| 155 | ||
| 156 | exports.nextLeaf = function (index) { | |
| 157 | let factor = 1 | |
| 158 | let r = index | |
| 159 | ||
| 160 | while ((r & 1) === 1) { | |
| 161 | r = (r - 1) / 2 | |
| 162 | factor *= 2 | |
| 163 | } | |
| 164 | ||
| 165 | return index + factor + 1 | |
| 166 | } | |
| 167 | ||
| 168 | exports.count = function (index, depth) { | |
| 169 | if (!(index & 1)) return 1 | |
| 170 | if (!depth) depth = exports.depth(index) | |
| 171 | return twoPow(depth + 1) - 1 | |
| 172 | } | |
| 173 | ||
| 174 | exports.countLeaves = function (index) { | |
| 175 | return (exports.count(index) + 1) / 2 | |
| 176 | } | |
| 177 | ||
| 178 | exports.spans = function (index, depth) { | |
| 179 | if (!(index & 1)) return [index, index] | |
| 180 | if (!depth) depth = exports.depth(index) | |
| 181 | ||
| 182 | const offset = exports.offset(index, depth) | |
| 183 | const width = twoPow(depth + 1) | |
| 184 | ||
| 185 | return [offset * width, (offset + 1) * width - 2] | |
| 186 | } | |
| 187 | ||
| 188 | exports.index = function (depth, offset) { | |
| 189 | return (1 + 2 * offset) * twoPow(depth) - 1 | |
| 190 | } | |
| 191 | ||
| 192 | exports.offset = function (index, depth) { | |
| 193 | if (!(index & 1)) return index / 2 | |
| 194 | if (!depth) depth = exports.depth(index) | |
| 195 | ||
| 196 | return ((index + 1) / twoPow(depth) - 1) / 2 | |
| 197 | } | |
| 198 | ||
| 199 | exports.iterator = function (index) { | |
| 200 | const ite = new Iterator() | |
| 201 | ite.seek(index || 0) | |
| 202 | return ite | |
| 203 | } | |
| 204 | ||
| 205 | function twoPow (n) { | |
| 206 | return n < 31 ? 1 << n : ((1 << 30) * (1 << (n - 30))) | |
| 207 | } | |
| 208 | ||
| 209 | function rightShift (n) { | |
| 210 | return (n - (n & 1)) / 2 | |
| 211 | } | |
| 212 | ||
| 213 | function Iterator () { | |
| 214 | this.index = 0 | |
| 215 | this.offset = 0 | |
| 216 | this.factor = 0 | |
| 217 | } | |
| 218 | ||
| 219 | Iterator.prototype.seek = function (index) { | |
| 220 | this.index = index | |
| 221 | if (this.index & 1) { | |
| 222 | this.offset = exports.offset(index) | |
| 223 | this.factor = twoPow(exports.depth(index) + 1) | |
| 224 | } else { | |
| 225 | this.offset = index / 2 | |
| 226 | this.factor = 2 | |
| 227 | } | |
| 228 | } | |
| 229 | ||
| 230 | Iterator.prototype.isLeft = function () { | |
| 231 | return (this.offset & 1) === 0 | |
| 232 | } | |
| 233 | ||
| 234 | Iterator.prototype.isRight = function () { | |
| 235 | return (this.offset & 1) === 1 | |
| 236 | } | |
| 237 | ||
| 238 | Iterator.prototype.isRoot = function (length) { | |
| 239 | const currentLength = 1 + (this.index + this.factor / 2 - 1) / 2 | |
| 240 | if (length < currentLength) return false | |
| 241 | ||
| 242 | const factor = this.factor * 2 | |
| 243 | const index = (this.offset & 1) | |
| 244 | ? this.index - this.factor / 2 | |
| 245 | : this.index + this.factor / 2 | |
| 246 | ||
| 247 | const parentLength = 1 + (index + factor / 2 - 1) / 2 | |
| 248 | return parentLength > length | |
| 249 | } | |
| 250 | ||
| 251 | Iterator.prototype.contains = function (index) { | |
| 252 | return index > this.index | |
| 253 | ? index < (this.index + this.factor / 2) | |
| 254 | : index < this.index | |
| 255 | ? index > (this.index - this.factor / 2) | |
| 256 | : true | |
| 257 | } | |
| 258 | ||
| 259 | Iterator.prototype.prev = function () { | |
| 260 | if (!this.offset) return this.index | |
| 261 | this.offset-- | |
| 262 | this.index -= this.factor | |
| 263 | return this.index | |
| 264 | } | |
| 265 | ||
| 266 | Iterator.prototype.next = function () { | |
| 267 | this.offset++ | |
| 268 | this.index += this.factor | |
| 269 | return this.index | |
| 270 | } | |
| 271 | ||
| 272 | Iterator.prototype.count = function () { | |
| 273 | if (!(this.index & 1)) return 1 | |
| 274 | return this.factor - 1 | |
| 275 | } | |
| 276 | ||
| 277 | Iterator.prototype.countLeaves = function () { | |
| 278 | return (this.count() + 1) / 2 | |
| 279 | } | |
| 280 | ||
| 281 | Iterator.prototype.sibling = function () { | |
| 282 | return this.isLeft() ? this.next() : this.prev() | |
| 283 | } | |
| 284 | ||
| 285 | Iterator.prototype.parent = function () { | |
| 286 | if (this.offset & 1) { | |
| 287 | this.index -= this.factor / 2 | |
| 288 | this.offset = (this.offset - 1) / 2 | |
| 289 | } else { | |
| 290 | this.index += this.factor / 2 | |
| 291 | this.offset /= 2 | |
| 292 | } | |
| 293 | this.factor *= 2 | |
| 294 | return this.index | |
| 295 | } | |
| 296 | ||
| 297 | Iterator.prototype.leftSpan = function () { | |
| 298 | this.index = this.index - this.factor / 2 + 1 | |
| 299 | this.offset = this.index / 2 | |
| 300 | this.factor = 2 | |
| 301 | return this.index | |
| 302 | } | |
| 303 | ||
| 304 | Iterator.prototype.peekLeftSpan = function () { | |
| 305 | return this.index - this.factor / 2 + 1 | |
| 306 | } | |
| 307 | ||
| 308 | Iterator.prototype.rightSpan = function () { | |
| 309 | this.index = this.index + this.factor / 2 - 1 | |
| 310 | this.offset = this.index / 2 | |
| 311 | this.factor = 2 | |
| 312 | return this.index | |
| 313 | } | |
| 314 | ||
| 315 | Iterator.prototype.peekRightSpan = function () { | |
| 316 | return this.index + this.factor / 2 - 1 | |
| 317 | } | |
| 318 | ||
| 319 | Iterator.prototype.leftChild = function () { | |
| 320 | if (this.factor === 2) return this.index | |
| 321 | this.factor /= 2 | |
| 322 | this.index -= this.factor / 2 | |
| 323 | this.offset *= 2 | |
| 324 | return this.index | |
| 325 | } | |
| 326 | ||
| 327 | Iterator.prototype.rightChild = function () { | |
| 328 | if (this.factor === 2) return this.index | |
| 329 | this.factor /= 2 | |
| 330 | this.index += this.factor / 2 | |
| 331 | this.offset = 2 * this.offset + 1 | |
| 332 | return this.index | |
| 333 | } | |
| 334 | ||
| 335 | Iterator.prototype.nextTree = function () { | |
| 336 | this.index = this.index + this.factor / 2 + 1 | |
| 337 | this.offset = this.index / 2 | |
| 338 | this.factor = 2 | |
| 339 | return this.index | |
| 340 | } | |
| 341 | ||
| 342 | Iterator.prototype.prevTree = function () { | |
| 343 | if (!this.offset) { | |
| 344 | this.index = 0 | |
| 345 | this.factor = 2 | |
| 346 | } else { | |
| 347 | this.index = this.index - this.factor / 2 - 1 | |
| 348 | this.offset = this.index / 2 | |
| 349 | this.factor = 2 | |
| 350 | } | |
| 351 | return this.index | |
| 352 | } | |
| 353 | ||
| 354 | Iterator.prototype.fullRoot = function (index) { | |
| 355 | if (index <= this.index || (this.index & 1) > 0) return false | |
| 356 | while (index > this.index + this.factor + this.factor / 2) { | |
| 357 | this.index += this.factor / 2 | |
| 358 | this.factor *= 2 | |
| 359 | this.offset /= 2 | |
| 360 | } | |
| 361 | return true | |
| 362 | } |
| … | … | @@ -0,0 +1,27 @@ |
| 1 | { | |
| 2 | "name": "flat-tree", | |
| 3 | "version": "1.13.0", | |
| 4 | "description": "A series of functions to map a binary tree to a list", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js" | |
| 8 | ], | |
| 9 | "dependencies": {}, | |
| 10 | "devDependencies": { | |
| 11 | "brittle": "^3.3.2", | |
| 12 | "standard": "^17.1.0" | |
| 13 | }, | |
| 14 | "scripts": { | |
| 15 | "test": "standard && brittle test.js" | |
| 16 | }, | |
| 17 | "repository": { | |
| 18 | "type": "git", | |
| 19 | "url": "https://github.com/mafintosh/flat-tree.git" | |
| 20 | }, | |
| 21 | "author": "Mathias Buus (@mafintosh)", | |
| 22 | "license": "MIT", | |
| 23 | "bugs": { | |
| 24 | "url": "https://github.com/mafintosh/flat-tree/issues" | |
| 25 | }, | |
| 26 | "homepage": "https://github.com/mafintosh/flat-tree" | |
| 27 | } |
| … | … | @@ -0,0 +1,118 @@ |
| 1 | cmake_minimum_required(VERSION 3.25) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) | |
| 5 | find_package(cmake-napi REQUIRED PATHS node_modules/cmake-napi) | |
| 6 | find_package(cmake-npm REQUIRED PATHS node_modules/cmake-npm) | |
| 7 | ||
| 8 | project(fs_native_extensions C) | |
| 9 | ||
| 10 | fetch_package("github:libuv/libuv@1.51.0") | |
| 11 | ||
| 12 | add_library(fs_native_extensions STATIC) | |
| 13 | ||
| 14 | target_sources( | |
| 15 | fs_native_extensions | |
| 16 | INTERFACE | |
| 17 | include/fs-ext.h | |
| 18 | PRIVATE | |
| 19 | src/platform.h | |
| 20 | src/shared.c | |
| 21 | ) | |
| 22 | ||
| 23 | target_include_directories( | |
| 24 | fs_native_extensions | |
| 25 | INTERFACE | |
| 26 | include | |
| 27 | PRIVATE | |
| 28 | $<TARGET_PROPERTY:uv,INTERFACE_INCLUDE_DIRECTORIES> | |
| 29 | ) | |
| 30 | ||
| 31 | if(APPLE) | |
| 32 | target_sources( | |
| 33 | fs_native_extensions | |
| 34 | PRIVATE | |
| 35 | src/apple.c | |
| 36 | ) | |
| 37 | endif() | |
| 38 | ||
| 39 | if(LINUX OR ANDROID) | |
| 40 | target_sources( | |
| 41 | fs_native_extensions | |
| 42 | PRIVATE | |
| 43 | src/linux.c | |
| 44 | ) | |
| 45 | endif() | |
| 46 | ||
| 47 | if(WIN32) | |
| 48 | target_sources( | |
| 49 | fs_native_extensions | |
| 50 | PRIVATE | |
| 51 | src/win32.c | |
| 52 | src/win32/nt.h | |
| 53 | ) | |
| 54 | else() | |
| 55 | target_sources( | |
| 56 | fs_native_extensions | |
| 57 | PRIVATE | |
| 58 | src/posix.c | |
| 59 | ) | |
| 60 | endif() | |
| 61 | ||
| 62 | add_bare_module(fs_native_extensions_bare) | |
| 63 | ||
| 64 | target_sources( | |
| 65 | ${fs_native_extensions_bare} | |
| 66 | PRIVATE | |
| 67 | binding.c | |
| 68 | ) | |
| 69 | ||
| 70 | target_link_libraries( | |
| 71 | ${fs_native_extensions_bare} | |
| 72 | PRIVATE | |
| 73 | fs_native_extensions | |
| 74 | ) | |
| 75 | ||
| 76 | if(WIN32) | |
| 77 | target_link_libraries( | |
| 78 | ${fs_native_extensions_bare} | |
| 79 | PUBLIC | |
| 80 | ntdll | |
| 81 | ) | |
| 82 | endif() | |
| 83 | ||
| 84 | add_napi_module(fs_native_extensions_napi) | |
| 85 | ||
| 86 | target_sources( | |
| 87 | ${fs_native_extensions_napi} | |
| 88 | PRIVATE | |
| 89 | binding.c | |
| 90 | ) | |
| 91 | ||
| 92 | target_link_libraries( | |
| 93 | ${fs_native_extensions_napi} | |
| 94 | PRIVATE | |
| 95 | fs_native_extensions | |
| 96 | ) | |
| 97 | ||
| 98 | target_compile_definitions( | |
| 99 | ${fs_native_extensions_napi} | |
| 100 | PRIVATE | |
| 101 | NAPI_VERSION=9 | |
| 102 | ) | |
| 103 | ||
| 104 | resolve_node_module(bare-compat-napi compat) | |
| 105 | ||
| 106 | target_include_directories( | |
| 107 | ${fs_native_extensions_napi} | |
| 108 | PRIVATE | |
| 109 | "${compat}/include" | |
| 110 | ) | |
| 111 | ||
| 112 | if(WIN32) | |
| 113 | target_link_libraries( | |
| 114 | ${fs_native_extensions_napi} | |
| 115 | PUBLIC | |
| 116 | ntdll | |
| 117 | ) | |
| 118 | endif() |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,118 @@ |
| 1 | # fs-native-extensions | |
| 2 | ||
| 3 | Native file system extensions for advanced file operations | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install fs-native-extensions | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | Write to a file using an exclusive lock: | |
| 12 | ||
| 13 | ```js | |
| 14 | const { open } = require('fs/promises') | |
| 15 | const { waitForLock, unlock } = require('fs-native-extensions') | |
| 16 | ||
| 17 | const file = await open('file.txt', 'a+') | |
| 18 | ||
| 19 | await waitForLock(file.fd) | |
| 20 | ||
| 21 | try { | |
| 22 | await file.write('hello world') | |
| 23 | } finally { | |
| 24 | unlock(file.fd) | |
| 25 | } | |
| 26 | ``` | |
| 27 | ||
| 28 | ## API | |
| 29 | ||
| 30 | #### `const granted = tryLock(fd[, offset[, length]][, options])` | |
| 31 | ||
| 32 | Request a lock on a file, returning `true` if the lock was granted or `false` if another file descriptor currently holds the lock. | |
| 33 | ||
| 34 | To lock only a portion of the file, `offset` and `length` may be passed. A `length` of `0` will request a lock from `offset` to the end of the file. | |
| 35 | ||
| 36 | On macOS, BSD locks are used and so only locks on the whole file are supported. | |
| 37 | ||
| 38 | Note that the lock is only advisory and there is nothing stopping another process from accessing the file by simply ignoring the lock. | |
| 39 | ||
| 40 | Options include: | |
| 41 | ||
| 42 | ```js | |
| 43 | { | |
| 44 | // If `true`, request a shared lock, i.e. a read lock, on the file. By | |
| 45 | // default, an exclusive lock, i.e. a write lock, is requested. | |
| 46 | // Be aware that an exclusive lock can only be granted to files that are | |
| 47 | // writable! | |
| 48 | shared: false | |
| 49 | } | |
| 50 | ``` | |
| 51 | ||
| 52 | #### `await waitForLock(fd[, offset[, length]][, options])` | |
| 53 | ||
| 54 | Request a lock on a file, resolving when the lock is granted. If another file descriptor holds the lock, the lock will not be granted until the other file descriptor releases the lock. | |
| 55 | ||
| 56 | Options are the same as `tryLock()`. | |
| 57 | ||
| 58 | #### `const granted = tryDowngradeLock(fd[, offset[, length]])` | |
| 59 | ||
| 60 | Request a downgrade from an already held exclusive lock to a shared lock, returning `true` if the lock was granted or `false` if another process currently holds the lock. If `false` is returned, the exclusive lock is lost and must be requested again. | |
| 61 | ||
| 62 | On Windows, the downgrade will happen atomically and be immediately granted. | |
| 63 | ||
| 64 | #### `await waitForDowngradeLock(fd[, offset[, length]])` | |
| 65 | ||
| 66 | Request a downgrade from an already held exclusive lock to a shared lock. | |
| 67 | ||
| 68 | On Windows, the downgrade will happen atomically and be immediately granted. | |
| 69 | ||
| 70 | #### `const granted = tryUpgradeLock(fd[, offset[, length]])` | |
| 71 | ||
| 72 | Request an upgrade from an already held shared lock to an exclusive lock, returning `true` if the lock was granted or `false` if another process currently holds the lock. If `false` is returned, the shared lock is lost and must be requested again. | |
| 73 | ||
| 74 | #### `await waitForUpgradeLock(fd[, offset[, length]])` | |
| 75 | ||
| 76 | Request an upgrade from an already held shared lock to an exclusive lock. | |
| 77 | ||
| 78 | #### `unlock(fd[, offset[, length]])` | |
| 79 | ||
| 80 | Release a lock on a file. | |
| 81 | ||
| 82 | #### `await trim(fd, offset, length)` | |
| 83 | ||
| 84 | Create a hole in a file at `offset` for `length` bytes. On file systems that support sparse files, file blocks wholly covered by a hole will take up no physical space. | |
| 85 | ||
| 86 | On Windows, the file must first be marked sparse using `sparse(fd)`. Otherwise, zeros will be explicitly written to the hole. | |
| 87 | ||
| 88 | #### `await sparse(fd)` | |
| 89 | ||
| 90 | Mark a file as sparse. On Windows, this operation is required before holes can be created in the file. On other systems, this operation has no effect. | |
| 91 | ||
| 92 | #### `await swap(from, to)` | |
| 93 | ||
| 94 | Swap the paths `from` and `to`, making `from` assume the identity of `to` and `to` assume the identity of `from`. | |
| 95 | ||
| 96 | On Windows, the swap is performed by first moving `to` to a temporary path, then moving `from` to `to`, and finally moving the temporary path to `from`. | |
| 97 | ||
| 98 | On macOS and Linux, the swap is performed atomically. | |
| 99 | ||
| 100 | #### `const value = await getAttr(fd, name)` | |
| 101 | ||
| 102 | Get the value of the extended file attribute `name`. If the attribute doesn't exist, `null` is returned. | |
| 103 | ||
| 104 | #### `await setAttr(fd, name, value[, encoding])` | |
| 105 | ||
| 106 | Set the value of the extended file attribute `name` to `value`. | |
| 107 | ||
| 108 | #### `await removeAttr(fd, name)` | |
| 109 | ||
| 110 | Remove the extended file attribute `name`. | |
| 111 | ||
| 112 | #### `const names = await listAttrs(fd)` | |
| 113 | ||
| 114 | List all extended file attributes. | |
| 115 | ||
| 116 | ## License | |
| 117 | ||
| 118 | Apache-2.0 |
| … | … | @@ -0,0 +1,1415 @@ |
| 1 | #include <bare.h> | |
| 2 | #include <js.h> | |
| 3 | #include <stdlib.h> | |
| 4 | #include <string.h> | |
| 5 | #include <utf.h> | |
| 6 | #include <uv.h> | |
| 7 | ||
| 8 | #include "include/fs-ext.h" | |
| 9 | ||
| 10 | typedef utf8_t fs_ext_js_path_t[4096 + 1 /* NULL */]; | |
| 11 | ||
| 12 | typedef struct { | |
| 13 | fs_ext_lock_t req; | |
| 14 | ||
| 15 | js_env_t *env; | |
| 16 | js_ref_t *ctx; | |
| 17 | js_ref_t *cb; | |
| 18 | ||
| 19 | bool exiting; | |
| 20 | ||
| 21 | js_deferred_teardown_t *teardown; | |
| 22 | } fs_ext_js_lock_t; | |
| 23 | ||
| 24 | typedef struct { | |
| 25 | fs_ext_trim_t req; | |
| 26 | ||
| 27 | js_env_t *env; | |
| 28 | js_ref_t *ctx; | |
| 29 | js_ref_t *cb; | |
| 30 | ||
| 31 | bool exiting; | |
| 32 | ||
| 33 | js_deferred_teardown_t *teardown; | |
| 34 | } fs_ext_js_trim_t; | |
| 35 | ||
| 36 | typedef struct { | |
| 37 | fs_ext_sparse_t req; | |
| 38 | ||
| 39 | js_env_t *env; | |
| 40 | js_ref_t *ctx; | |
| 41 | js_ref_t *cb; | |
| 42 | ||
| 43 | bool exiting; | |
| 44 | ||
| 45 | js_deferred_teardown_t *teardown; | |
| 46 | } fs_ext_js_sparse_t; | |
| 47 | ||
| 48 | typedef struct { | |
| 49 | fs_ext_swap_t req; | |
| 50 | ||
| 51 | js_env_t *env; | |
| 52 | js_ref_t *ctx; | |
| 53 | js_ref_t *cb; | |
| 54 | ||
| 55 | bool exiting; | |
| 56 | ||
| 57 | js_deferred_teardown_t *teardown; | |
| 58 | } fs_ext_js_swap_t; | |
| 59 | ||
| 60 | typedef struct { | |
| 61 | fs_ext_get_attr_t req; | |
| 62 | ||
| 63 | js_env_t *env; | |
| 64 | js_ref_t *ctx; | |
| 65 | js_ref_t *cb; | |
| 66 | ||
| 67 | bool exiting; | |
| 68 | ||
| 69 | js_deferred_teardown_t *teardown; | |
| 70 | } fs_ext_js_get_attr_t; | |
| 71 | ||
| 72 | typedef struct { | |
| 73 | fs_ext_set_attr_t req; | |
| 74 | ||
| 75 | js_env_t *env; | |
| 76 | js_ref_t *ctx; | |
| 77 | js_ref_t *cb; | |
| 78 | ||
| 79 | bool exiting; | |
| 80 | ||
| 81 | js_deferred_teardown_t *teardown; | |
| 82 | } fs_ext_js_set_attr_t; | |
| 83 | ||
| 84 | typedef struct { | |
| 85 | fs_ext_remove_attr_t req; | |
| 86 | ||
| 87 | js_env_t *env; | |
| 88 | js_ref_t *ctx; | |
| 89 | js_ref_t *cb; | |
| 90 | ||
| 91 | bool exiting; | |
| 92 | ||
| 93 | js_deferred_teardown_t *teardown; | |
| 94 | } fs_ext_js_remove_attr_t; | |
| 95 | ||
| 96 | typedef struct { | |
| 97 | fs_ext_list_attrs_t req; | |
| 98 | ||
| 99 | js_env_t *env; | |
| 100 | js_ref_t *ctx; | |
| 101 | js_ref_t *cb; | |
| 102 | ||
| 103 | bool exiting; | |
| 104 | ||
| 105 | js_deferred_teardown_t *teardown; | |
| 106 | } fs_ext_js_list_attrs_t; | |
| 107 | ||
| 108 | static void | |
| 109 | fs_ext_js__on_teardown(js_deferred_teardown_t *teardown, void *data) { | |
| 110 | bool *exiting = data; | |
| 111 | ||
| 112 | *exiting = true; | |
| 113 | } | |
| 114 | ||
| 115 | static void | |
| 116 | fs_ext_js__on_lock(fs_ext_lock_t *req, int status) { | |
| 117 | int err; | |
| 118 | ||
| 119 | fs_ext_js_lock_t *r = (fs_ext_js_lock_t *) req; | |
| 120 | ||
| 121 | js_env_t *env = r->env; | |
| 122 | ||
| 123 | js_deferred_teardown_t *teardown = r->teardown; | |
| 124 | ||
| 125 | js_handle_scope_t *scope; | |
| 126 | err = js_open_handle_scope(env, &scope); | |
| 127 | assert(err == 0); | |
| 128 | ||
| 129 | js_value_t *argv[1]; | |
| 130 | ||
| 131 | js_value_t *ctx; | |
| 132 | err = js_get_reference_value(env, r->ctx, &ctx); | |
| 133 | assert(err == 0); | |
| 134 | ||
| 135 | js_value_t *callback; | |
| 136 | err = js_get_reference_value(env, r->cb, &callback); | |
| 137 | assert(err == 0); | |
| 138 | ||
| 139 | err = js_delete_reference(env, r->cb); | |
| 140 | assert(err == 0); | |
| 141 | ||
| 142 | err = js_delete_reference(env, r->ctx); | |
| 143 | assert(err == 0); | |
| 144 | ||
| 145 | if (req->result < 0) { | |
| 146 | js_value_t *code; | |
| 147 | err = js_create_string_utf8(env, (const utf8_t *) uv_err_name(req->result), -1, &code); | |
| 148 | assert(err == 0); | |
| 149 | ||
| 150 | js_value_t *message; | |
| 151 | err = js_create_string_utf8(env, (const utf8_t *) uv_strerror(req->result), -1, &message); | |
| 152 | assert(err == 0); | |
| 153 | ||
| 154 | err = js_create_error(env, code, message, &argv[0]); | |
| 155 | assert(err == 0); | |
| 156 | } else { | |
| 157 | err = js_get_null(env, &argv[0]); | |
| 158 | assert(err == 0); | |
| 159 | } | |
| 160 | ||
| 161 | if (!r->exiting) js_call_function_with_checkpoint(env, ctx, callback, 1, argv, NULL); | |
| 162 | ||
| 163 | err = js_close_handle_scope(env, scope); | |
| 164 | assert(err == 0); | |
| 165 | ||
| 166 | err = js_finish_deferred_teardown_callback(teardown); | |
| 167 | assert(err == 0); | |
| 168 | } | |
| 169 | ||
| 170 | static void | |
| 171 | fs_ext_js__on_trim(fs_ext_trim_t *req, int status) { | |
| 172 | int err; | |
| 173 | ||
| 174 | fs_ext_js_trim_t *r = (fs_ext_js_trim_t *) req; | |
| 175 | ||
| 176 | js_env_t *env = r->env; | |
| 177 | ||
| 178 | js_deferred_teardown_t *teardown = r->teardown; | |
| 179 | ||
| 180 | js_handle_scope_t *scope; | |
| 181 | err = js_open_handle_scope(env, &scope); | |
| 182 | assert(err == 0); | |
| 183 | ||
| 184 | js_value_t *argv[1]; | |
| 185 | ||
| 186 | js_value_t *ctx; | |
| 187 | err = js_get_reference_value(env, r->ctx, &ctx); | |
| 188 | assert(err == 0); | |
| 189 | ||
| 190 | js_value_t *callback; | |
| 191 | err = js_get_reference_value(env, r->cb, &callback); | |
| 192 | assert(err == 0); | |
| 193 | ||
| 194 | err = js_delete_reference(env, r->cb); | |
| 195 | assert(err == 0); | |
| 196 | ||
| 197 | err = js_delete_reference(env, r->ctx); | |
| 198 | assert(err == 0); | |
| 199 | ||
| 200 | if (req->result < 0) { | |
| 201 | js_value_t *code; | |
| 202 | err = js_create_string_utf8(env, (const utf8_t *) uv_err_name(req->result), -1, &code); | |
| 203 | assert(err == 0); | |
| 204 | ||
| 205 | js_value_t *message; | |
| 206 | err = js_create_string_utf8(env, (const utf8_t *) uv_strerror(req->result), -1, &message); | |
| 207 | assert(err == 0); | |
| 208 | ||
| 209 | err = js_create_error(env, code, message, &argv[0]); | |
| 210 | assert(err == 0); | |
| 211 | } else { | |
| 212 | err = js_get_null(env, &argv[0]); | |
| 213 | assert(err == 0); | |
| 214 | } | |
| 215 | ||
| 216 | if (!r->exiting) js_call_function_with_checkpoint(env, ctx, callback, 1, argv, NULL); | |
| 217 | ||
| 218 | err = js_close_handle_scope(env, scope); | |
| 219 | assert(err == 0); | |
| 220 | ||
| 221 | err = js_finish_deferred_teardown_callback(teardown); | |
| 222 | assert(err == 0); | |
| 223 | } | |
| 224 | ||
| 225 | static void | |
| 226 | fs_ext_js__on_sparse(fs_ext_sparse_t *req, int status) { | |
| 227 | int err; | |
| 228 | ||
| 229 | fs_ext_js_sparse_t *r = (fs_ext_js_sparse_t *) req; | |
| 230 | ||
| 231 | js_env_t *env = r->env; | |
| 232 | ||
| 233 | js_deferred_teardown_t *teardown = r->teardown; | |
| 234 | ||
| 235 | js_handle_scope_t *scope; | |
| 236 | err = js_open_handle_scope(env, &scope); | |
| 237 | assert(err == 0); | |
| 238 | ||
| 239 | js_value_t *argv[1]; | |
| 240 | ||
| 241 | js_value_t *ctx; | |
| 242 | err = js_get_reference_value(env, r->ctx, &ctx); | |
| 243 | assert(err == 0); | |
| 244 | ||
| 245 | js_value_t *callback; | |
| 246 | err = js_get_reference_value(env, r->cb, &callback); | |
| 247 | assert(err == 0); | |
| 248 | ||
| 249 | err = js_delete_reference(env, r->cb); | |
| 250 | assert(err == 0); | |
| 251 | ||
| 252 | err = js_delete_reference(env, r->ctx); | |
| 253 | assert(err == 0); | |
| 254 | ||
| 255 | if (req->result < 0) { | |
| 256 | js_value_t *code; | |
| 257 | err = js_create_string_utf8(env, (const utf8_t *) uv_err_name(req->result), -1, &code); | |
| 258 | assert(err == 0); | |
| 259 | ||
| 260 | js_value_t *message; | |
| 261 | err = js_create_string_utf8(env, (const utf8_t *) uv_strerror(req->result), -1, &message); | |
| 262 | assert(err == 0); | |
| 263 | ||
| 264 | err = js_create_error(env, code, message, &argv[0]); | |
| 265 | assert(err == 0); | |
| 266 | } else { | |
| 267 | err = js_get_null(env, &argv[0]); | |
| 268 | assert(err == 0); | |
| 269 | } | |
| 270 | ||
| 271 | if (!r->exiting) js_call_function_with_checkpoint(env, ctx, callback, 1, argv, NULL); | |
| 272 | ||
| 273 | err = js_close_handle_scope(env, scope); | |
| 274 | assert(err == 0); | |
| 275 | ||
| 276 | err = js_finish_deferred_teardown_callback(teardown); | |
| 277 | assert(err == 0); | |
| 278 | } | |
| 279 | ||
| 280 | static void | |
| 281 | fs_ext_js__on_swap(fs_ext_swap_t *req, int status) { | |
| 282 | int err; | |
| 283 | ||
| 284 | fs_ext_js_swap_t *r = (fs_ext_js_swap_t *) req; | |
| 285 | ||
| 286 | js_env_t *env = r->env; | |
| 287 | ||
| 288 | js_deferred_teardown_t *teardown = r->teardown; | |
| 289 | ||
| 290 | js_handle_scope_t *scope; | |
| 291 | err = js_open_handle_scope(env, &scope); | |
| 292 | assert(err == 0); | |
| 293 | ||
| 294 | js_value_t *argv[1]; | |
| 295 | ||
| 296 | js_value_t *ctx; | |
| 297 | err = js_get_reference_value(env, r->ctx, &ctx); | |
| 298 | assert(err == 0); | |
| 299 | ||
| 300 | js_value_t *callback; | |
| 301 | err = js_get_reference_value(env, r->cb, &callback); | |
| 302 | assert(err == 0); | |
| 303 | ||
| 304 | err = js_delete_reference(env, r->cb); | |
| 305 | assert(err == 0); | |
| 306 | ||
| 307 | err = js_delete_reference(env, r->ctx); | |
| 308 | assert(err == 0); | |
| 309 | ||
| 310 | if (req->result < 0) { | |
| 311 | js_value_t *code; | |
| 312 | err = js_create_string_utf8(env, (const utf8_t *) uv_err_name(req->result), -1, &code); | |
| 313 | assert(err == 0); | |
| 314 | ||
| 315 | js_value_t *message; | |
| 316 | err = js_create_string_utf8(env, (const utf8_t *) uv_strerror(req->result), -1, &message); | |
| 317 | assert(err == 0); | |
| 318 | ||
| 319 | err = js_create_error(env, code, message, &argv[0]); | |
| 320 | assert(err == 0); | |
| 321 | } else { | |
| 322 | err = js_get_null(env, &argv[0]); | |
| 323 | assert(err == 0); | |
| 324 | } | |
| 325 | ||
| 326 | if (!r->exiting) js_call_function_with_checkpoint(env, ctx, callback, 1, argv, NULL); | |
| 327 | ||
| 328 | err = js_close_handle_scope(env, scope); | |
| 329 | assert(err == 0); | |
| 330 | ||
| 331 | err = js_finish_deferred_teardown_callback(teardown); | |
| 332 | assert(err == 0); | |
| 333 | } | |
| 334 | ||
| 335 | static void | |
| 336 | fs_ext_js__on_get_attr(fs_ext_get_attr_t *req, int status, const uv_buf_t *value) { | |
| 337 | int err; | |
| 338 | ||
| 339 | fs_ext_js_get_attr_t *r = (fs_ext_js_get_attr_t *) req; | |
| 340 | ||
| 341 | js_env_t *env = r->env; | |
| 342 | ||
| 343 | js_deferred_teardown_t *teardown = r->teardown; | |
| 344 | ||
| 345 | js_handle_scope_t *scope; | |
| 346 | err = js_open_handle_scope(env, &scope); | |
| 347 | assert(err == 0); | |
| 348 | ||
| 349 | js_value_t *argv[2]; | |
| 350 | ||
| 351 | js_value_t *ctx; | |
| 352 | err = js_get_reference_value(env, r->ctx, &ctx); | |
| 353 | assert(err == 0); | |
| 354 | ||
| 355 | js_value_t *callback; | |
| 356 | err = js_get_reference_value(env, r->cb, &callback); | |
| 357 | assert(err == 0); | |
| 358 | ||
| 359 | err = js_delete_reference(env, r->cb); | |
| 360 | assert(err == 0); | |
| 361 | ||
| 362 | err = js_delete_reference(env, r->ctx); | |
| 363 | assert(err == 0); | |
| 364 | ||
| 365 | if (req->result < 0 && req->result != UV_ENODATA) { | |
| 366 | js_value_t *code; | |
| 367 | err = js_create_string_utf8(env, (const utf8_t *) uv_err_name(req->result), -1, &code); | |
| 368 | assert(err == 0); | |
| 369 | ||
| 370 | js_value_t *message; | |
| 371 | err = js_create_string_utf8(env, (const utf8_t *) uv_strerror(req->result), -1, &message); | |
| 372 | assert(err == 0); | |
| 373 | ||
| 374 | err = js_create_error(env, code, message, &argv[0]); | |
| 375 | assert(err == 0); | |
| 376 | ||
| 377 | err = js_get_null(env, &argv[1]); | |
| 378 | assert(err == 0); | |
| 379 | } else { | |
| 380 | err = js_get_null(env, &argv[0]); | |
| 381 | assert(err == 0); | |
| 382 | ||
| 383 | if (req->result == UV_ENODATA) { | |
| 384 | err = js_get_null(env, &argv[1]); | |
| 385 | assert(err == 0); | |
| 386 | } else { | |
| 387 | void *data; | |
| 388 | err = js_create_arraybuffer(env, value->len, &data, &argv[1]); | |
| 389 | assert(err == 0); | |
| 390 | ||
| 391 | memcpy(data, value->base, value->len); | |
| 392 | } | |
| 393 | } | |
| 394 | ||
| 395 | if (!r->exiting) js_call_function_with_checkpoint(env, ctx, callback, 2, argv, NULL); | |
| 396 | ||
| 397 | err = js_close_handle_scope(env, scope); | |
| 398 | assert(err == 0); | |
| 399 | ||
| 400 | err = js_finish_deferred_teardown_callback(teardown); | |
| 401 | assert(err == 0); | |
| 402 | } | |
| 403 | ||
| 404 | static void | |
| 405 | fs_ext_js__on_set_attr(fs_ext_set_attr_t *req, int status) { | |
| 406 | int err; | |
| 407 | ||
| 408 | fs_ext_js_set_attr_t *r = (fs_ext_js_set_attr_t *) req; | |
| 409 | ||
| 410 | js_env_t *env = r->env; | |
| 411 | ||
| 412 | js_deferred_teardown_t *teardown = r->teardown; | |
| 413 | ||
| 414 | js_handle_scope_t *scope; | |
| 415 | err = js_open_handle_scope(env, &scope); | |
| 416 | assert(err == 0); | |
| 417 | ||
| 418 | js_value_t *argv[1]; | |
| 419 | ||
| 420 | js_value_t *ctx; | |
| 421 | err = js_get_reference_value(env, r->ctx, &ctx); | |
| 422 | assert(err == 0); | |
| 423 | ||
| 424 | js_value_t *callback; | |
| 425 | err = js_get_reference_value(env, r->cb, &callback); | |
| 426 | assert(err == 0); | |
| 427 | ||
| 428 | err = js_delete_reference(env, r->cb); | |
| 429 | assert(err == 0); | |
| 430 | ||
| 431 | err = js_delete_reference(env, r->ctx); | |
| 432 | assert(err == 0); | |
| 433 | ||
| 434 | if (req->result < 0) { | |
| 435 | js_value_t *code; | |
| 436 | err = js_create_string_utf8(env, (const utf8_t *) uv_err_name(req->result), -1, &code); | |
| 437 | assert(err == 0); | |
| 438 | ||
| 439 | js_value_t *message; | |
| 440 | err = js_create_string_utf8(env, (const utf8_t *) uv_strerror(req->result), -1, &message); | |
| 441 | assert(err == 0); | |
| 442 | ||
| 443 | err = js_create_error(env, code, message, &argv[0]); | |
| 444 | assert(err == 0); | |
| 445 | } else { | |
| 446 | err = js_get_null(env, &argv[0]); | |
| 447 | assert(err == 0); | |
| 448 | } | |
| 449 | ||
| 450 | if (!r->exiting) js_call_function_with_checkpoint(env, ctx, callback, 1, argv, NULL); | |
| 451 | ||
| 452 | err = js_close_handle_scope(env, scope); | |
| 453 | assert(err == 0); | |
| 454 | ||
| 455 | err = js_finish_deferred_teardown_callback(teardown); | |
| 456 | assert(err == 0); | |
| 457 | } | |
| 458 | ||
| 459 | static void | |
| 460 | fs_ext_js__on_remove_attr(fs_ext_remove_attr_t *req, int status) { | |
| 461 | int err; | |
| 462 | ||
| 463 | fs_ext_js_remove_attr_t *r = (fs_ext_js_remove_attr_t *) req; | |
| 464 | ||
| 465 | js_env_t *env = r->env; | |
| 466 | ||
| 467 | js_deferred_teardown_t *teardown = r->teardown; | |
| 468 | ||
| 469 | js_handle_scope_t *scope; | |
| 470 | err = js_open_handle_scope(env, &scope); | |
| 471 | assert(err == 0); | |
| 472 | ||
| 473 | js_value_t *argv[1]; | |
| 474 | ||
| 475 | js_value_t *ctx; | |
| 476 | err = js_get_reference_value(env, r->ctx, &ctx); | |
| 477 | assert(err == 0); | |
| 478 | ||
| 479 | js_value_t *callback; | |
| 480 | err = js_get_reference_value(env, r->cb, &callback); | |
| 481 | assert(err == 0); | |
| 482 | ||
| 483 | err = js_delete_reference(env, r->cb); | |
| 484 | assert(err == 0); | |
| 485 | ||
| 486 | err = js_delete_reference(env, r->ctx); | |
| 487 | assert(err == 0); | |
| 488 | ||
| 489 | if (req->result < 0 && req->result != UV_ENODATA) { | |
| 490 | js_value_t *code; | |
| 491 | err = js_create_string_utf8(env, (const utf8_t *) uv_err_name(req->result), -1, &code); | |
| 492 | assert(err == 0); | |
| 493 | ||
| 494 | js_value_t *message; | |
| 495 | err = js_create_string_utf8(env, (const utf8_t *) uv_strerror(req->result), -1, &message); | |
| 496 | assert(err == 0); | |
| 497 | ||
| 498 | err = js_create_error(env, code, message, &argv[0]); | |
| 499 | assert(err == 0); | |
| 500 | } else { | |
| 501 | err = js_get_null(env, &argv[0]); | |
| 502 | assert(err == 0); | |
| 503 | } | |
| 504 | ||
| 505 | if (!r->exiting) js_call_function_with_checkpoint(env, ctx, callback, 1, argv, NULL); | |
| 506 | ||
| 507 | err = js_close_handle_scope(env, scope); | |
| 508 | assert(err == 0); | |
| 509 | ||
| 510 | err = js_finish_deferred_teardown_callback(teardown); | |
| 511 | assert(err == 0); | |
| 512 | } | |
| 513 | ||
| 514 | static void | |
| 515 | fs_ext_js__on_list_attrs(fs_ext_list_attrs_t *req, int status, const char *attrs[], ssize_t len) { | |
| 516 | int err; | |
| 517 | ||
| 518 | fs_ext_js_list_attrs_t *r = (fs_ext_js_list_attrs_t *) req; | |
| 519 | ||
| 520 | js_env_t *env = r->env; | |
| 521 | ||
| 522 | js_deferred_teardown_t *teardown = r->teardown; | |
| 523 | ||
| 524 | js_handle_scope_t *scope; | |
| 525 | err = js_open_handle_scope(env, &scope); | |
| 526 | assert(err == 0); | |
| 527 | ||
| 528 | js_value_t *argv[2]; | |
| 529 | ||
| 530 | js_value_t *ctx; | |
| 531 | err = js_get_reference_value(env, r->ctx, &ctx); | |
| 532 | assert(err == 0); | |
| 533 | ||
| 534 | js_value_t *callback; | |
| 535 | err = js_get_reference_value(env, r->cb, &callback); | |
| 536 | assert(err == 0); | |
| 537 | ||
| 538 | err = js_delete_reference(env, r->cb); | |
| 539 | assert(err == 0); | |
| 540 | ||
| 541 | err = js_delete_reference(env, r->ctx); | |
| 542 | assert(err == 0); | |
| 543 | ||
| 544 | if (req->result < 0) { | |
| 545 | js_value_t *code; | |
| 546 | err = js_create_string_utf8(env, (const utf8_t *) uv_err_name(req->result), -1, &code); | |
| 547 | assert(err == 0); | |
| 548 | ||
| 549 | js_value_t *message; | |
| 550 | err = js_create_string_utf8(env, (const utf8_t *) uv_strerror(req->result), -1, &message); | |
| 551 | assert(err == 0); | |
| 552 | ||
| 553 | err = js_create_error(env, code, message, &argv[0]); | |
| 554 | assert(err == 0); | |
| 555 | ||
| 556 | err = js_get_null(env, &argv[1]); | |
| 557 | assert(err == 0); | |
| 558 | } else { | |
| 559 | err = js_get_null(env, &argv[0]); | |
| 560 | assert(err == 0); | |
| 561 | ||
| 562 | err = js_create_array_with_length(env, len, &argv[1]); | |
| 563 | assert(err == 0); | |
| 564 | ||
| 565 | for (size_t i = 0; i < len; i++) { | |
| 566 | js_value_t *value; | |
| 567 | err = js_create_string_utf8(env, (const utf8_t *) attrs[i], -1, &value); | |
| 568 | assert(err == 0); | |
| 569 | ||
| 570 | err = js_set_element(env, argv[1], i, value); | |
| 571 | assert(err == 0); | |
| 572 | } | |
| 573 | } | |
| 574 | ||
| 575 | if (!r->exiting) js_call_function_with_checkpoint(env, ctx, callback, 2, argv, NULL); | |
| 576 | ||
| 577 | err = js_close_handle_scope(env, scope); | |
| 578 | assert(err == 0); | |
| 579 | ||
| 580 | err = js_finish_deferred_teardown_callback(teardown); | |
| 581 | assert(err == 0); | |
| 582 | } | |
| 583 | ||
| 584 | static js_value_t * | |
| 585 | fs_ext_js_try_lock(js_env_t *env, js_callback_info_t *info) { | |
| 586 | int err; | |
| 587 | ||
| 588 | size_t argc = 4; | |
| 589 | js_value_t *argv[4]; | |
| 590 | ||
| 591 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 592 | assert(err == 0); | |
| 593 | ||
| 594 | assert(argc == 4); | |
| 595 | ||
| 596 | int64_t fd; | |
| 597 | err = js_get_value_int64(env, argv[0], &fd); | |
| 598 | assert(err == 0); | |
| 599 | ||
| 600 | int64_t offset; | |
| 601 | err = js_get_value_int64(env, argv[1], &offset); | |
| 602 | assert(err == 0); | |
| 603 | ||
| 604 | int64_t len; | |
| 605 | err = js_get_value_int64(env, argv[2], &len); | |
| 606 | assert(err == 0); | |
| 607 | ||
| 608 | bool exclusive; | |
| 609 | err = js_get_value_bool(env, argv[3], &exclusive); | |
| 610 | assert(err == 0); | |
| 611 | ||
| 612 | err = fs_ext_try_lock( | |
| 613 | uv_get_osfhandle(fd), | |
| 614 | offset, | |
| 615 | len, | |
| 616 | exclusive ? FS_EXT_WRLOCK : FS_EXT_RDLOCK | |
| 617 | ); | |
| 618 | ||
| 619 | if (err < 0) { | |
| 620 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 621 | assert(err == 0); | |
| 622 | } | |
| 623 | ||
| 624 | return NULL; | |
| 625 | } | |
| 626 | ||
| 627 | static js_value_t * | |
| 628 | fs_ext_js_wait_for_lock(js_env_t *env, js_callback_info_t *info) { | |
| 629 | int err; | |
| 630 | ||
| 631 | size_t argc = 6; | |
| 632 | js_value_t *argv[6]; | |
| 633 | ||
| 634 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 635 | assert(err == 0); | |
| 636 | ||
| 637 | assert(argc == 6); | |
| 638 | ||
| 639 | int64_t fd; | |
| 640 | err = js_get_value_int64(env, argv[0], &fd); | |
| 641 | assert(err == 0); | |
| 642 | ||
| 643 | int64_t offset; | |
| 644 | err = js_get_value_int64(env, argv[1], &offset); | |
| 645 | assert(err == 0); | |
| 646 | ||
| 647 | int64_t len; | |
| 648 | err = js_get_value_int64(env, argv[2], &len); | |
| 649 | assert(err == 0); | |
| 650 | ||
| 651 | bool exclusive; | |
| 652 | err = js_get_value_bool(env, argv[3], &exclusive); | |
| 653 | assert(err == 0); | |
| 654 | ||
| 655 | js_value_t *handle; | |
| 656 | ||
| 657 | fs_ext_js_lock_t *req; | |
| 658 | err = js_create_arraybuffer(env, sizeof(fs_ext_js_lock_t), (void **) &req, &handle); | |
| 659 | assert(err == 0); | |
| 660 | ||
| 661 | uv_loop_t *loop; | |
| 662 | err = js_get_env_loop(env, &loop); | |
| 663 | assert(err == 0); | |
| 664 | ||
| 665 | err = fs_ext_wait_for_lock( | |
| 666 | loop, | |
| 667 | (fs_ext_lock_t *) req, | |
| 668 | uv_get_osfhandle(fd), | |
| 669 | offset, | |
| 670 | len, | |
| 671 | exclusive ? FS_EXT_WRLOCK : FS_EXT_RDLOCK, | |
| 672 | fs_ext_js__on_lock | |
| 673 | ); | |
| 674 | ||
| 675 | if (err < 0) { | |
| 676 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 677 | assert(err == 0); | |
| 678 | ||
| 679 | return NULL; | |
| 680 | } | |
| 681 | ||
| 682 | req->env = env; | |
| 683 | req->exiting = false; | |
| 684 | ||
| 685 | err = js_create_reference(env, argv[4], 1, &req->ctx); | |
| 686 | assert(err == 0); | |
| 687 | ||
| 688 | err = js_create_reference(env, argv[5], 1, &req->cb); | |
| 689 | assert(err == 0); | |
| 690 | ||
| 691 | err = js_add_deferred_teardown_callback(env, fs_ext_js__on_teardown, (void *) &req->exiting, &req->teardown); | |
| 692 | assert(err == 0); | |
| 693 | ||
| 694 | return handle; | |
| 695 | } | |
| 696 | ||
| 697 | static js_value_t * | |
| 698 | fs_ext_js_try_downgrade_lock(js_env_t *env, js_callback_info_t *info) { | |
| 699 | int err; | |
| 700 | ||
| 701 | size_t argc = 3; | |
| 702 | js_value_t *argv[3]; | |
| 703 | ||
| 704 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 705 | assert(err == 0); | |
| 706 | ||
| 707 | assert(argc == 3); | |
| 708 | ||
| 709 | int64_t fd; | |
| 710 | err = js_get_value_int64(env, argv[0], &fd); | |
| 711 | assert(err == 0); | |
| 712 | ||
| 713 | int64_t offset; | |
| 714 | err = js_get_value_int64(env, argv[1], &offset); | |
| 715 | assert(err == 0); | |
| 716 | ||
| 717 | int64_t len; | |
| 718 | err = js_get_value_int64(env, argv[2], &len); | |
| 719 | assert(err == 0); | |
| 720 | ||
| 721 | err = fs_ext_try_downgrade_lock(uv_get_osfhandle(fd), offset, len); | |
| 722 | ||
| 723 | if (err < 0) { | |
| 724 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 725 | assert(err == 0); | |
| 726 | } | |
| 727 | ||
| 728 | return NULL; | |
| 729 | } | |
| 730 | ||
| 731 | static js_value_t * | |
| 732 | fs_ext_js_wait_for_downgrade_lock(js_env_t *env, js_callback_info_t *info) { | |
| 733 | int err; | |
| 734 | ||
| 735 | size_t argc = 5; | |
| 736 | js_value_t *argv[5]; | |
| 737 | ||
| 738 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 739 | assert(err == 0); | |
| 740 | ||
| 741 | assert(argc == 5); | |
| 742 | ||
| 743 | int64_t fd; | |
| 744 | err = js_get_value_int64(env, argv[0], &fd); | |
| 745 | assert(err == 0); | |
| 746 | ||
| 747 | int64_t offset; | |
| 748 | err = js_get_value_int64(env, argv[1], &offset); | |
| 749 | assert(err == 0); | |
| 750 | ||
| 751 | int64_t len; | |
| 752 | err = js_get_value_int64(env, argv[2], &len); | |
| 753 | assert(err == 0); | |
| 754 | ||
| 755 | js_value_t *handle; | |
| 756 | ||
| 757 | fs_ext_js_lock_t *req; | |
| 758 | err = js_create_arraybuffer(env, sizeof(fs_ext_js_lock_t), (void **) &req, &handle); | |
| 759 | assert(err == 0); | |
| 760 | ||
| 761 | uv_loop_t *loop; | |
| 762 | err = js_get_env_loop(env, &loop); | |
| 763 | assert(err == 0); | |
| 764 | ||
| 765 | err = fs_ext_wait_for_downgrade_lock( | |
| 766 | loop, | |
| 767 | (fs_ext_lock_t *) req, | |
| 768 | uv_get_osfhandle(fd), | |
| 769 | offset, | |
| 770 | len, | |
| 771 | fs_ext_js__on_lock | |
| 772 | ); | |
| 773 | ||
| 774 | if (err < 0) { | |
| 775 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 776 | assert(err == 0); | |
| 777 | ||
| 778 | return NULL; | |
| 779 | } | |
| 780 | ||
| 781 | req->env = env; | |
| 782 | req->exiting = false; | |
| 783 | ||
| 784 | err = js_create_reference(env, argv[3], 1, &req->ctx); | |
| 785 | assert(err == 0); | |
| 786 | ||
| 787 | err = js_create_reference(env, argv[4], 1, &req->cb); | |
| 788 | assert(err == 0); | |
| 789 | ||
| 790 | err = js_add_deferred_teardown_callback(env, fs_ext_js__on_teardown, (void *) &req->exiting, &req->teardown); | |
| 791 | assert(err == 0); | |
| 792 | ||
| 793 | return handle; | |
| 794 | } | |
| 795 | ||
| 796 | static js_value_t * | |
| 797 | fs_ext_js_try_upgrade_lock(js_env_t *env, js_callback_info_t *info) { | |
| 798 | int err; | |
| 799 | ||
| 800 | size_t argc = 3; | |
| 801 | js_value_t *argv[3]; | |
| 802 | ||
| 803 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 804 | assert(err == 0); | |
| 805 | ||
| 806 | assert(argc == 3); | |
| 807 | ||
| 808 | int64_t fd; | |
| 809 | err = js_get_value_int64(env, argv[0], &fd); | |
| 810 | assert(err == 0); | |
| 811 | ||
| 812 | int64_t offset; | |
| 813 | err = js_get_value_int64(env, argv[1], &offset); | |
| 814 | assert(err == 0); | |
| 815 | ||
| 816 | int64_t len; | |
| 817 | err = js_get_value_int64(env, argv[2], &len); | |
| 818 | assert(err == 0); | |
| 819 | ||
| 820 | err = fs_ext_try_upgrade_lock(uv_get_osfhandle(fd), offset, len); | |
| 821 | ||
| 822 | if (err < 0) { | |
| 823 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 824 | assert(err == 0); | |
| 825 | } | |
| 826 | ||
| 827 | return NULL; | |
| 828 | } | |
| 829 | ||
| 830 | static js_value_t * | |
| 831 | fs_ext_js_wait_for_upgrade_lock(js_env_t *env, js_callback_info_t *info) { | |
| 832 | int err; | |
| 833 | ||
| 834 | size_t argc = 5; | |
| 835 | js_value_t *argv[5]; | |
| 836 | ||
| 837 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 838 | assert(err == 0); | |
| 839 | ||
| 840 | assert(argc == 5); | |
| 841 | ||
| 842 | int64_t fd; | |
| 843 | err = js_get_value_int64(env, argv[0], &fd); | |
| 844 | assert(err == 0); | |
| 845 | ||
| 846 | int64_t offset; | |
| 847 | err = js_get_value_int64(env, argv[1], &offset); | |
| 848 | assert(err == 0); | |
| 849 | ||
| 850 | int64_t len; | |
| 851 | err = js_get_value_int64(env, argv[2], &len); | |
| 852 | assert(err == 0); | |
| 853 | ||
| 854 | js_value_t *handle; | |
| 855 | ||
| 856 | fs_ext_js_lock_t *req; | |
| 857 | err = js_create_arraybuffer(env, sizeof(fs_ext_js_lock_t), (void **) &req, &handle); | |
| 858 | assert(err == 0); | |
| 859 | ||
| 860 | uv_loop_t *loop; | |
| 861 | err = js_get_env_loop(env, &loop); | |
| 862 | assert(err == 0); | |
| 863 | ||
| 864 | err = fs_ext_wait_for_upgrade_lock( | |
| 865 | loop, | |
| 866 | (fs_ext_lock_t *) req, | |
| 867 | uv_get_osfhandle(fd), | |
| 868 | offset, | |
| 869 | len, | |
| 870 | fs_ext_js__on_lock | |
| 871 | ); | |
| 872 | ||
| 873 | if (err < 0) { | |
| 874 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 875 | assert(err == 0); | |
| 876 | ||
| 877 | return NULL; | |
| 878 | } | |
| 879 | ||
| 880 | req->env = env; | |
| 881 | req->exiting = false; | |
| 882 | ||
| 883 | err = js_create_reference(env, argv[3], 1, &req->ctx); | |
| 884 | assert(err == 0); | |
| 885 | ||
| 886 | err = js_create_reference(env, argv[4], 1, &req->cb); | |
| 887 | assert(err == 0); | |
| 888 | ||
| 889 | err = js_add_deferred_teardown_callback(env, fs_ext_js__on_teardown, (void *) &req->exiting, &req->teardown); | |
| 890 | assert(err == 0); | |
| 891 | ||
| 892 | return handle; | |
| 893 | } | |
| 894 | ||
| 895 | static js_value_t * | |
| 896 | fs_ext_js_unlock(js_env_t *env, js_callback_info_t *info) { | |
| 897 | int err; | |
| 898 | ||
| 899 | size_t argc = 3; | |
| 900 | js_value_t *argv[3]; | |
| 901 | ||
| 902 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 903 | assert(err == 0); | |
| 904 | ||
| 905 | assert(argc == 3); | |
| 906 | ||
| 907 | int64_t fd; | |
| 908 | err = js_get_value_int64(env, argv[0], &fd); | |
| 909 | assert(err == 0); | |
| 910 | ||
| 911 | int64_t offset; | |
| 912 | err = js_get_value_int64(env, argv[1], &offset); | |
| 913 | assert(err == 0); | |
| 914 | ||
| 915 | int64_t len; | |
| 916 | err = js_get_value_int64(env, argv[2], &len); | |
| 917 | assert(err == 0); | |
| 918 | ||
| 919 | err = fs_ext_unlock(uv_get_osfhandle(fd), offset, len); | |
| 920 | ||
| 921 | if (err < 0) { | |
| 922 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 923 | assert(err == 0); | |
| 924 | } | |
| 925 | ||
| 926 | return NULL; | |
| 927 | } | |
| 928 | ||
| 929 | static js_value_t * | |
| 930 | fs_ext_js_trim(js_env_t *env, js_callback_info_t *info) { | |
| 931 | int err; | |
| 932 | ||
| 933 | size_t argc = 5; | |
| 934 | js_value_t *argv[5]; | |
| 935 | ||
| 936 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 937 | assert(err == 0); | |
| 938 | ||
| 939 | assert(argc == 5); | |
| 940 | ||
| 941 | int64_t fd; | |
| 942 | err = js_get_value_int64(env, argv[0], &fd); | |
| 943 | assert(err == 0); | |
| 944 | ||
| 945 | int64_t offset; | |
| 946 | err = js_get_value_int64(env, argv[1], &offset); | |
| 947 | assert(err == 0); | |
| 948 | ||
| 949 | int64_t len; | |
| 950 | err = js_get_value_int64(env, argv[2], &len); | |
| 951 | assert(err == 0); | |
| 952 | ||
| 953 | js_value_t *handle; | |
| 954 | ||
| 955 | fs_ext_js_trim_t *req; | |
| 956 | err = js_create_arraybuffer(env, sizeof(fs_ext_js_trim_t), (void **) &req, &handle); | |
| 957 | assert(err == 0); | |
| 958 | ||
| 959 | uv_loop_t *loop; | |
| 960 | err = js_get_env_loop(env, &loop); | |
| 961 | assert(err == 0); | |
| 962 | ||
| 963 | err = fs_ext_trim( | |
| 964 | loop, | |
| 965 | (fs_ext_trim_t *) req, | |
| 966 | uv_get_osfhandle(fd), | |
| 967 | offset, | |
| 968 | len, | |
| 969 | fs_ext_js__on_trim | |
| 970 | ); | |
| 971 | ||
| 972 | if (err < 0) { | |
| 973 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 974 | assert(err == 0); | |
| 975 | ||
| 976 | return NULL; | |
| 977 | } | |
| 978 | ||
| 979 | req->env = env; | |
| 980 | req->exiting = false; | |
| 981 | ||
| 982 | err = js_create_reference(env, argv[3], 1, &req->ctx); | |
| 983 | assert(err == 0); | |
| 984 | ||
| 985 | err = js_create_reference(env, argv[4], 1, &req->cb); | |
| 986 | assert(err == 0); | |
| 987 | ||
| 988 | err = js_add_deferred_teardown_callback(env, fs_ext_js__on_teardown, (void *) &req->exiting, &req->teardown); | |
| 989 | assert(err == 0); | |
| 990 | ||
| 991 | return handle; | |
| 992 | } | |
| 993 | ||
| 994 | static js_value_t * | |
| 995 | fs_ext_js_sparse(js_env_t *env, js_callback_info_t *info) { | |
| 996 | int err; | |
| 997 | ||
| 998 | size_t argc = 3; | |
| 999 | js_value_t *argv[3]; | |
| 1000 | ||
| 1001 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1002 | assert(err == 0); | |
| 1003 | ||
| 1004 | assert(argc == 3); | |
| 1005 | ||
| 1006 | int64_t fd; | |
| 1007 | err = js_get_value_int64(env, argv[0], &fd); | |
| 1008 | assert(err == 0); | |
| 1009 | ||
| 1010 | js_value_t *handle; | |
| 1011 | ||
| 1012 | fs_ext_js_sparse_t *req; | |
| 1013 | err = js_create_arraybuffer(env, sizeof(fs_ext_js_sparse_t), (void **) &req, &handle); | |
| 1014 | assert(err == 0); | |
| 1015 | ||
| 1016 | uv_loop_t *loop; | |
| 1017 | err = js_get_env_loop(env, &loop); | |
| 1018 | assert(err == 0); | |
| 1019 | ||
| 1020 | err = fs_ext_sparse( | |
| 1021 | loop, | |
| 1022 | (fs_ext_sparse_t *) req, | |
| 1023 | uv_get_osfhandle(fd), | |
| 1024 | fs_ext_js__on_sparse | |
| 1025 | ); | |
| 1026 | ||
| 1027 | if (err < 0) { | |
| 1028 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1029 | assert(err == 0); | |
| 1030 | ||
| 1031 | return NULL; | |
| 1032 | } | |
| 1033 | ||
| 1034 | req->env = env; | |
| 1035 | req->exiting = false; | |
| 1036 | ||
| 1037 | err = js_create_reference(env, argv[1], 1, &req->ctx); | |
| 1038 | assert(err == 0); | |
| 1039 | ||
| 1040 | err = js_create_reference(env, argv[2], 1, &req->cb); | |
| 1041 | assert(err == 0); | |
| 1042 | ||
| 1043 | err = js_add_deferred_teardown_callback(env, fs_ext_js__on_teardown, (void *) &req->exiting, &req->teardown); | |
| 1044 | assert(err == 0); | |
| 1045 | ||
| 1046 | return handle; | |
| 1047 | } | |
| 1048 | ||
| 1049 | static js_value_t * | |
| 1050 | fs_ext_js_swap(js_env_t *env, js_callback_info_t *info) { | |
| 1051 | int err; | |
| 1052 | ||
| 1053 | size_t argc = 4; | |
| 1054 | js_value_t *argv[4]; | |
| 1055 | ||
| 1056 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1057 | assert(err == 0); | |
| 1058 | ||
| 1059 | assert(argc == 4); | |
| 1060 | ||
| 1061 | fs_ext_js_path_t from; | |
| 1062 | err = js_get_value_string_utf8(env, argv[0], from, sizeof(from), NULL); | |
| 1063 | assert(err == 0); | |
| 1064 | ||
| 1065 | fs_ext_js_path_t to; | |
| 1066 | err = js_get_value_string_utf8(env, argv[1], to, sizeof(to), NULL); | |
| 1067 | assert(err == 0); | |
| 1068 | ||
| 1069 | js_value_t *handle; | |
| 1070 | ||
| 1071 | fs_ext_js_swap_t *req; | |
| 1072 | err = js_create_arraybuffer(env, sizeof(fs_ext_js_swap_t), (void **) &req, &handle); | |
| 1073 | assert(err == 0); | |
| 1074 | ||
| 1075 | uv_loop_t *loop; | |
| 1076 | err = js_get_env_loop(env, &loop); | |
| 1077 | assert(err == 0); | |
| 1078 | ||
| 1079 | err = fs_ext_swap( | |
| 1080 | loop, | |
| 1081 | (fs_ext_swap_t *) req, | |
| 1082 | (const char *) from, | |
| 1083 | (const char *) to, | |
| 1084 | fs_ext_js__on_swap | |
| 1085 | ); | |
| 1086 | ||
| 1087 | if (err < 0) { | |
| 1088 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1089 | assert(err == 0); | |
| 1090 | ||
| 1091 | return NULL; | |
| 1092 | } | |
| 1093 | ||
| 1094 | req->env = env; | |
| 1095 | req->exiting = false; | |
| 1096 | ||
| 1097 | err = js_create_reference(env, argv[2], 1, &req->ctx); | |
| 1098 | assert(err == 0); | |
| 1099 | ||
| 1100 | err = js_create_reference(env, argv[3], 1, &req->cb); | |
| 1101 | assert(err == 0); | |
| 1102 | ||
| 1103 | err = js_add_deferred_teardown_callback(env, fs_ext_js__on_teardown, (void *) &req->exiting, &req->teardown); | |
| 1104 | assert(err == 0); | |
| 1105 | ||
| 1106 | return handle; | |
| 1107 | } | |
| 1108 | ||
| 1109 | static js_value_t * | |
| 1110 | fs_ext_js_get_attr(js_env_t *env, js_callback_info_t *info) { | |
| 1111 | int err; | |
| 1112 | ||
| 1113 | size_t argc = 4; | |
| 1114 | js_value_t *argv[4]; | |
| 1115 | ||
| 1116 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1117 | assert(err == 0); | |
| 1118 | ||
| 1119 | assert(argc == 4); | |
| 1120 | ||
| 1121 | int64_t fd; | |
| 1122 | err = js_get_value_int64(env, argv[0], &fd); | |
| 1123 | assert(err == 0); | |
| 1124 | ||
| 1125 | size_t name_len; | |
| 1126 | err = js_get_value_string_utf8(env, argv[1], NULL, 0, &name_len); | |
| 1127 | assert(err == 0); | |
| 1128 | ||
| 1129 | name_len += 1 /* NULL */; | |
| 1130 | ||
| 1131 | utf8_t *name = malloc(name_len); | |
| 1132 | err = js_get_value_string_utf8(env, argv[1], name, name_len, NULL); | |
| 1133 | assert(err == 0); | |
| 1134 | ||
| 1135 | js_value_t *handle; | |
| 1136 | ||
| 1137 | fs_ext_js_get_attr_t *req; | |
| 1138 | err = js_create_arraybuffer(env, sizeof(fs_ext_js_get_attr_t), (void **) &req, &handle); | |
| 1139 | assert(err == 0); | |
| 1140 | ||
| 1141 | uv_loop_t *loop; | |
| 1142 | err = js_get_env_loop(env, &loop); | |
| 1143 | assert(err == 0); | |
| 1144 | ||
| 1145 | err = fs_ext_get_attr( | |
| 1146 | loop, | |
| 1147 | (fs_ext_get_attr_t *) req, | |
| 1148 | uv_get_osfhandle(fd), | |
| 1149 | (const char *) name, | |
| 1150 | fs_ext_js__on_get_attr | |
| 1151 | ); | |
| 1152 | ||
| 1153 | free(name); | |
| 1154 | ||
| 1155 | if (err < 0) { | |
| 1156 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1157 | assert(err == 0); | |
| 1158 | ||
| 1159 | return NULL; | |
| 1160 | } | |
| 1161 | ||
| 1162 | req->env = env; | |
| 1163 | req->exiting = false; | |
| 1164 | ||
| 1165 | err = js_create_reference(env, argv[2], 1, &req->ctx); | |
| 1166 | assert(err == 0); | |
| 1167 | ||
| 1168 | err = js_create_reference(env, argv[3], 1, &req->cb); | |
| 1169 | assert(err == 0); | |
| 1170 | ||
| 1171 | err = js_add_deferred_teardown_callback(env, fs_ext_js__on_teardown, (void *) &req->exiting, &req->teardown); | |
| 1172 | assert(err == 0); | |
| 1173 | ||
| 1174 | return handle; | |
| 1175 | } | |
| 1176 | ||
| 1177 | static js_value_t * | |
| 1178 | fs_ext_js_set_attr(js_env_t *env, js_callback_info_t *info) { | |
| 1179 | int err; | |
| 1180 | ||
| 1181 | size_t argc = 7; | |
| 1182 | js_value_t *argv[7]; | |
| 1183 | ||
| 1184 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1185 | assert(err == 0); | |
| 1186 | ||
| 1187 | assert(argc == 7); | |
| 1188 | ||
| 1189 | int64_t fd; | |
| 1190 | err = js_get_value_int64(env, argv[0], &fd); | |
| 1191 | assert(err == 0); | |
| 1192 | ||
| 1193 | size_t name_len; | |
| 1194 | err = js_get_value_string_utf8(env, argv[1], NULL, 0, &name_len); | |
| 1195 | assert(err == 0); | |
| 1196 | ||
| 1197 | name_len += 1 /* NULL */; | |
| 1198 | ||
| 1199 | utf8_t *name = malloc(name_len); | |
| 1200 | err = js_get_value_string_utf8(env, argv[1], name, name_len, NULL); | |
| 1201 | assert(err == 0); | |
| 1202 | ||
| 1203 | char *data; | |
| 1204 | err = js_get_arraybuffer_info(env, argv[2], (void **) &data, NULL); | |
| 1205 | assert(err == 0); | |
| 1206 | ||
| 1207 | int64_t data_offset; | |
| 1208 | err = js_get_value_int64(env, argv[3], &data_offset); | |
| 1209 | assert(err == 0); | |
| 1210 | ||
| 1211 | int64_t data_len; | |
| 1212 | err = js_get_value_int64(env, argv[4], &data_len); | |
| 1213 | assert(err == 0); | |
| 1214 | ||
| 1215 | js_value_t *handle; | |
| 1216 | ||
| 1217 | fs_ext_js_set_attr_t *req; | |
| 1218 | err = js_create_arraybuffer(env, sizeof(fs_ext_js_set_attr_t), (void **) &req, &handle); | |
| 1219 | assert(err == 0); | |
| 1220 | ||
| 1221 | uv_loop_t *loop; | |
| 1222 | err = js_get_env_loop(env, &loop); | |
| 1223 | assert(err == 0); | |
| 1224 | ||
| 1225 | uv_buf_t buf = uv_buf_init(&data[data_offset], data_len); | |
| 1226 | ||
| 1227 | err = fs_ext_set_attr( | |
| 1228 | loop, | |
| 1229 | (fs_ext_set_attr_t *) req, | |
| 1230 | uv_get_osfhandle(fd), | |
| 1231 | (const char *) name, | |
| 1232 | &buf, | |
| 1233 | fs_ext_js__on_set_attr | |
| 1234 | ); | |
| 1235 | ||
| 1236 | free(name); | |
| 1237 | ||
| 1238 | if (err < 0) { | |
| 1239 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1240 | assert(err == 0); | |
| 1241 | ||
| 1242 | return NULL; | |
| 1243 | } | |
| 1244 | ||
| 1245 | req->env = env; | |
| 1246 | req->exiting = false; | |
| 1247 | ||
| 1248 | err = js_create_reference(env, argv[5], 1, &req->ctx); | |
| 1249 | assert(err == 0); | |
| 1250 | ||
| 1251 | err = js_create_reference(env, argv[6], 1, &req->cb); | |
| 1252 | assert(err == 0); | |
| 1253 | ||
| 1254 | err = js_add_deferred_teardown_callback(env, fs_ext_js__on_teardown, (void *) &req->exiting, &req->teardown); | |
| 1255 | assert(err == 0); | |
| 1256 | ||
| 1257 | return handle; | |
| 1258 | } | |
| 1259 | ||
| 1260 | static js_value_t * | |
| 1261 | fs_ext_js_remove_attr(js_env_t *env, js_callback_info_t *info) { | |
| 1262 | int err; | |
| 1263 | ||
| 1264 | size_t argc = 4; | |
| 1265 | js_value_t *argv[4]; | |
| 1266 | ||
| 1267 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1268 | assert(err == 0); | |
| 1269 | ||
| 1270 | assert(argc == 4); | |
| 1271 | ||
| 1272 | int64_t fd; | |
| 1273 | err = js_get_value_int64(env, argv[0], &fd); | |
| 1274 | assert(err == 0); | |
| 1275 | ||
| 1276 | size_t name_len; | |
| 1277 | err = js_get_value_string_utf8(env, argv[1], NULL, 0, &name_len); | |
| 1278 | assert(err == 0); | |
| 1279 | ||
| 1280 | name_len += 1 /* NULL */; | |
| 1281 | ||
| 1282 | utf8_t *name = malloc(name_len); | |
| 1283 | err = js_get_value_string_utf8(env, argv[1], name, name_len, NULL); | |
| 1284 | assert(err == 0); | |
| 1285 | ||
| 1286 | js_value_t *handle; | |
| 1287 | ||
| 1288 | fs_ext_js_remove_attr_t *req; | |
| 1289 | err = js_create_arraybuffer(env, sizeof(fs_ext_js_remove_attr_t), (void **) &req, &handle); | |
| 1290 | assert(err == 0); | |
| 1291 | ||
| 1292 | uv_loop_t *loop; | |
| 1293 | err = js_get_env_loop(env, &loop); | |
| 1294 | assert(err == 0); | |
| 1295 | ||
| 1296 | err = fs_ext_remove_attr( | |
| 1297 | loop, | |
| 1298 | (fs_ext_remove_attr_t *) req, | |
| 1299 | uv_get_osfhandle(fd), | |
| 1300 | (const char *) name, | |
| 1301 | fs_ext_js__on_remove_attr | |
| 1302 | ); | |
| 1303 | ||
| 1304 | free(name); | |
| 1305 | ||
| 1306 | if (err < 0) { | |
| 1307 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1308 | assert(err == 0); | |
| 1309 | ||
| 1310 | return NULL; | |
| 1311 | } | |
| 1312 | ||
| 1313 | req->env = env; | |
| 1314 | req->exiting = false; | |
| 1315 | ||
| 1316 | err = js_create_reference(env, argv[2], 1, &req->ctx); | |
| 1317 | assert(err == 0); | |
| 1318 | ||
| 1319 | err = js_create_reference(env, argv[3], 1, &req->cb); | |
| 1320 | assert(err == 0); | |
| 1321 | ||
| 1322 | err = js_add_deferred_teardown_callback(env, fs_ext_js__on_teardown, (void *) &req->exiting, &req->teardown); | |
| 1323 | assert(err == 0); | |
| 1324 | ||
| 1325 | return handle; | |
| 1326 | } | |
| 1327 | ||
| 1328 | static js_value_t * | |
| 1329 | fs_ext_js_list_attrs(js_env_t *env, js_callback_info_t *info) { | |
| 1330 | int err; | |
| 1331 | ||
| 1332 | size_t argc = 3; | |
| 1333 | js_value_t *argv[3]; | |
| 1334 | ||
| 1335 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 1336 | assert(err == 0); | |
| 1337 | ||
| 1338 | assert(argc == 3); | |
| 1339 | ||
| 1340 | int64_t fd; | |
| 1341 | err = js_get_value_int64(env, argv[0], &fd); | |
| 1342 | assert(err == 0); | |
| 1343 | ||
| 1344 | js_value_t *handle; | |
| 1345 | ||
| 1346 | fs_ext_js_list_attrs_t *req; | |
| 1347 | err = js_create_arraybuffer(env, sizeof(fs_ext_js_list_attrs_t), (void **) &req, &handle); | |
| 1348 | assert(err == 0); | |
| 1349 | ||
| 1350 | uv_loop_t *loop; | |
| 1351 | err = js_get_env_loop(env, &loop); | |
| 1352 | assert(err == 0); | |
| 1353 | ||
| 1354 | err = fs_ext_list_attrs( | |
| 1355 | loop, | |
| 1356 | (fs_ext_list_attrs_t *) req, | |
| 1357 | uv_get_osfhandle(fd), | |
| 1358 | fs_ext_js__on_list_attrs | |
| 1359 | ); | |
| 1360 | ||
| 1361 | if (err < 0) { | |
| 1362 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1363 | assert(err == 0); | |
| 1364 | ||
| 1365 | return NULL; | |
| 1366 | } | |
| 1367 | ||
| 1368 | req->env = env; | |
| 1369 | req->exiting = false; | |
| 1370 | ||
| 1371 | err = js_create_reference(env, argv[1], 1, &req->ctx); | |
| 1372 | assert(err == 0); | |
| 1373 | ||
| 1374 | err = js_create_reference(env, argv[2], 1, &req->cb); | |
| 1375 | assert(err == 0); | |
| 1376 | ||
| 1377 | err = js_add_deferred_teardown_callback(env, fs_ext_js__on_teardown, (void *) &req->exiting, &req->teardown); | |
| 1378 | assert(err == 0); | |
| 1379 | ||
| 1380 | return handle; | |
| 1381 | } | |
| 1382 | ||
| 1383 | static js_value_t * | |
| 1384 | fs_ext_js_exports(js_env_t *env, js_value_t *exports) { | |
| 1385 | int err; | |
| 1386 | ||
| 1387 | #define V(name, fn) \ | |
| 1388 | { \ | |
| 1389 | js_value_t *val; \ | |
| 1390 | err = js_create_function(env, name, -1, fn, NULL, &val); \ | |
| 1391 | assert(err == 0); \ | |
| 1392 | err = js_set_named_property(env, exports, name, val); \ | |
| 1393 | assert(err == 0); \ | |
| 1394 | } | |
| 1395 | ||
| 1396 | V("tryLock", fs_ext_js_try_lock) | |
| 1397 | V("waitForLock", fs_ext_js_wait_for_lock) | |
| 1398 | V("tryDowngradeLock", fs_ext_js_try_downgrade_lock) | |
| 1399 | V("waitForDowngradeLock", fs_ext_js_wait_for_downgrade_lock) | |
| 1400 | V("tryUpgradeLock", fs_ext_js_try_upgrade_lock) | |
| 1401 | V("waitForUpgradeLock", fs_ext_js_wait_for_upgrade_lock) | |
| 1402 | V("unlock", fs_ext_js_unlock) | |
| 1403 | V("trim", fs_ext_js_trim) | |
| 1404 | V("sparse", fs_ext_js_sparse) | |
| 1405 | V("swap", fs_ext_js_swap) | |
| 1406 | V("getAttr", fs_ext_js_get_attr) | |
| 1407 | V("setAttr", fs_ext_js_set_attr) | |
| 1408 | V("removeAttr", fs_ext_js_remove_attr) | |
| 1409 | V("listAttrs", fs_ext_js_list_attrs) | |
| 1410 | #undef V | |
| 1411 | ||
| 1412 | return exports; | |
| 1413 | } | |
| 1414 | ||
| 1415 | BARE_MODULE(fs_ext_js, fs_ext_js_exports) |
| … | … | @@ -0,0 +1,3 @@ |
| 1 | require.addon = require('require-addon') | |
| 2 | ||
| 3 | module.exports = require.addon('.', __filename) |
| … | … | @@ -0,0 +1,193 @@ |
| 1 | #ifndef FS_EXT_H | |
| 2 | #define FS_EXT_H | |
| 3 | ||
| 4 | #ifdef __cplusplus | |
| 5 | extern "C" { | |
| 6 | #endif | |
| 7 | ||
| 8 | #include <stdint.h> | |
| 9 | #include <uv.h> | |
| 10 | ||
| 11 | // Requests | |
| 12 | ||
| 13 | typedef struct fs_ext_lock_s fs_ext_lock_t; | |
| 14 | typedef struct fs_ext_trim_s fs_ext_trim_t; | |
| 15 | typedef struct fs_ext_sparse_s fs_ext_sparse_t; | |
| 16 | typedef struct fs_ext_swap_s fs_ext_swap_t; | |
| 17 | typedef struct fs_ext_get_attr_s fs_ext_get_attr_t; | |
| 18 | typedef struct fs_ext_set_attr_s fs_ext_set_attr_t; | |
| 19 | typedef struct fs_ext_remove_attr_s fs_ext_remove_attr_t; | |
| 20 | typedef struct fs_ext_list_attrs_s fs_ext_list_attrs_t; | |
| 21 | ||
| 22 | // Callbacks | |
| 23 | ||
| 24 | typedef void (*fs_ext_lock_cb)(fs_ext_lock_t *req, int status); | |
| 25 | typedef void (*fs_ext_trim_cb)(fs_ext_trim_t *req, int status); | |
| 26 | typedef void (*fs_ext_sparse_cb)(fs_ext_sparse_t *req, int status); | |
| 27 | typedef void (*fs_ext_swap_cb)(fs_ext_swap_t *req, int status); | |
| 28 | typedef void (*fs_ext_get_attr_cb)(fs_ext_get_attr_t *req, int status, const uv_buf_t *value); | |
| 29 | typedef void (*fs_ext_set_attr_cb)(fs_ext_set_attr_t *req, int status); | |
| 30 | typedef void (*fs_ext_remove_attr_cb)(fs_ext_remove_attr_t *req, int status); | |
| 31 | typedef void (*fs_ext_list_attrs_cb)(fs_ext_list_attrs_t *req, int status, const char *attrs[], ssize_t len); | |
| 32 | ||
| 33 | typedef enum { | |
| 34 | FS_EXT_RDLOCK = 1, | |
| 35 | FS_EXT_WRLOCK, | |
| 36 | } fs_ext_lock_type_t; | |
| 37 | ||
| 38 | struct fs_ext_lock_s { | |
| 39 | uv_thread_t thread; | |
| 40 | uv_async_t signal; | |
| 41 | ||
| 42 | uv_os_fd_t fd; | |
| 43 | uint64_t offset; | |
| 44 | uint64_t length; | |
| 45 | fs_ext_lock_type_t type; | |
| 46 | ||
| 47 | fs_ext_lock_cb cb; | |
| 48 | ||
| 49 | int result; | |
| 50 | ||
| 51 | void *data; | |
| 52 | }; | |
| 53 | ||
| 54 | struct fs_ext_trim_s { | |
| 55 | uv_work_t req; | |
| 56 | ||
| 57 | uv_os_fd_t fd; | |
| 58 | uint64_t offset; | |
| 59 | uint64_t length; | |
| 60 | ||
| 61 | fs_ext_trim_cb cb; | |
| 62 | ||
| 63 | int result; | |
| 64 | ||
| 65 | void *data; | |
| 66 | }; | |
| 67 | ||
| 68 | struct fs_ext_sparse_s { | |
| 69 | uv_work_t req; | |
| 70 | ||
| 71 | uv_os_fd_t fd; | |
| 72 | ||
| 73 | fs_ext_sparse_cb cb; | |
| 74 | ||
| 75 | int result; | |
| 76 | ||
| 77 | void *data; | |
| 78 | }; | |
| 79 | ||
| 80 | struct fs_ext_swap_s { | |
| 81 | uv_work_t req; | |
| 82 | ||
| 83 | char *from; | |
| 84 | char *to; | |
| 85 | ||
| 86 | fs_ext_swap_cb cb; | |
| 87 | ||
| 88 | int result; | |
| 89 | ||
| 90 | void *data; | |
| 91 | }; | |
| 92 | ||
| 93 | struct fs_ext_get_attr_s { | |
| 94 | uv_work_t req; | |
| 95 | ||
| 96 | uv_os_fd_t fd; | |
| 97 | char *name; | |
| 98 | uv_buf_t value; | |
| 99 | ||
| 100 | fs_ext_get_attr_cb cb; | |
| 101 | ||
| 102 | int result; | |
| 103 | ||
| 104 | void *data; | |
| 105 | }; | |
| 106 | ||
| 107 | struct fs_ext_set_attr_s { | |
| 108 | uv_work_t req; | |
| 109 | ||
| 110 | uv_os_fd_t fd; | |
| 111 | char *name; | |
| 112 | uv_buf_t value; | |
| 113 | ||
| 114 | fs_ext_set_attr_cb cb; | |
| 115 | ||
| 116 | int result; | |
| 117 | ||
| 118 | void *data; | |
| 119 | }; | |
| 120 | ||
| 121 | struct fs_ext_remove_attr_s { | |
| 122 | uv_work_t req; | |
| 123 | ||
| 124 | uv_os_fd_t fd; | |
| 125 | char *name; | |
| 126 | ||
| 127 | fs_ext_remove_attr_cb cb; | |
| 128 | ||
| 129 | int result; | |
| 130 | ||
| 131 | void *data; | |
| 132 | }; | |
| 133 | ||
| 134 | struct fs_ext_list_attrs_s { | |
| 135 | uv_work_t req; | |
| 136 | ||
| 137 | uv_os_fd_t fd; | |
| 138 | char *names; | |
| 139 | size_t length; | |
| 140 | ||
| 141 | fs_ext_list_attrs_cb cb; | |
| 142 | ||
| 143 | int result; | |
| 144 | ||
| 145 | void *data; | |
| 146 | }; | |
| 147 | ||
| 148 | int | |
| 149 | fs_ext_try_lock(uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_type_t type); | |
| 150 | ||
| 151 | int | |
| 152 | fs_ext_wait_for_lock(uv_loop_t *loop, fs_ext_lock_t *req, uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_type_t type, fs_ext_lock_cb cb); | |
| 153 | ||
| 154 | int | |
| 155 | fs_ext_try_downgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length); | |
| 156 | ||
| 157 | int | |
| 158 | fs_ext_wait_for_downgrade_lock(uv_loop_t *loop, fs_ext_lock_t *req, uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_cb cb); | |
| 159 | ||
| 160 | int | |
| 161 | fs_ext_try_upgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length); | |
| 162 | ||
| 163 | int | |
| 164 | fs_ext_wait_for_upgrade_lock(uv_loop_t *loop, fs_ext_lock_t *req, uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_cb cb); | |
| 165 | ||
| 166 | int | |
| 167 | fs_ext_unlock(uv_os_fd_t fd, uint64_t offset, size_t length); | |
| 168 | ||
| 169 | int | |
| 170 | fs_ext_trim(uv_loop_t *loop, fs_ext_trim_t *req, uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_trim_cb cb); | |
| 171 | ||
| 172 | int | |
| 173 | fs_ext_sparse(uv_loop_t *loop, fs_ext_sparse_t *req, uv_os_fd_t fd, fs_ext_sparse_cb cb); | |
| 174 | ||
| 175 | int | |
| 176 | fs_ext_swap(uv_loop_t *loop, fs_ext_swap_t *req, const char *from, const char *to, fs_ext_swap_cb cb); | |
| 177 | ||
| 178 | int | |
| 179 | fs_ext_get_attr(uv_loop_t *loop, fs_ext_get_attr_t *req, uv_os_fd_t fd, const char *name, fs_ext_get_attr_cb cb); | |
| 180 | ||
| 181 | int | |
| 182 | fs_ext_set_attr(uv_loop_t *loop, fs_ext_set_attr_t *req, uv_os_fd_t fd, const char *name, const uv_buf_t *value, fs_ext_set_attr_cb cb); | |
| 183 | ||
| 184 | int | |
| 185 | fs_ext_remove_attr(uv_loop_t *loop, fs_ext_remove_attr_t *req, uv_os_fd_t fd, const char *name, fs_ext_remove_attr_cb cb); | |
| 186 | ||
| 187 | int | |
| 188 | fs_ext_list_attrs(uv_loop_t *loop, fs_ext_list_attrs_t *req, uv_os_fd_t fd, fs_ext_list_attrs_cb cb); | |
| 189 | ||
| 190 | #ifdef __cplusplus | |
| 191 | } | |
| 192 | #endif | |
| 193 | #endif // FS_EXT_H |
| … | … | @@ -0,0 +1,318 @@ |
| 1 | const { isWindows } = require('which-runtime') | |
| 2 | const binding = require('./binding') | |
| 3 | ||
| 4 | function onwork(err, result) { | |
| 5 | if (err) this.reject(err) | |
| 6 | else this.resolve(result) | |
| 7 | } | |
| 8 | ||
| 9 | exports.tryLock = function tryLock(fd, offset = 0, length = 0, opts = {}) { | |
| 10 | if (typeof offset === 'object') { | |
| 11 | opts = offset | |
| 12 | offset = 0 | |
| 13 | } | |
| 14 | ||
| 15 | if (typeof length === 'object') { | |
| 16 | opts = length | |
| 17 | length = 0 | |
| 18 | } | |
| 19 | ||
| 20 | if (typeof opts !== 'object' || opts === null) { | |
| 21 | opts = {} | |
| 22 | } | |
| 23 | ||
| 24 | try { | |
| 25 | binding.tryLock(fd, offset, length, opts.shared !== true) | |
| 26 | } catch (err) { | |
| 27 | if (err.code === 'EAGAIN') return false | |
| 28 | throw err | |
| 29 | } | |
| 30 | ||
| 31 | return true | |
| 32 | } | |
| 33 | ||
| 34 | exports.waitForLock = function waitForLock( | |
| 35 | fd, | |
| 36 | offset = 0, | |
| 37 | length = 0, | |
| 38 | opts = {} | |
| 39 | ) { | |
| 40 | if (typeof offset === 'object') { | |
| 41 | opts = offset | |
| 42 | offset = 0 | |
| 43 | } | |
| 44 | ||
| 45 | if (typeof length === 'object') { | |
| 46 | opts = length | |
| 47 | length = 0 | |
| 48 | } | |
| 49 | ||
| 50 | if (typeof opts !== 'object' || opts === null) { | |
| 51 | opts = {} | |
| 52 | } | |
| 53 | ||
| 54 | const req = { | |
| 55 | handle: null, | |
| 56 | resolve: null, | |
| 57 | reject: null | |
| 58 | } | |
| 59 | ||
| 60 | const promise = new Promise((resolve, reject) => { | |
| 61 | req.resolve = resolve | |
| 62 | req.reject = reject | |
| 63 | }) | |
| 64 | ||
| 65 | try { | |
| 66 | req.handle = binding.waitForLock( | |
| 67 | fd, | |
| 68 | offset, | |
| 69 | length, | |
| 70 | opts.shared !== true, | |
| 71 | req, | |
| 72 | onwork | |
| 73 | ) | |
| 74 | } catch (err) { | |
| 75 | return Promise.reject(err) | |
| 76 | } | |
| 77 | ||
| 78 | return promise | |
| 79 | } | |
| 80 | ||
| 81 | exports.tryDowngradeLock = function tryDowngradeLock( | |
| 82 | fd, | |
| 83 | offset = 0, | |
| 84 | length = 0 | |
| 85 | ) { | |
| 86 | try { | |
| 87 | binding.tryDowngradeLock(fd, offset, length) | |
| 88 | } catch (err) { | |
| 89 | if (err.code === 'EAGAIN') return false | |
| 90 | throw err | |
| 91 | } | |
| 92 | ||
| 93 | return true | |
| 94 | } | |
| 95 | ||
| 96 | exports.waitForDowngradeLock = function downgradeLock( | |
| 97 | fd, | |
| 98 | offset = 0, | |
| 99 | length = 0 | |
| 100 | ) { | |
| 101 | const req = { | |
| 102 | handle: null, | |
| 103 | resolve: null, | |
| 104 | reject: null | |
| 105 | } | |
| 106 | ||
| 107 | const promise = new Promise((resolve, reject) => { | |
| 108 | req.resolve = resolve | |
| 109 | req.reject = reject | |
| 110 | }) | |
| 111 | ||
| 112 | try { | |
| 113 | req.handle = binding.waitForDowngradeLock(fd, offset, length, req, onwork) | |
| 114 | } catch (err) { | |
| 115 | return Promise.reject(err) | |
| 116 | } | |
| 117 | ||
| 118 | return promise | |
| 119 | } | |
| 120 | ||
| 121 | exports.tryUpgradeLock = function tryUpgradeLock(fd, offset = 0, length = 0) { | |
| 122 | try { | |
| 123 | binding.tryUpgradeLock(fd, offset, length) | |
| 124 | } catch (err) { | |
| 125 | if (err.code === 'EAGAIN') return false | |
| 126 | throw err | |
| 127 | } | |
| 128 | ||
| 129 | return true | |
| 130 | } | |
| 131 | ||
| 132 | exports.waitForUpgradeLock = function upgradeLock(fd, offset = 0, length = 0) { | |
| 133 | const req = { | |
| 134 | handle: null, | |
| 135 | resolve: null, | |
| 136 | reject: null | |
| 137 | } | |
| 138 | ||
| 139 | const promise = new Promise((resolve, reject) => { | |
| 140 | req.resolve = resolve | |
| 141 | req.reject = reject | |
| 142 | }) | |
| 143 | ||
| 144 | try { | |
| 145 | req.handle = binding.waitForUpgradeLock(fd, offset, length, req, onwork) | |
| 146 | } catch (err) { | |
| 147 | return Promise.reject(err) | |
| 148 | } | |
| 149 | ||
| 150 | return promise | |
| 151 | } | |
| 152 | ||
| 153 | exports.unlock = function unlock(fd, offset = 0, length = 0) { | |
| 154 | binding.unlock(fd, offset, length) | |
| 155 | } | |
| 156 | ||
| 157 | exports.trim = function trim(fd, offset, length) { | |
| 158 | const req = { | |
| 159 | handle: null, | |
| 160 | resolve: null, | |
| 161 | reject: null | |
| 162 | } | |
| 163 | ||
| 164 | const promise = new Promise((resolve, reject) => { | |
| 165 | req.resolve = resolve | |
| 166 | req.reject = reject | |
| 167 | }) | |
| 168 | ||
| 169 | try { | |
| 170 | req.handle = binding.trim(fd, offset, length, req, onwork) | |
| 171 | } catch (err) { | |
| 172 | return Promise.reject(err) | |
| 173 | } | |
| 174 | ||
| 175 | return promise | |
| 176 | } | |
| 177 | ||
| 178 | exports.sparse = function sparse(fd) { | |
| 179 | // Short circuit on everything but Windows | |
| 180 | if (!isWindows) return Promise.resolve() | |
| 181 | ||
| 182 | const req = { | |
| 183 | handle: null, | |
| 184 | resolve: null, | |
| 185 | reject: null | |
| 186 | } | |
| 187 | ||
| 188 | const promise = new Promise((resolve, reject) => { | |
| 189 | req.resolve = resolve | |
| 190 | req.reject = reject | |
| 191 | }) | |
| 192 | ||
| 193 | try { | |
| 194 | req.handle = binding.sparse(fd, req, onwork) | |
| 195 | } catch (err) { | |
| 196 | return Promise.reject(err) | |
| 197 | } | |
| 198 | ||
| 199 | return promise | |
| 200 | } | |
| 201 | ||
| 202 | exports.swap = function swap(from, to) { | |
| 203 | const req = { | |
| 204 | handle: null, | |
| 205 | resolve: null, | |
| 206 | reject: null | |
| 207 | } | |
| 208 | ||
| 209 | const promise = new Promise((resolve, reject) => { | |
| 210 | req.resolve = resolve | |
| 211 | req.reject = reject | |
| 212 | }) | |
| 213 | ||
| 214 | try { | |
| 215 | req.handle = binding.swap(from, to, req, onwork) | |
| 216 | } catch (err) { | |
| 217 | return Promise.reject(err) | |
| 218 | } | |
| 219 | ||
| 220 | return promise | |
| 221 | } | |
| 222 | ||
| 223 | exports.getAttr = function getAttr(fd, name) { | |
| 224 | const req = { | |
| 225 | handle: null, | |
| 226 | resolve: null, | |
| 227 | reject: null | |
| 228 | } | |
| 229 | ||
| 230 | const promise = new Promise((resolve, reject) => { | |
| 231 | req.resolve = resolve | |
| 232 | req.reject = reject | |
| 233 | }) | |
| 234 | ||
| 235 | try { | |
| 236 | req.handle = binding.getAttr(fd, name, req, onwork) | |
| 237 | } catch (err) { | |
| 238 | return Promise.reject(err) | |
| 239 | } | |
| 240 | ||
| 241 | return promise.then((buffer) => | |
| 242 | buffer === null ? null : Buffer.from(buffer) | |
| 243 | ) | |
| 244 | } | |
| 245 | ||
| 246 | exports.setAttr = function setAttr(fd, name, value, encoding) { | |
| 247 | if (typeof value === 'string') value = Buffer.from(value, encoding) | |
| 248 | ||
| 249 | const req = { | |
| 250 | value, | |
| 251 | handle: null, | |
| 252 | resolve: null, | |
| 253 | reject: null | |
| 254 | } | |
| 255 | ||
| 256 | const promise = new Promise((resolve, reject) => { | |
| 257 | req.resolve = resolve | |
| 258 | req.reject = reject | |
| 259 | }) | |
| 260 | ||
| 261 | try { | |
| 262 | req.handle = binding.setAttr( | |
| 263 | fd, | |
| 264 | name, | |
| 265 | value.buffer, | |
| 266 | value.byteOffset, | |
| 267 | value.byteLength, | |
| 268 | req, | |
| 269 | onwork | |
| 270 | ) | |
| 271 | } catch (err) { | |
| 272 | return Promise.reject(err) | |
| 273 | } | |
| 274 | ||
| 275 | return promise | |
| 276 | } | |
| 277 | ||
| 278 | exports.removeAttr = function removeAttr(fd, name) { | |
| 279 | const req = { | |
| 280 | handle: null, | |
| 281 | resolve: null, | |
| 282 | reject: null | |
| 283 | } | |
| 284 | ||
| 285 | const promise = new Promise((resolve, reject) => { | |
| 286 | req.resolve = resolve | |
| 287 | req.reject = reject | |
| 288 | }) | |
| 289 | ||
| 290 | try { | |
| 291 | req.handle = binding.removeAttr(fd, name, req, onwork) | |
| 292 | } catch (err) { | |
| 293 | return Promise.reject(err) | |
| 294 | } | |
| 295 | ||
| 296 | return promise | |
| 297 | } | |
| 298 | ||
| 299 | exports.listAttrs = function listAttrs(fd) { | |
| 300 | const req = { | |
| 301 | handle: null, | |
| 302 | resolve: null, | |
| 303 | reject: null | |
| 304 | } | |
| 305 | ||
| 306 | const promise = new Promise((resolve, reject) => { | |
| 307 | req.resolve = resolve | |
| 308 | req.reject = reject | |
| 309 | }) | |
| 310 | ||
| 311 | try { | |
| 312 | req.handle = binding.listAttrs(fd, req, onwork) | |
| 313 | } catch (err) { | |
| 314 | return Promise.reject(err) | |
| 315 | } | |
| 316 | ||
| 317 | return promise | |
| 318 | } |
| … | … | @@ -0,0 +1,70 @@ |
| 1 | { | |
| 2 | "name": "fs-native-extensions", | |
| 3 | "version": "1.4.5", | |
| 4 | "description": "Native file system extensions for advanced file operations", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js", | |
| 8 | "macros.h", | |
| 9 | "binding.c", | |
| 10 | "binding.js", | |
| 11 | "CMakeLists.txt", | |
| 12 | "include", | |
| 13 | "src", | |
| 14 | "prebuilds" | |
| 15 | ], | |
| 16 | "imports": { | |
| 17 | "child_process": { | |
| 18 | "bare": "bare-subprocess", | |
| 19 | "default": "child_process" | |
| 20 | }, | |
| 21 | "fs": { | |
| 22 | "bare": "bare-fs", | |
| 23 | "default": "fs" | |
| 24 | }, | |
| 25 | "fs/*": { | |
| 26 | "bare": "bare-fs/*", | |
| 27 | "default": "fs/*" | |
| 28 | }, | |
| 29 | "path": { | |
| 30 | "bare": "bare-path", | |
| 31 | "default": "path" | |
| 32 | } | |
| 33 | }, | |
| 34 | "addon": true, | |
| 35 | "scripts": { | |
| 36 | "test": "npm run lint && npm run test:bare && npm run test:node", | |
| 37 | "test:bare": "bare test.js", | |
| 38 | "test:node": "node test.js", | |
| 39 | "lint": "prettier . --check" | |
| 40 | }, | |
| 41 | "repository": { | |
| 42 | "type": "git", | |
| 43 | "url": "git+https://github.com/holepunchto/fs-native-extensions.git" | |
| 44 | }, | |
| 45 | "author": "Kasper Isager Dalsgarð <kasper@funktionel.co>", | |
| 46 | "license": "Apache-2.0", | |
| 47 | "bugs": { | |
| 48 | "url": "https://github.com/holepunchto/fs-native-extensions/issues" | |
| 49 | }, | |
| 50 | "homepage": "https://github.com/holepunchto/fs-native-extensions#readme", | |
| 51 | "dependencies": { | |
| 52 | "require-addon": "^1.1.0", | |
| 53 | "which-runtime": "^1.2.0" | |
| 54 | }, | |
| 55 | "devDependencies": { | |
| 56 | "bare-compat-napi": "^1.3.8", | |
| 57 | "bare-fs": "^4.4.4", | |
| 58 | "bare-path": "^3.0.0", | |
| 59 | "bare-subprocess": "^4.0.5", | |
| 60 | "brittle": "^3.1.1", | |
| 61 | "cmake-bare": "^1.1.10", | |
| 62 | "cmake-fetch": "^1.4.7", | |
| 63 | "cmake-napi": "^1.0.5", | |
| 64 | "cmake-npm": "^1.1.0", | |
| 65 | "minimist": "^1.2.6", | |
| 66 | "prettier": "^3.5.3", | |
| 67 | "prettier-config-standard": "^7.0.0", | |
| 68 | "test-tmp": "^1.2.1" | |
| 69 | } | |
| 70 | } |
| … | … | @@ -0,0 +1,197 @@ |
| 1 | #include <errno.h> | |
| 2 | #include <fcntl.h> | |
| 3 | #include <stdint.h> | |
| 4 | #include <stdlib.h> | |
| 5 | #include <string.h> | |
| 6 | #include <sys/xattr.h> | |
| 7 | #include <unistd.h> | |
| 8 | #include <uv.h> | |
| 9 | ||
| 10 | #include "../include/fs-ext.h" | |
| 11 | ||
| 12 | #include "platform.h" | |
| 13 | ||
| 14 | int | |
| 15 | fs_ext__try_lock(uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_type_t type) { | |
| 16 | if (offset != 0 || length != 0) return UV_EINVAL; | |
| 17 | ||
| 18 | int res = flock(fd, (type == FS_EXT_WRLOCK ? LOCK_EX : LOCK_SH) | LOCK_NB); | |
| 19 | ||
| 20 | return res == -1 ? uv_translate_sys_error(errno) : res; | |
| 21 | } | |
| 22 | ||
| 23 | int | |
| 24 | fs_ext__wait_for_lock(uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_type_t type) { | |
| 25 | if (offset != 0 || length != 0) return UV_EINVAL; | |
| 26 | ||
| 27 | int res = flock(fd, type == FS_EXT_WRLOCK ? LOCK_EX : LOCK_SH); | |
| 28 | ||
| 29 | return res == -1 ? uv_translate_sys_error(errno) : res; | |
| 30 | } | |
| 31 | ||
| 32 | int | |
| 33 | fs_ext__unlock(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 34 | if (offset != 0 || length != 0) return UV_EINVAL; | |
| 35 | ||
| 36 | int res = flock(fd, LOCK_UN); | |
| 37 | ||
| 38 | return res == -1 ? uv_translate_sys_error(errno) : res; | |
| 39 | } | |
| 40 | ||
| 41 | static inline size_t | |
| 42 | fs_ext__align_up(size_t n, size_t align) { | |
| 43 | return (n + (align - 1)) & -align; | |
| 44 | } | |
| 45 | ||
| 46 | static inline size_t | |
| 47 | fs_ext__align_down(size_t n, size_t align) { | |
| 48 | return n & -align; | |
| 49 | } | |
| 50 | ||
| 51 | static inline size_t | |
| 52 | fs_ext__min(size_t a, size_t b) { | |
| 53 | return a < b ? a : b; | |
| 54 | } | |
| 55 | ||
| 56 | int | |
| 57 | fs_ext__trim(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 58 | struct stat st; | |
| 59 | ||
| 60 | int res = fstat(fd, &st); | |
| 61 | ||
| 62 | if (res == -1) return uv_translate_sys_error(errno); | |
| 63 | ||
| 64 | size_t align = st.st_blksize; | |
| 65 | ||
| 66 | size_t aligned_offset = fs_ext__align_up(offset, align); | |
| 67 | ||
| 68 | if (aligned_offset != offset) { | |
| 69 | size_t zero_length = fs_ext__min(length, aligned_offset - offset); | |
| 70 | ||
| 71 | void *buf = calloc(1, zero_length); | |
| 72 | ||
| 73 | ssize_t res = pwrite(fd, buf, zero_length, offset); | |
| 74 | ||
| 75 | free(buf); | |
| 76 | ||
| 77 | if (res == -1) return uv_translate_sys_error(errno); | |
| 78 | ||
| 79 | offset += zero_length; | |
| 80 | length -= zero_length; | |
| 81 | } | |
| 82 | ||
| 83 | size_t aligned_length = fs_ext__align_down(length, align); | |
| 84 | ||
| 85 | if (aligned_length >= align) { | |
| 86 | struct fpunchhole data = { | |
| 87 | .fp_flags = 0, | |
| 88 | .reserved = 0, | |
| 89 | .fp_offset = offset, | |
| 90 | .fp_length = aligned_length, | |
| 91 | }; | |
| 92 | ||
| 93 | int res = fcntl(fd, F_PUNCHHOLE, &data); | |
| 94 | ||
| 95 | if (res == -1) return uv_translate_sys_error(errno); | |
| 96 | ||
| 97 | offset += aligned_length; | |
| 98 | length -= aligned_length; | |
| 99 | } | |
| 100 | ||
| 101 | if (length > 0) { | |
| 102 | void *buf = calloc(1, length); | |
| 103 | ||
| 104 | ssize_t res = pwrite(fd, buf, length, offset); | |
| 105 | ||
| 106 | free(buf); | |
| 107 | ||
| 108 | if (res == -1) return uv_translate_sys_error(errno); | |
| 109 | } | |
| 110 | ||
| 111 | return 0; | |
| 112 | } | |
| 113 | ||
| 114 | int | |
| 115 | fs_ext__swap(const char *from, const char *to) { | |
| 116 | int res = renameatx_np(AT_FDCWD, from, AT_FDCWD, to, RENAME_SWAP); | |
| 117 | ||
| 118 | return res == -1 ? uv_translate_sys_error(errno) : res; | |
| 119 | } | |
| 120 | ||
| 121 | int | |
| 122 | fs_ext__get_attr(uv_os_fd_t fd, const char *name, uv_buf_t *value) { | |
| 123 | int res = fgetxattr(fd, name, NULL, 0, 0, 0); | |
| 124 | ||
| 125 | if (res == -1) { | |
| 126 | if (errno == ENOATTR) return UV_ENODATA; | |
| 127 | ||
| 128 | return uv_translate_sys_error(errno); | |
| 129 | } | |
| 130 | ||
| 131 | *value = uv_buf_init(malloc(res), res); | |
| 132 | ||
| 133 | res = fgetxattr(fd, name, value->base, value->len, 0, 0); | |
| 134 | ||
| 135 | return res == -1 ? uv_translate_sys_error(errno) : 0; | |
| 136 | } | |
| 137 | ||
| 138 | int | |
| 139 | fs_ext__set_attr(uv_os_fd_t fd, const char *name, const uv_buf_t *value) { | |
| 140 | int res = fsetxattr(fd, name, value->base, value->len, 0, 0); | |
| 141 | ||
| 142 | return res == -1 ? uv_translate_sys_error(errno) : 0; | |
| 143 | } | |
| 144 | ||
| 145 | int | |
| 146 | fs_ext__remove_attr(uv_os_fd_t fd, const char *name) { | |
| 147 | int res = fremovexattr(fd, name, 0); | |
| 148 | ||
| 149 | if (res == -1) { | |
| 150 | if (errno == ENOATTR) return UV_ENODATA; | |
| 151 | ||
| 152 | return uv_translate_sys_error(errno); | |
| 153 | } | |
| 154 | ||
| 155 | return 0; | |
| 156 | } | |
| 157 | ||
| 158 | int | |
| 159 | fs_ext__list_attrs(uv_os_fd_t fd, char **names, size_t *length) { | |
| 160 | int res = flistxattr(fd, NULL, 0, 0); | |
| 161 | ||
| 162 | if (res == -1) return uv_translate_sys_error(errno); | |
| 163 | ||
| 164 | *names = malloc(res); | |
| 165 | *length = res; | |
| 166 | ||
| 167 | res = flistxattr(fd, *names, *length, 0); | |
| 168 | ||
| 169 | if (res == -1) return uv_translate_sys_error(errno); | |
| 170 | ||
| 171 | size_t i = 0, j = 0, n = *length; | |
| 172 | ||
| 173 | *length = 0; | |
| 174 | ||
| 175 | while (i < n) { | |
| 176 | i += strlen(*names + i) + 1; | |
| 177 | *length += 1; | |
| 178 | } | |
| 179 | ||
| 180 | size_t offset = *length * sizeof(char *); | |
| 181 | ||
| 182 | *names = realloc(*names, offset + n); | |
| 183 | ||
| 184 | memmove(*names + offset, *names, n); | |
| 185 | ||
| 186 | i = 0; | |
| 187 | ||
| 188 | while (i < n) { | |
| 189 | char *name = *names + offset + i; | |
| 190 | i += strlen(name) + 1; | |
| 191 | ||
| 192 | memcpy(*names + j, &name, sizeof(char *)); | |
| 193 | j += sizeof(char *); | |
| 194 | } | |
| 195 | ||
| 196 | return 0; | |
| 197 | } |
| … | … | @@ -0,0 +1,146 @@ |
| 1 | #define _GNU_SOURCE | |
| 2 | ||
| 3 | #include <errno.h> | |
| 4 | #include <fcntl.h> | |
| 5 | #include <stdint.h> | |
| 6 | #include <stdlib.h> | |
| 7 | #include <string.h> | |
| 8 | #include <sys/syscall.h> | |
| 9 | #include <sys/xattr.h> | |
| 10 | #include <unistd.h> | |
| 11 | #include <uv.h> | |
| 12 | ||
| 13 | #include "../include/fs-ext.h" | |
| 14 | ||
| 15 | #include "platform.h" | |
| 16 | ||
| 17 | #ifndef RENAME_EXCHANGE | |
| 18 | #define RENAME_EXCHANGE 2 | |
| 19 | #endif | |
| 20 | ||
| 21 | int | |
| 22 | fs_ext__try_lock(uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_type_t type) { | |
| 23 | struct flock data = { | |
| 24 | .l_start = offset, | |
| 25 | .l_len = length, | |
| 26 | .l_pid = 0, | |
| 27 | .l_type = type == FS_EXT_WRLOCK ? F_WRLCK : F_RDLCK, | |
| 28 | .l_whence = SEEK_SET, | |
| 29 | }; | |
| 30 | ||
| 31 | int res = fcntl(fd, F_OFD_SETLK, &data); | |
| 32 | ||
| 33 | return res == -1 ? uv_translate_sys_error(errno) : res; | |
| 34 | } | |
| 35 | ||
| 36 | int | |
| 37 | fs_ext__wait_for_lock(uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_type_t type) { | |
| 38 | struct flock data = { | |
| 39 | .l_start = offset, | |
| 40 | .l_len = length, | |
| 41 | .l_pid = 0, | |
| 42 | .l_type = type == FS_EXT_WRLOCK ? F_WRLCK : F_RDLCK, | |
| 43 | .l_whence = SEEK_SET, | |
| 44 | }; | |
| 45 | ||
| 46 | int res = fcntl(fd, F_OFD_SETLKW, &data); | |
| 47 | ||
| 48 | return res == -1 ? uv_translate_sys_error(errno) : res; | |
| 49 | } | |
| 50 | ||
| 51 | int | |
| 52 | fs_ext__unlock(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 53 | struct flock data = { | |
| 54 | .l_start = offset, | |
| 55 | .l_len = length, | |
| 56 | .l_pid = 0, | |
| 57 | .l_type = F_UNLCK, | |
| 58 | .l_whence = SEEK_SET, | |
| 59 | }; | |
| 60 | ||
| 61 | int res = fcntl(fd, F_OFD_SETLK, &data); | |
| 62 | ||
| 63 | return res == -1 ? uv_translate_sys_error(errno) : res; | |
| 64 | } | |
| 65 | ||
| 66 | int | |
| 67 | fs_ext__trim(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 68 | int res = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, length); | |
| 69 | ||
| 70 | return res == -1 ? uv_translate_sys_error(errno) : res; | |
| 71 | } | |
| 72 | ||
| 73 | int | |
| 74 | fs_ext__swap(const char *from, const char *to) { | |
| 75 | int res = syscall(SYS_renameat2, AT_FDCWD, from, AT_FDCWD, to, RENAME_EXCHANGE); | |
| 76 | ||
| 77 | return res == -1 ? uv_translate_sys_error(errno) : res; | |
| 78 | } | |
| 79 | ||
| 80 | int | |
| 81 | fs_ext__get_attr(uv_os_fd_t fd, const char *name, uv_buf_t *value) { | |
| 82 | int res = fgetxattr(fd, name, NULL, 0); | |
| 83 | ||
| 84 | if (res == -1) return uv_translate_sys_error(errno); | |
| 85 | ||
| 86 | *value = uv_buf_init(malloc(res), res); | |
| 87 | ||
| 88 | res = fgetxattr(fd, name, value->base, value->len); | |
| 89 | ||
| 90 | return res == -1 ? uv_translate_sys_error(errno) : 0; | |
| 91 | } | |
| 92 | ||
| 93 | int | |
| 94 | fs_ext__set_attr(uv_os_fd_t fd, const char *name, const uv_buf_t *value) { | |
| 95 | int res = fsetxattr(fd, name, value->base, value->len, 0); | |
| 96 | ||
| 97 | return res == -1 ? uv_translate_sys_error(errno) : 0; | |
| 98 | } | |
| 99 | ||
| 100 | int | |
| 101 | fs_ext__remove_attr(uv_os_fd_t fd, const char *name) { | |
| 102 | int res = fremovexattr(fd, name); | |
| 103 | ||
| 104 | return res == -1 ? uv_translate_sys_error(errno) : 0; | |
| 105 | } | |
| 106 | ||
| 107 | int | |
| 108 | fs_ext__list_attrs(uv_os_fd_t fd, char **names, size_t *length) { | |
| 109 | int res = flistxattr(fd, NULL, 0); | |
| 110 | ||
| 111 | if (res == -1) return uv_translate_sys_error(errno); | |
| 112 | ||
| 113 | *names = malloc(res); | |
| 114 | *length = res; | |
| 115 | ||
| 116 | res = flistxattr(fd, *names, *length); | |
| 117 | ||
| 118 | if (res == -1) return uv_translate_sys_error(errno); | |
| 119 | ||
| 120 | size_t i = 0, j = 0, n = *length; | |
| 121 | ||
| 122 | *length = 0; | |
| 123 | ||
| 124 | while (i < n) { | |
| 125 | i += strlen(*names + i) + 1; | |
| 126 | *length += 1; | |
| 127 | } | |
| 128 | ||
| 129 | size_t offset = *length * sizeof(char *); | |
| 130 | ||
| 131 | *names = realloc(*names, offset + n); | |
| 132 | ||
| 133 | memmove(*names + offset, *names, n); | |
| 134 | ||
| 135 | i = 0; | |
| 136 | ||
| 137 | while (i < n) { | |
| 138 | char *name = *names + offset + i; | |
| 139 | i += strlen(name) + 1; | |
| 140 | ||
| 141 | memcpy(*names + j, &name, sizeof(char *)); | |
| 142 | j += sizeof(char *); | |
| 143 | } | |
| 144 | ||
| 145 | return 0; | |
| 146 | } |
| … | … | @@ -0,0 +1,51 @@ |
| 1 | #ifndef FS_EXT_PLATFORM_H | |
| 2 | #define FS_EXT_PLATFORM_H | |
| 3 | ||
| 4 | #include <stdint.h> | |
| 5 | #include <uv.h> | |
| 6 | ||
| 7 | #include "../include/fs-ext.h" | |
| 8 | ||
| 9 | int | |
| 10 | fs_ext__try_lock(uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_type_t type); | |
| 11 | ||
| 12 | int | |
| 13 | fs_ext__wait_for_lock(uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_type_t type); | |
| 14 | ||
| 15 | int | |
| 16 | fs_ext__try_downgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length); | |
| 17 | ||
| 18 | int | |
| 19 | fs_ext__wait_for_downgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length); | |
| 20 | ||
| 21 | int | |
| 22 | fs_ext__try_upgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length); | |
| 23 | ||
| 24 | int | |
| 25 | fs_ext__wait_for_upgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length); | |
| 26 | ||
| 27 | int | |
| 28 | fs_ext__unlock(uv_os_fd_t fd, uint64_t offset, size_t length); | |
| 29 | ||
| 30 | int | |
| 31 | fs_ext__trim(uv_os_fd_t fd, uint64_t offset, size_t length); | |
| 32 | ||
| 33 | int | |
| 34 | fs_ext__sparse(uv_os_fd_t fd); | |
| 35 | ||
| 36 | int | |
| 37 | fs_ext__swap(const char *from, const char *to); | |
| 38 | ||
| 39 | int | |
| 40 | fs_ext__get_attr(uv_os_fd_t fd, const char *name, uv_buf_t *value); | |
| 41 | ||
| 42 | int | |
| 43 | fs_ext__set_attr(uv_os_fd_t fd, const char *name, const uv_buf_t *value); | |
| 44 | ||
| 45 | int | |
| 46 | fs_ext__remove_attr(uv_os_fd_t fd, const char *name); | |
| 47 | ||
| 48 | int | |
| 49 | fs_ext__list_attrs(uv_os_fd_t fd, char **names, size_t *length); | |
| 50 | ||
| 51 | #endif // FS_EXT_PLATFORM_H |
| … | … | @@ -0,0 +1,33 @@ |
| 1 | #include <errno.h> | |
| 2 | #include <fcntl.h> | |
| 3 | #include <stdint.h> | |
| 4 | #include <uv.h> | |
| 5 | ||
| 6 | #include "../include/fs-ext.h" | |
| 7 | ||
| 8 | #include "platform.h" | |
| 9 | ||
| 10 | int | |
| 11 | fs_ext__try_downgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 12 | return fs_ext__try_lock(fd, offset, length, FS_EXT_RDLOCK); | |
| 13 | } | |
| 14 | ||
| 15 | int | |
| 16 | fs_ext__wait_for_downgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 17 | return fs_ext__wait_for_lock(fd, offset, length, FS_EXT_RDLOCK); | |
| 18 | } | |
| 19 | ||
| 20 | int | |
| 21 | fs_ext__try_upgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 22 | return fs_ext__try_lock(fd, offset, length, FS_EXT_WRLOCK); | |
| 23 | } | |
| 24 | ||
| 25 | int | |
| 26 | fs_ext__wait_for_upgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 27 | return fs_ext__wait_for_lock(fd, offset, length, FS_EXT_WRLOCK); | |
| 28 | } | |
| 29 | ||
| 30 | int | |
| 31 | fs_ext__sparse(uv_os_fd_t fd) { | |
| 32 | return 0; | |
| 33 | } |
| … | … | @@ -0,0 +1,397 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <stdint.h> | |
| 3 | #include <stdlib.h> | |
| 4 | #include <string.h> | |
| 5 | #include <uv.h> | |
| 6 | ||
| 7 | #include "../include/fs-ext.h" | |
| 8 | ||
| 9 | #include "platform.h" | |
| 10 | ||
| 11 | int | |
| 12 | fs_ext_try_lock(uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_type_t type) { | |
| 13 | int err = fs_ext__try_lock(fd, offset, length, type); | |
| 14 | ||
| 15 | if (err == UV_EACCES || err == UV_EAGAIN || err == UV_EBUSY) { | |
| 16 | err = UV_EAGAIN; | |
| 17 | } | |
| 18 | ||
| 19 | return err; | |
| 20 | } | |
| 21 | ||
| 22 | static void | |
| 23 | fs_ext__lock_close(uv_handle_t *handle) { | |
| 24 | fs_ext_lock_t *r = (fs_ext_lock_t *) handle->data; | |
| 25 | ||
| 26 | if (r->cb) r->cb(r, r->result); | |
| 27 | } | |
| 28 | ||
| 29 | static void | |
| 30 | fs_ext__lock_after_work(uv_async_t *signal) { | |
| 31 | int err; | |
| 32 | ||
| 33 | fs_ext_lock_t *r = (fs_ext_lock_t *) signal->data; | |
| 34 | ||
| 35 | err = uv_thread_join(&r->thread); | |
| 36 | assert(err == 0); | |
| 37 | ||
| 38 | uv_close((uv_handle_t *) signal, fs_ext__lock_close); | |
| 39 | } | |
| 40 | ||
| 41 | static void | |
| 42 | fs_ext__lock_work(void *data) { | |
| 43 | int err; | |
| 44 | ||
| 45 | fs_ext_lock_t *r = (fs_ext_lock_t *) data; | |
| 46 | ||
| 47 | r->result = fs_ext__wait_for_lock(r->fd, r->offset, r->length, r->type); | |
| 48 | ||
| 49 | err = uv_async_send(&r->signal); | |
| 50 | assert(err == 0); | |
| 51 | } | |
| 52 | ||
| 53 | int | |
| 54 | fs_ext_wait_for_lock(uv_loop_t *loop, fs_ext_lock_t *req, uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_type_t type, fs_ext_lock_cb cb) { | |
| 55 | int err; | |
| 56 | ||
| 57 | req->fd = fd; | |
| 58 | req->offset = offset; | |
| 59 | req->length = length; | |
| 60 | req->type = type; | |
| 61 | req->cb = cb; | |
| 62 | req->signal.data = (void *) req; | |
| 63 | ||
| 64 | err = uv_async_init(loop, &req->signal, fs_ext__lock_after_work); | |
| 65 | assert(err == 0); | |
| 66 | ||
| 67 | err = uv_thread_create(&req->thread, fs_ext__lock_work, (void *) req); | |
| 68 | assert(err == 0); | |
| 69 | ||
| 70 | return 0; | |
| 71 | } | |
| 72 | ||
| 73 | int | |
| 74 | fs_ext_try_downgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 75 | int err = fs_ext__try_downgrade_lock(fd, offset, length); | |
| 76 | ||
| 77 | if (err == UV_EACCES || err == UV_EAGAIN || err == UV_EBUSY) { | |
| 78 | err = UV_EAGAIN; | |
| 79 | } | |
| 80 | ||
| 81 | return err; | |
| 82 | } | |
| 83 | ||
| 84 | static void | |
| 85 | fs_ext__downgrade_lock_work(void *data) { | |
| 86 | int err; | |
| 87 | ||
| 88 | fs_ext_lock_t *r = (fs_ext_lock_t *) data; | |
| 89 | ||
| 90 | r->result = fs_ext__wait_for_downgrade_lock(r->fd, r->offset, r->length); | |
| 91 | ||
| 92 | err = uv_async_send(&r->signal); | |
| 93 | assert(err == 0); | |
| 94 | } | |
| 95 | ||
| 96 | int | |
| 97 | fs_ext_wait_for_downgrade_lock(uv_loop_t *loop, fs_ext_lock_t *req, uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_cb cb) { | |
| 98 | int err; | |
| 99 | ||
| 100 | req->fd = fd; | |
| 101 | req->offset = offset; | |
| 102 | req->length = length; | |
| 103 | req->type = FS_EXT_RDLOCK; | |
| 104 | req->cb = cb; | |
| 105 | req->signal.data = (void *) req; | |
| 106 | ||
| 107 | err = uv_async_init(loop, &req->signal, fs_ext__lock_after_work); | |
| 108 | assert(err == 0); | |
| 109 | ||
| 110 | err = uv_thread_create(&req->thread, fs_ext__downgrade_lock_work, (void *) req); | |
| 111 | assert(err == 0); | |
| 112 | ||
| 113 | return 0; | |
| 114 | } | |
| 115 | ||
| 116 | static void | |
| 117 | fs_ext__upgrade_lock_work(void *data) { | |
| 118 | int err; | |
| 119 | ||
| 120 | fs_ext_lock_t *r = (fs_ext_lock_t *) data; | |
| 121 | ||
| 122 | r->result = fs_ext__wait_for_upgrade_lock(r->fd, r->offset, r->length); | |
| 123 | ||
| 124 | err = uv_async_send(&r->signal); | |
| 125 | assert(err == 0); | |
| 126 | } | |
| 127 | ||
| 128 | int | |
| 129 | fs_ext_try_upgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 130 | int err = fs_ext__try_upgrade_lock(fd, offset, length); | |
| 131 | ||
| 132 | if (err == UV_EACCES || err == UV_EAGAIN || err == UV_EBUSY) { | |
| 133 | err = UV_EAGAIN; | |
| 134 | } | |
| 135 | ||
| 136 | return err; | |
| 137 | } | |
| 138 | ||
| 139 | int | |
| 140 | fs_ext_wait_for_upgrade_lock(uv_loop_t *loop, fs_ext_lock_t *req, uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_cb cb) { | |
| 141 | int err; | |
| 142 | ||
| 143 | req->fd = fd; | |
| 144 | req->offset = offset; | |
| 145 | req->length = length; | |
| 146 | req->type = FS_EXT_WRLOCK; | |
| 147 | req->cb = cb; | |
| 148 | req->signal.data = (void *) req; | |
| 149 | ||
| 150 | err = uv_async_init(loop, &req->signal, fs_ext__lock_after_work); | |
| 151 | assert(err == 0); | |
| 152 | ||
| 153 | err = uv_thread_create(&req->thread, fs_ext__upgrade_lock_work, (void *) req); | |
| 154 | assert(err == 0); | |
| 155 | ||
| 156 | return 0; | |
| 157 | } | |
| 158 | ||
| 159 | int | |
| 160 | fs_ext_unlock(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 161 | return fs_ext__unlock(fd, offset, length); | |
| 162 | } | |
| 163 | ||
| 164 | static void | |
| 165 | fs_ext__trim_work(uv_work_t *req) { | |
| 166 | fs_ext_trim_t *r = (fs_ext_trim_t *) req->data; | |
| 167 | ||
| 168 | r->result = fs_ext__trim(r->fd, r->offset, r->length); | |
| 169 | } | |
| 170 | ||
| 171 | static void | |
| 172 | fs_ext__trim_after_work(uv_work_t *req, int status) { | |
| 173 | fs_ext_trim_t *r = (fs_ext_trim_t *) req->data; | |
| 174 | ||
| 175 | if (r->cb) r->cb(r, r->result); | |
| 176 | } | |
| 177 | ||
| 178 | int | |
| 179 | fs_ext_trim(uv_loop_t *loop, fs_ext_trim_t *req, uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_trim_cb cb) { | |
| 180 | req->fd = fd; | |
| 181 | req->offset = offset; | |
| 182 | req->length = length; | |
| 183 | req->cb = cb; | |
| 184 | req->req.data = (void *) req; | |
| 185 | ||
| 186 | return uv_queue_work(loop, &req->req, fs_ext__trim_work, fs_ext__trim_after_work); | |
| 187 | } | |
| 188 | ||
| 189 | static void | |
| 190 | fs_ext__sparse_work(uv_work_t *req) { | |
| 191 | fs_ext_sparse_t *r = (fs_ext_sparse_t *) req->data; | |
| 192 | ||
| 193 | r->result = fs_ext__sparse(r->fd); | |
| 194 | } | |
| 195 | ||
| 196 | static void | |
| 197 | fs_ext__sparse_after_work(uv_work_t *req, int status) { | |
| 198 | fs_ext_sparse_t *r = (fs_ext_sparse_t *) req->data; | |
| 199 | ||
| 200 | if (r->cb) r->cb(r, r->result); | |
| 201 | } | |
| 202 | ||
| 203 | int | |
| 204 | fs_ext_sparse(uv_loop_t *loop, fs_ext_sparse_t *req, uv_os_fd_t fd, fs_ext_sparse_cb cb) { | |
| 205 | req->fd = fd; | |
| 206 | req->cb = cb; | |
| 207 | req->req.data = (void *) req; | |
| 208 | ||
| 209 | return uv_queue_work(loop, &req->req, fs_ext__sparse_work, fs_ext__sparse_after_work); | |
| 210 | } | |
| 211 | ||
| 212 | static void | |
| 213 | fs_ext__swap_work(uv_work_t *req) { | |
| 214 | fs_ext_swap_t *r = (fs_ext_swap_t *) req->data; | |
| 215 | ||
| 216 | r->result = fs_ext__swap(r->from, r->to); | |
| 217 | } | |
| 218 | ||
| 219 | static void | |
| 220 | fs_ext__swap_after_work(uv_work_t *req, int status) { | |
| 221 | fs_ext_swap_t *r = (fs_ext_swap_t *) req->data; | |
| 222 | ||
| 223 | char *from = r->from; | |
| 224 | char *to = r->to; | |
| 225 | ||
| 226 | if (r->cb) r->cb(r, r->result); | |
| 227 | ||
| 228 | free(from); | |
| 229 | free(to); | |
| 230 | } | |
| 231 | ||
| 232 | int | |
| 233 | fs_ext_swap(uv_loop_t *loop, fs_ext_swap_t *req, const char *from, const char *to, fs_ext_swap_cb cb) { | |
| 234 | int err; | |
| 235 | ||
| 236 | req->from = strdup(from); | |
| 237 | req->to = strdup(to); | |
| 238 | req->cb = cb; | |
| 239 | req->req.data = (void *) req; | |
| 240 | ||
| 241 | err = uv_queue_work(loop, &req->req, fs_ext__swap_work, fs_ext__swap_after_work); | |
| 242 | ||
| 243 | if (err < 0) { | |
| 244 | free(req->from); | |
| 245 | free(req->to); | |
| 246 | ||
| 247 | return err; | |
| 248 | } | |
| 249 | ||
| 250 | return 0; | |
| 251 | } | |
| 252 | ||
| 253 | static void | |
| 254 | fs_ext__get_attr_work(uv_work_t *req) { | |
| 255 | fs_ext_get_attr_t *r = (fs_ext_get_attr_t *) req->data; | |
| 256 | ||
| 257 | r->result = fs_ext__get_attr(r->fd, r->name, &r->value); | |
| 258 | } | |
| 259 | ||
| 260 | static void | |
| 261 | fs_ext__get_attr_after_work(uv_work_t *req, int status) { | |
| 262 | fs_ext_get_attr_t *r = (fs_ext_get_attr_t *) req->data; | |
| 263 | ||
| 264 | uv_buf_t value = r->value; | |
| 265 | char *name = r->name; | |
| 266 | ||
| 267 | if (r->cb) r->cb(r, r->result, &value); | |
| 268 | ||
| 269 | free(value.base); | |
| 270 | free(name); | |
| 271 | } | |
| 272 | ||
| 273 | int | |
| 274 | fs_ext_get_attr(uv_loop_t *loop, fs_ext_get_attr_t *req, uv_os_fd_t fd, const char *name, fs_ext_get_attr_cb cb) { | |
| 275 | int err; | |
| 276 | ||
| 277 | req->fd = fd; | |
| 278 | req->name = strdup(name); | |
| 279 | req->value.base = NULL; | |
| 280 | req->value.len = 0; | |
| 281 | req->cb = cb; | |
| 282 | req->req.data = (void *) req; | |
| 283 | ||
| 284 | err = uv_queue_work(loop, &req->req, fs_ext__get_attr_work, fs_ext__get_attr_after_work); | |
| 285 | ||
| 286 | if (err < 0) { | |
| 287 | free(req->name); | |
| 288 | ||
| 289 | return err; | |
| 290 | } | |
| 291 | ||
| 292 | return 0; | |
| 293 | } | |
| 294 | ||
| 295 | static void | |
| 296 | fs_ext__set_attr_work(uv_work_t *req) { | |
| 297 | fs_ext_set_attr_t *r = (fs_ext_set_attr_t *) req->data; | |
| 298 | ||
| 299 | r->result = fs_ext__set_attr(r->fd, r->name, &r->value); | |
| 300 | } | |
| 301 | ||
| 302 | static void | |
| 303 | fs_ext__set_attr_after_work(uv_work_t *req, int status) { | |
| 304 | fs_ext_set_attr_t *r = (fs_ext_set_attr_t *) req->data; | |
| 305 | ||
| 306 | char *name = r->name; | |
| 307 | ||
| 308 | if (r->cb) r->cb(r, r->result); | |
| 309 | ||
| 310 | free(name); | |
| 311 | } | |
| 312 | ||
| 313 | int | |
| 314 | fs_ext_set_attr(uv_loop_t *loop, fs_ext_set_attr_t *req, uv_os_fd_t fd, const char *name, const uv_buf_t *value, fs_ext_set_attr_cb cb) { | |
| 315 | int err; | |
| 316 | ||
| 317 | req->fd = fd; | |
| 318 | req->name = strdup(name); | |
| 319 | req->value = *value; | |
| 320 | req->cb = cb; | |
| 321 | req->req.data = (void *) req; | |
| 322 | ||
| 323 | err = uv_queue_work(loop, &req->req, fs_ext__set_attr_work, fs_ext__set_attr_after_work); | |
| 324 | ||
| 325 | if (err < 0) { | |
| 326 | free(req->name); | |
| 327 | ||
| 328 | return err; | |
| 329 | } | |
| 330 | ||
| 331 | return 0; | |
| 332 | } | |
| 333 | ||
| 334 | static void | |
| 335 | fs_ext__remove_attr_work(uv_work_t *req) { | |
| 336 | fs_ext_remove_attr_t *r = (fs_ext_remove_attr_t *) req->data; | |
| 337 | ||
| 338 | r->result = fs_ext__remove_attr(r->fd, r->name); | |
| 339 | } | |
| 340 | ||
| 341 | static void | |
| 342 | fs_ext__remove_attr_after_work(uv_work_t *req, int status) { | |
| 343 | fs_ext_remove_attr_t *r = (fs_ext_remove_attr_t *) req->data; | |
| 344 | ||
| 345 | char *name = r->name; | |
| 346 | ||
| 347 | if (r->cb) r->cb(r, r->result); | |
| 348 | ||
| 349 | free(name); | |
| 350 | } | |
| 351 | ||
| 352 | int | |
| 353 | fs_ext_remove_attr(uv_loop_t *loop, fs_ext_remove_attr_t *req, uv_os_fd_t fd, const char *name, fs_ext_remove_attr_cb cb) { | |
| 354 | int err; | |
| 355 | ||
| 356 | req->fd = fd; | |
| 357 | req->name = strdup(name); | |
| 358 | req->cb = cb; | |
| 359 | req->req.data = (void *) req; | |
| 360 | ||
| 361 | err = uv_queue_work(loop, &req->req, fs_ext__remove_attr_work, fs_ext__remove_attr_after_work); | |
| 362 | ||
| 363 | if (err < 0) { | |
| 364 | free(req->name); | |
| 365 | ||
| 366 | return err; | |
| 367 | } | |
| 368 | ||
| 369 | return 0; | |
| 370 | } | |
| 371 | ||
| 372 | static void | |
| 373 | fs_ext__list_attrs_work(uv_work_t *req) { | |
| 374 | fs_ext_list_attrs_t *r = (fs_ext_list_attrs_t *) req->data; | |
| 375 | ||
| 376 | r->result = fs_ext__list_attrs(r->fd, &r->names, &r->length); | |
| 377 | } | |
| 378 | ||
| 379 | static void | |
| 380 | fs_ext__list_attrs_after_work(uv_work_t *req, int status) { | |
| 381 | fs_ext_list_attrs_t *r = (fs_ext_list_attrs_t *) req->data; | |
| 382 | ||
| 383 | const char **names = (const char **) r->names; | |
| 384 | ||
| 385 | if (r->cb) r->cb(r, r->result, names, r->length); | |
| 386 | ||
| 387 | free(names); | |
| 388 | } | |
| 389 | ||
| 390 | int | |
| 391 | fs_ext_list_attrs(uv_loop_t *loop, fs_ext_list_attrs_t *req, uv_os_fd_t fd, fs_ext_list_attrs_cb cb) { | |
| 392 | req->fd = fd; | |
| 393 | req->cb = cb; | |
| 394 | req->req.data = (void *) req; | |
| 395 | ||
| 396 | return uv_queue_work(loop, &req->req, fs_ext__list_attrs_work, fs_ext__list_attrs_after_work); | |
| 397 | } |
| … | … | @@ -0,0 +1,490 @@ |
| 1 | #include <io.h> | |
| 2 | #include <stdbool.h> | |
| 3 | #include <stdint.h> | |
| 4 | #include <string.h> | |
| 5 | #include <strsafe.h> | |
| 6 | #include <uv.h> | |
| 7 | ||
| 8 | #include "../include/fs-ext.h" | |
| 9 | ||
| 10 | #include "platform.h" | |
| 11 | #include "win32/nt.h" | |
| 12 | ||
| 13 | int | |
| 14 | fs_ext__try_lock(uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_type_t type) { | |
| 15 | if (length == 0) length = SIZE_MAX; | |
| 16 | ||
| 17 | DWORD flags = LOCKFILE_FAIL_IMMEDIATELY; | |
| 18 | ||
| 19 | if (type == FS_EXT_WRLOCK) flags |= LOCKFILE_EXCLUSIVE_LOCK; | |
| 20 | ||
| 21 | OVERLAPPED data = { | |
| 22 | .hEvent = 0, | |
| 23 | .Offset = offset, | |
| 24 | .OffsetHigh = offset >> 32, | |
| 25 | }; | |
| 26 | ||
| 27 | BOOL res = LockFileEx( | |
| 28 | fd, | |
| 29 | flags, | |
| 30 | 0, | |
| 31 | length, | |
| 32 | length >> 32, | |
| 33 | &data | |
| 34 | ); | |
| 35 | ||
| 36 | return res ? 0 : uv_translate_sys_error(GetLastError()); | |
| 37 | } | |
| 38 | ||
| 39 | int | |
| 40 | fs_ext__wait_for_lock(uv_os_fd_t fd, uint64_t offset, size_t length, fs_ext_lock_type_t type) { | |
| 41 | if (length == 0) length = SIZE_MAX; | |
| 42 | ||
| 43 | DWORD flags = 0; | |
| 44 | ||
| 45 | if (type == FS_EXT_WRLOCK) flags |= LOCKFILE_EXCLUSIVE_LOCK; | |
| 46 | ||
| 47 | OVERLAPPED data = { | |
| 48 | .hEvent = 0, | |
| 49 | .Offset = offset, | |
| 50 | .OffsetHigh = offset >> 32, | |
| 51 | }; | |
| 52 | ||
| 53 | BOOL res = LockFileEx( | |
| 54 | fd, | |
| 55 | flags, | |
| 56 | 0, | |
| 57 | length, | |
| 58 | length >> 32, | |
| 59 | &data | |
| 60 | ); | |
| 61 | ||
| 62 | return res ? 0 : uv_translate_sys_error(GetLastError()); | |
| 63 | } | |
| 64 | ||
| 65 | int | |
| 66 | fs_ext__try_downgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 67 | int res = fs_ext__try_lock(fd, offset, length, FS_EXT_RDLOCK); | |
| 68 | if (res < 0) return res; | |
| 69 | ||
| 70 | return fs_ext__unlock(fd, offset, length); | |
| 71 | } | |
| 72 | ||
| 73 | int | |
| 74 | fs_ext__wait_for_downgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 75 | int res = fs_ext__wait_for_lock(fd, offset, length, FS_EXT_RDLOCK); | |
| 76 | if (res < 0) return res; | |
| 77 | ||
| 78 | return fs_ext__unlock(fd, offset, length); | |
| 79 | } | |
| 80 | ||
| 81 | int | |
| 82 | fs_ext__try_upgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 83 | int res = fs_ext__unlock(fd, offset, length); | |
| 84 | if (res < 0) return res; | |
| 85 | ||
| 86 | return fs_ext__try_lock(fd, offset, length, FS_EXT_WRLOCK); | |
| 87 | } | |
| 88 | ||
| 89 | int | |
| 90 | fs_ext__wait_for_upgrade_lock(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 91 | int res = fs_ext__unlock(fd, offset, length); | |
| 92 | if (res < 0) return res; | |
| 93 | ||
| 94 | return fs_ext__wait_for_lock(fd, offset, length, FS_EXT_WRLOCK); | |
| 95 | } | |
| 96 | ||
| 97 | int | |
| 98 | fs_ext__unlock(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 99 | if (length == 0) length = SIZE_MAX; | |
| 100 | ||
| 101 | OVERLAPPED data = { | |
| 102 | .hEvent = 0, | |
| 103 | .Offset = offset, | |
| 104 | .OffsetHigh = offset >> 32, | |
| 105 | }; | |
| 106 | ||
| 107 | BOOL res = UnlockFileEx( | |
| 108 | fd, | |
| 109 | 0, | |
| 110 | length, | |
| 111 | length >> 32, | |
| 112 | &data | |
| 113 | ); | |
| 114 | ||
| 115 | return res ? 0 : uv_translate_sys_error(GetLastError()); | |
| 116 | } | |
| 117 | ||
| 118 | int | |
| 119 | fs_ext__trim(uv_os_fd_t fd, uint64_t offset, size_t length) { | |
| 120 | FILE_ZERO_DATA_INFORMATION data = { | |
| 121 | .FileOffset = { | |
| 122 | .QuadPart = offset, | |
| 123 | }, | |
| 124 | .BeyondFinalZero = { | |
| 125 | .QuadPart = offset + length, | |
| 126 | }, | |
| 127 | }; | |
| 128 | ||
| 129 | DWORD bytes; | |
| 130 | ||
| 131 | BOOL res = DeviceIoControl( | |
| 132 | fd, | |
| 133 | FSCTL_SET_ZERO_DATA, | |
| 134 | &data, | |
| 135 | sizeof(data), | |
| 136 | NULL, | |
| 137 | 0, | |
| 138 | &bytes, // Must be passed when lpOverlapped is NULL | |
| 139 | NULL | |
| 140 | ); | |
| 141 | ||
| 142 | return res ? 0 : uv_translate_sys_error(GetLastError()); | |
| 143 | } | |
| 144 | ||
| 145 | int | |
| 146 | fs_ext__sparse(uv_os_fd_t fd) { | |
| 147 | DWORD bytes; | |
| 148 | ||
| 149 | BOOL res = DeviceIoControl( | |
| 150 | fd, | |
| 151 | FSCTL_SET_SPARSE, | |
| 152 | NULL, | |
| 153 | 0, | |
| 154 | NULL, | |
| 155 | 0, | |
| 156 | &bytes, // Must be passed when lpOverlapped is NULL | |
| 157 | NULL | |
| 158 | ); | |
| 159 | ||
| 160 | return res ? 0 : uv_translate_sys_error(GetLastError()); | |
| 161 | } | |
| 162 | ||
| 163 | static int | |
| 164 | fs_ext__temp_path(char *path) { | |
| 165 | TCHAR temp_path[MAX_PATH]; | |
| 166 | ||
| 167 | DWORD bytes = GetTempPath(MAX_PATH, temp_path); | |
| 168 | ||
| 169 | if (bytes == 0) return uv_translate_sys_error(GetLastError()); | |
| 170 | ||
| 171 | bytes = GetTempFileName(temp_path, NULL, 0, path); | |
| 172 | ||
| 173 | return bytes > 0 ? 0 : uv_translate_sys_error(GetLastError()); | |
| 174 | } | |
| 175 | ||
| 176 | static int | |
| 177 | fs_ext__move(const char *from, const char *to) { | |
| 178 | BOOL res = MoveFileEx( | |
| 179 | from, | |
| 180 | to, | |
| 181 | MOVEFILE_REPLACE_EXISTING | |
| 182 | ); | |
| 183 | ||
| 184 | return res ? 0 : uv_translate_sys_error(GetLastError()); | |
| 185 | } | |
| 186 | ||
| 187 | int | |
| 188 | fs_ext__swap(const char *from, const char *to) { | |
| 189 | TCHAR swap[MAX_PATH]; | |
| 190 | ||
| 191 | int err = fs_ext__temp_path(swap); | |
| 192 | if (err < 0) return err; | |
| 193 | ||
| 194 | err = fs_ext__move(to, from); | |
| 195 | if (err < 0) return err; | |
| 196 | ||
| 197 | err = fs_ext__move(from, to); | |
| 198 | if (err < 0) return err; | |
| 199 | ||
| 200 | return fs_ext__move(swap, from); | |
| 201 | } | |
| 202 | ||
| 203 | int | |
| 204 | fs_ext__get_attr(uv_os_fd_t fd, const char *name, uv_buf_t *value) { | |
| 205 | HANDLE handle = fd; | |
| 206 | HANDLE stream_handle; | |
| 207 | ||
| 208 | size_t len = strlen(name); | |
| 209 | ||
| 210 | WCHAR unicode_name[MAX_PATH] = L":"; | |
| 211 | MultiByteToWideChar(CP_OEMCP, 0, name, -1, &unicode_name[1], len + 1); | |
| 212 | ||
| 213 | len = wcslen(unicode_name) * 2; | |
| 214 | ||
| 215 | UNICODE_STRING object_name = { | |
| 216 | .Length = len, | |
| 217 | .MaximumLength = len + 2, | |
| 218 | .Buffer = unicode_name, | |
| 219 | }; | |
| 220 | ||
| 221 | OBJECT_ATTRIBUTES object_attributes = { | |
| 222 | .Length = sizeof(object_attributes), | |
| 223 | .RootDirectory = handle, | |
| 224 | .ObjectName = &object_name, | |
| 225 | }; | |
| 226 | ||
| 227 | IO_STATUS_BLOCK status; | |
| 228 | ||
| 229 | NTSTATUS res = NtOpenFile( | |
| 230 | &stream_handle, | |
| 231 | GENERIC_READ, | |
| 232 | &object_attributes, | |
| 233 | &status, | |
| 234 | FILE_SHARE_READ, | |
| 235 | 0 | |
| 236 | ); | |
| 237 | ||
| 238 | if (res < 0) { | |
| 239 | if (res == STATUS_OBJECT_NAME_NOT_FOUND) return UV_ENODATA; | |
| 240 | ||
| 241 | return UV_EIO; | |
| 242 | } | |
| 243 | ||
| 244 | FILE_STANDARD_INFORMATION info; | |
| 245 | ||
| 246 | res = NtQueryInformationFile( | |
| 247 | stream_handle, | |
| 248 | &status, | |
| 249 | &info, | |
| 250 | sizeof(info), | |
| 251 | FileStandardInformation | |
| 252 | ); | |
| 253 | ||
| 254 | if (res < 0) { | |
| 255 | NtClose(stream_handle); | |
| 256 | ||
| 257 | return UV_EIO; | |
| 258 | } | |
| 259 | ||
| 260 | size_t length = info.EndOfFile.QuadPart; | |
| 261 | ||
| 262 | *value = uv_buf_init(malloc(length), length); | |
| 263 | ||
| 264 | LARGE_INTEGER offset = { | |
| 265 | .QuadPart = 0, | |
| 266 | }; | |
| 267 | ||
| 268 | res = NtReadFile( | |
| 269 | stream_handle, | |
| 270 | NULL, | |
| 271 | NULL, | |
| 272 | NULL, | |
| 273 | &status, | |
| 274 | value->base, | |
| 275 | value->len, | |
| 276 | &offset, | |
| 277 | NULL | |
| 278 | ); | |
| 279 | ||
| 280 | NtClose(stream_handle); | |
| 281 | ||
| 282 | if (res < 0) return UV_EIO; | |
| 283 | ||
| 284 | return 0; | |
| 285 | } | |
| 286 | ||
| 287 | int | |
| 288 | fs_ext__set_attr(uv_os_fd_t fd, const char *name, const uv_buf_t *value) { | |
| 289 | HANDLE handle = fd; | |
| 290 | HANDLE stream_handle; | |
| 291 | ||
| 292 | size_t len = strlen(name); | |
| 293 | ||
| 294 | WCHAR unicode_name[MAX_PATH] = L":"; | |
| 295 | MultiByteToWideChar(CP_OEMCP, 0, name, -1, &unicode_name[1], len + 1); | |
| 296 | ||
| 297 | len = wcslen(unicode_name) * 2; | |
| 298 | ||
| 299 | UNICODE_STRING object_name = { | |
| 300 | .Length = len, | |
| 301 | .MaximumLength = len + 2, | |
| 302 | .Buffer = unicode_name, | |
| 303 | }; | |
| 304 | ||
| 305 | OBJECT_ATTRIBUTES object_attributes = { | |
| 306 | .Length = sizeof(object_attributes), | |
| 307 | .RootDirectory = handle, | |
| 308 | .ObjectName = &object_name, | |
| 309 | }; | |
| 310 | ||
| 311 | IO_STATUS_BLOCK status; | |
| 312 | ||
| 313 | NTSTATUS res = NtCreateFile( | |
| 314 | &stream_handle, | |
| 315 | GENERIC_WRITE, | |
| 316 | &object_attributes, | |
| 317 | &status, | |
| 318 | NULL, | |
| 319 | FILE_ATTRIBUTE_NORMAL, | |
| 320 | 0, | |
| 321 | FILE_OVERWRITE_IF, | |
| 322 | 0, | |
| 323 | NULL, | |
| 324 | 0 | |
| 325 | ); | |
| 326 | ||
| 327 | if (res < 0) return UV_EIO; | |
| 328 | ||
| 329 | LARGE_INTEGER offset = { | |
| 330 | .QuadPart = 0, | |
| 331 | }; | |
| 332 | ||
| 333 | res = NtWriteFile( | |
| 334 | stream_handle, | |
| 335 | NULL, | |
| 336 | NULL, | |
| 337 | NULL, | |
| 338 | &status, | |
| 339 | value->base, | |
| 340 | value->len, | |
| 341 | &offset, | |
| 342 | NULL | |
| 343 | ); | |
| 344 | ||
| 345 | NtClose(stream_handle); | |
| 346 | ||
| 347 | if (res < 0) return UV_EIO; | |
| 348 | ||
| 349 | return 0; | |
| 350 | } | |
| 351 | ||
| 352 | int | |
| 353 | fs_ext__remove_attr(uv_os_fd_t fd, const char *name) { | |
| 354 | HANDLE handle = fd; | |
| 355 | HANDLE stream_handle; | |
| 356 | ||
| 357 | size_t len = strlen(name); | |
| 358 | ||
| 359 | WCHAR unicode_name[MAX_PATH] = L":"; | |
| 360 | MultiByteToWideChar(CP_OEMCP, 0, name, -1, &unicode_name[1], len + 1); | |
| 361 | ||
| 362 | len = wcslen(unicode_name) * 2; | |
| 363 | ||
| 364 | UNICODE_STRING object_name = { | |
| 365 | .Length = len, | |
| 366 | .MaximumLength = len + 2, | |
| 367 | .Buffer = unicode_name, | |
| 368 | }; | |
| 369 | ||
| 370 | OBJECT_ATTRIBUTES object_attributes = { | |
| 371 | .Length = sizeof(object_attributes), | |
| 372 | .RootDirectory = handle, | |
| 373 | .ObjectName = &object_name, | |
| 374 | }; | |
| 375 | ||
| 376 | IO_STATUS_BLOCK status; | |
| 377 | ||
| 378 | NTSTATUS res = NtOpenFile( | |
| 379 | &stream_handle, | |
| 380 | DELETE, | |
| 381 | &object_attributes, | |
| 382 | &status, | |
| 383 | FILE_SHARE_DELETE, | |
| 384 | 0 | |
| 385 | ); | |
| 386 | ||
| 387 | if (res < 0) { | |
| 388 | if (res == STATUS_OBJECT_NAME_NOT_FOUND) return UV_ENODATA; | |
| 389 | ||
| 390 | return UV_EIO; | |
| 391 | } | |
| 392 | ||
| 393 | res = NtDeleteFile(&object_attributes); | |
| 394 | ||
| 395 | NtClose(stream_handle); | |
| 396 | ||
| 397 | if (res < 0) return UV_EIO; | |
| 398 | ||
| 399 | return 0; | |
| 400 | } | |
| 401 | ||
| 402 | int | |
| 403 | fs_ext__list_attrs(uv_os_fd_t fd, char **names, size_t *length) { | |
| 404 | HANDLE handle = fd; | |
| 405 | ||
| 406 | size_t info_size = 64; | |
| 407 | ||
| 408 | PFILE_STREAM_INFORMATION info = malloc(info_size); | |
| 409 | memset(info, 0, info_size); | |
| 410 | ||
| 411 | NTSTATUS res; | |
| 412 | ||
| 413 | do { | |
| 414 | IO_STATUS_BLOCK status; | |
| 415 | ||
| 416 | res = NtQueryInformationFile( | |
| 417 | handle, | |
| 418 | &status, | |
| 419 | &info[0], | |
| 420 | info_size, | |
| 421 | FileStreamInformation | |
| 422 | ); | |
| 423 | ||
| 424 | if (res == STATUS_BUFFER_OVERFLOW) { | |
| 425 | info = realloc(info, info_size *= 2); | |
| 426 | memset(info, 0, info_size); | |
| 427 | } else { | |
| 428 | break; | |
| 429 | } | |
| 430 | } while (true); | |
| 431 | ||
| 432 | if (res < 0) return UV_EIO; | |
| 433 | ||
| 434 | PFILE_STREAM_INFORMATION next = info; | |
| 435 | ||
| 436 | size_t names_len = 0; | |
| 437 | *length = 0; | |
| 438 | ||
| 439 | do { | |
| 440 | PWCHAR unicode_name = &next->StreamName[1]; | |
| 441 | ||
| 442 | unicode_name[wcslen(unicode_name) - wcslen(L":$DATA")] = L'\0'; | |
| 443 | ||
| 444 | if (wcscmp(unicode_name, L"") != 0) { | |
| 445 | size_t name_len = WideCharToMultiByte(CP_UTF8, 0, unicode_name, -1, NULL, 0, NULL, NULL); | |
| 446 | ||
| 447 | *length += 1; | |
| 448 | ||
| 449 | names_len += sizeof(char *) + name_len; | |
| 450 | } | |
| 451 | ||
| 452 | if (next->NextEntryOffset) { | |
| 453 | next = (PFILE_STREAM_INFORMATION) (((uintptr_t) next) + next->NextEntryOffset); | |
| 454 | } else { | |
| 455 | break; | |
| 456 | } | |
| 457 | } while (true); | |
| 458 | ||
| 459 | *names = malloc(names_len); | |
| 460 | ||
| 461 | size_t offset = *length * sizeof(char *), i = 0, j = 0; | |
| 462 | ||
| 463 | next = info; | |
| 464 | ||
| 465 | do { | |
| 466 | PWCHAR unicode_name = &next->StreamName[1]; | |
| 467 | ||
| 468 | if (wcscmp(unicode_name, L"") != 0) { | |
| 469 | size_t name_len = WideCharToMultiByte(CP_UTF8, 0, unicode_name, -1, NULL, 0, NULL, NULL); | |
| 470 | ||
| 471 | char *name = *names + offset + i; | |
| 472 | i += name_len; | |
| 473 | ||
| 474 | WideCharToMultiByte(CP_UTF8, 0, unicode_name, -1, name, name_len, NULL, NULL); | |
| 475 | ||
| 476 | memcpy(*names + j, &name, sizeof(char *)); | |
| 477 | j += sizeof(char *); | |
| 478 | } | |
| 479 | ||
| 480 | if (next->NextEntryOffset) { | |
| 481 | next = (PFILE_STREAM_INFORMATION) (((uintptr_t) next) + next->NextEntryOffset); | |
| 482 | } else { | |
| 483 | break; | |
| 484 | } | |
| 485 | } while (true); | |
| 486 | ||
| 487 | free(info); | |
| 488 | ||
| 489 | return 0; | |
| 490 | } |
| … | … | @@ -0,0 +1,224 @@ |
| 1 | #ifndef FS_NT_H | |
| 2 | #define FS_NT_H | |
| 3 | ||
| 4 | // Definitions of needed Windows kernel APIs, mostly taken from winternl.h and | |
| 5 | // the Windows SDK documentation. | |
| 6 | // | |
| 7 | // Copyright (c) Microsoft Corp. All rights reserved. | |
| 8 | ||
| 9 | #include <windows.h> | |
| 10 | ||
| 11 | #include <ntstatus.h> | |
| 12 | ||
| 13 | #define FILE_SUPERSEDE 0x00000000 | |
| 14 | #define FILE_OPEN 0x00000001 | |
| 15 | #define FILE_CREATE 0x00000002 | |
| 16 | #define FILE_OPEN_IF 0x00000003 | |
| 17 | #define FILE_OVERWRITE 0x00000004 | |
| 18 | #define FILE_OVERWRITE_IF 0x00000005 | |
| 19 | #define FILE_MAXIMUM_DISPOSITION 0x00000005 | |
| 20 | ||
| 21 | #define FILE_SUPERSEDED 0x00000000 | |
| 22 | #define FILE_OPENED 0x00000001 | |
| 23 | #define FILE_CREATED 0x00000002 | |
| 24 | #define FILE_OVERWRITTEN 0x00000003 | |
| 25 | #define FILE_EXISTS 0x00000004 | |
| 26 | #define FILE_DOES_NOT_EXIST 0x00000005 | |
| 27 | ||
| 28 | typedef struct _UNICODE_STRING { | |
| 29 | USHORT Length; | |
| 30 | USHORT MaximumLength; | |
| 31 | PWSTR Buffer; | |
| 32 | } UNICODE_STRING, *PUNICODE_STRING; | |
| 33 | ||
| 34 | typedef struct _OBJECT_ATTRIBUTES { | |
| 35 | ULONG Length; | |
| 36 | HANDLE RootDirectory; | |
| 37 | PUNICODE_STRING ObjectName; | |
| 38 | ULONG Attributes; | |
| 39 | PVOID SecurityDescriptor; | |
| 40 | PVOID SecurityQualityOfService; | |
| 41 | } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; | |
| 42 | ||
| 43 | typedef struct _IO_STATUS_BLOCK { | |
| 44 | union { | |
| 45 | NTSTATUS Status; | |
| 46 | PVOID Pointer; | |
| 47 | }; | |
| 48 | ULONG_PTR Information; | |
| 49 | } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; | |
| 50 | ||
| 51 | typedef struct _FILE_STANDARD_INFORMATION { | |
| 52 | LARGE_INTEGER AllocationSize; | |
| 53 | LARGE_INTEGER EndOfFile; | |
| 54 | ULONG NumberOfLinks; | |
| 55 | BOOLEAN DeletePending; | |
| 56 | BOOLEAN Directory; | |
| 57 | } FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION; | |
| 58 | ||
| 59 | typedef struct _FILE_STREAM_INFORMATION { | |
| 60 | ULONG NextEntryOffset; | |
| 61 | ULONG StreamNameLength; | |
| 62 | LARGE_INTEGER StreamSize; | |
| 63 | LARGE_INTEGER StreamAllocationSize; | |
| 64 | WCHAR StreamName[1]; | |
| 65 | } FILE_STREAM_INFORMATION, *PFILE_STREAM_INFORMATION; | |
| 66 | ||
| 67 | typedef enum _FILE_INFORMATION_CLASS { | |
| 68 | FileDirectoryInformation = 1, | |
| 69 | FileFullDirectoryInformation, // 2 | |
| 70 | FileBothDirectoryInformation, // 3 | |
| 71 | FileBasicInformation, // 4 | |
| 72 | FileStandardInformation, // 5 | |
| 73 | FileInternalInformation, // 6 | |
| 74 | FileEaInformation, // 7 | |
| 75 | FileAccessInformation, // 8 | |
| 76 | FileNameInformation, // 9 | |
| 77 | FileRenameInformation, // 10 | |
| 78 | FileLinkInformation, // 11 | |
| 79 | FileNamesInformation, // 12 | |
| 80 | FileDispositionInformation, // 13 | |
| 81 | FilePositionInformation, // 14 | |
| 82 | FileFullEaInformation, // 15 | |
| 83 | FileModeInformation, // 16 | |
| 84 | FileAlignmentInformation, // 17 | |
| 85 | FileAllInformation, // 18 | |
| 86 | FileAllocationInformation, // 19 | |
| 87 | FileEndOfFileInformation, // 20 | |
| 88 | FileAlternateNameInformation, // 21 | |
| 89 | FileStreamInformation, // 22 | |
| 90 | FilePipeInformation, // 23 | |
| 91 | FilePipeLocalInformation, // 24 | |
| 92 | FilePipeRemoteInformation, // 25 | |
| 93 | FileMailslotQueryInformation, // 26 | |
| 94 | FileMailslotSetInformation, // 27 | |
| 95 | FileCompressionInformation, // 28 | |
| 96 | FileObjectIdInformation, // 29 | |
| 97 | FileCompletionInformation, // 30 | |
| 98 | FileMoveClusterInformation, // 31 | |
| 99 | FileQuotaInformation, // 32 | |
| 100 | FileReparsePointInformation, // 33 | |
| 101 | FileNetworkOpenInformation, // 34 | |
| 102 | FileAttributeTagInformation, // 35 | |
| 103 | FileTrackingInformation, // 36 | |
| 104 | FileIdBothDirectoryInformation, // 37 | |
| 105 | FileIdFullDirectoryInformation, // 38 | |
| 106 | FileValidDataLengthInformation, // 39 | |
| 107 | FileShortNameInformation, // 40 | |
| 108 | FileIoCompletionNotificationInformation, // 41 | |
| 109 | FileIoStatusBlockRangeInformation, // 42 | |
| 110 | FileIoPriorityHintInformation, // 43 | |
| 111 | FileSfioReserveInformation, // 44 | |
| 112 | FileSfioVolumeInformation, // 45 | |
| 113 | FileHardLinkInformation, // 46 | |
| 114 | FileProcessIdsUsingFileInformation, // 47 | |
| 115 | FileNormalizedNameInformation, // 48 | |
| 116 | FileNetworkPhysicalNameInformation, // 49 | |
| 117 | FileIdGlobalTxDirectoryInformation, // 50 | |
| 118 | FileIsRemoteDeviceInformation, // 51 | |
| 119 | FileUnusedInformation, // 52 | |
| 120 | FileNumaNodeInformation, // 53 | |
| 121 | FileStandardLinkInformation, // 54 | |
| 122 | FileRemoteProtocolInformation, // 55 | |
| 123 | FileRenameInformationBypassAccessCheck, // 56 | |
| 124 | FileLinkInformationBypassAccessCheck, // 57 | |
| 125 | FileVolumeNameInformation, // 58 | |
| 126 | FileIdInformation, // 59 | |
| 127 | FileIdExtdDirectoryInformation, // 60 | |
| 128 | FileReplaceCompletionInformation, // 61 | |
| 129 | FileHardLinkFullIdInformation, // 62 | |
| 130 | FileIdExtdBothDirectoryInformation, // 63 | |
| 131 | FileDispositionInformationEx, // 64 | |
| 132 | FileRenameInformationEx, // 65 | |
| 133 | FileRenameInformationExBypassAccessCheck, // 66 | |
| 134 | FileDesiredStorageClassInformation, // 67 | |
| 135 | FileStatInformation, // 68 | |
| 136 | FileMemoryPartitionInformation, // 69 | |
| 137 | FileStatLxInformation, // 70 | |
| 138 | FileCaseSensitiveInformation, // 71 | |
| 139 | FileLinkInformationEx, // 72 | |
| 140 | FileLinkInformationExBypassAccessCheck, // 73 | |
| 141 | FileStorageReserveIdInformation, // 74 | |
| 142 | FileCaseSensitiveInformationForceAccessCheck, // 75 | |
| 143 | FileKnownFolderInformation, // 76 | |
| 144 | FileMaximumInformation | |
| 145 | } FILE_INFORMATION_CLASS, | |
| 146 | *PFILE_INFORMATION_CLASS; | |
| 147 | ||
| 148 | typedef VOID(NTAPI *PIO_APC_ROUTINE)( | |
| 149 | IN PVOID ApcContext, | |
| 150 | IN PIO_STATUS_BLOCK IoStatusBlock, | |
| 151 | IN ULONG Reserved | |
| 152 | ); | |
| 153 | ||
| 154 | __kernel_entry NTSYSCALLAPI NTSTATUS NTAPI | |
| 155 | NtClose( | |
| 156 | IN HANDLE Handle | |
| 157 | ); | |
| 158 | ||
| 159 | __kernel_entry NTSYSCALLAPI NTSTATUS NTAPI | |
| 160 | NtCreateFile( | |
| 161 | OUT PHANDLE FileHandle, | |
| 162 | IN ACCESS_MASK DesiredAccess, | |
| 163 | IN POBJECT_ATTRIBUTES ObjectAttributes, | |
| 164 | OUT PIO_STATUS_BLOCK IoStatusBlock, | |
| 165 | IN PLARGE_INTEGER AllocationSize OPTIONAL, | |
| 166 | IN ULONG FileAttributes, | |
| 167 | IN ULONG ShareAccess, | |
| 168 | IN ULONG CreateDisposition, | |
| 169 | IN ULONG CreateOptions, | |
| 170 | IN PVOID EaBuffer OPTIONAL, | |
| 171 | IN ULONG EaLength | |
| 172 | ); | |
| 173 | ||
| 174 | __kernel_entry NTSYSCALLAPI NTSTATUS NTAPI | |
| 175 | NtOpenFile( | |
| 176 | OUT PHANDLE FileHandle, | |
| 177 | IN ACCESS_MASK DesiredAccess, | |
| 178 | IN POBJECT_ATTRIBUTES ObjectAttributes, | |
| 179 | OUT PIO_STATUS_BLOCK IoStatusBlock, | |
| 180 | IN ULONG ShareAccess, | |
| 181 | IN ULONG OpenOptions | |
| 182 | ); | |
| 183 | ||
| 184 | __kernel_entry NTSYSCALLAPI NTSTATUS NTAPI | |
| 185 | NtReadFile( | |
| 186 | IN HANDLE FileHandle, | |
| 187 | IN HANDLE Event, | |
| 188 | IN PIO_APC_ROUTINE ApcRoutine, | |
| 189 | IN PVOID ApcContext, | |
| 190 | OUT PIO_STATUS_BLOCK IoStatusBlock, | |
| 191 | OUT PVOID Buffer, | |
| 192 | IN ULONG Length, | |
| 193 | IN PLARGE_INTEGER ByteOffset, | |
| 194 | IN PULONG Key | |
| 195 | ); | |
| 196 | ||
| 197 | __kernel_entry NTSYSCALLAPI NTSTATUS NTAPI | |
| 198 | NtWriteFile( | |
| 199 | IN HANDLE FileHandle, | |
| 200 | IN HANDLE Event, | |
| 201 | IN PIO_APC_ROUTINE ApcRoutine, | |
| 202 | IN PVOID ApcContext, | |
| 203 | OUT PIO_STATUS_BLOCK IoStatusBlock, | |
| 204 | IN PVOID Buffer, | |
| 205 | IN ULONG Length, | |
| 206 | IN PLARGE_INTEGER ByteOffset, | |
| 207 | IN PULONG Key | |
| 208 | ); | |
| 209 | ||
| 210 | __kernel_entry NTSYSCALLAPI NTSTATUS NTAPI | |
| 211 | NtDeleteFile( | |
| 212 | IN POBJECT_ATTRIBUTES ObjectAttributes | |
| 213 | ); | |
| 214 | ||
| 215 | __kernel_entry NTSYSCALLAPI NTSTATUS NTAPI | |
| 216 | NtQueryInformationFile( | |
| 217 | IN HANDLE FileHandle, | |
| 218 | OUT PIO_STATUS_BLOCK IoStatusBlock, | |
| 219 | OUT PVOID FileInformation, | |
| 220 | IN ULONG Length, | |
| 221 | IN FILE_INFORMATION_CLASS FileInformationClass | |
| 222 | ); | |
| 223 | ||
| 224 | #endif // FS_NT_H |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2020 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,402 @@ |
| 1 | # Hyperbee 🐝 | |
| 2 | ||
| 3 | [See API docs at docs.pears.com](https://docs.pears.com/building-blocks/hyperbee) | |
| 4 | ||
| 5 | An append-only B-tree running on a Hypercore. Allows sorted iteration and more. | |
| 6 | ||
| 7 | ``` | |
| 8 | npm install hyperbee | |
| 9 | ``` | |
| 10 | ||
| 11 | ## Usage | |
| 12 | ||
| 13 | ```js | |
| 14 | const Hyperbee = require('hyperbee') | |
| 15 | const Hypercore = require('hypercore') | |
| 16 | ||
| 17 | const core = new Hypercore('./storage') | |
| 18 | const db = new Hyperbee(core, { keyEncoding: 'utf-8', valueEncoding: 'binary' }) | |
| 19 | ||
| 20 | // If you own the core | |
| 21 | await db.put('key1', 'value1') | |
| 22 | await db.put('key2', 'value2') | |
| 23 | await db.del('some-key') | |
| 24 | ||
| 25 | // If you want to insert/delete batched values | |
| 26 | const batch = db.batch() | |
| 27 | ||
| 28 | await batch.put('key', 'value') | |
| 29 | await batch.del('some-key') | |
| 30 | await batch.flush() // Execute the batch | |
| 31 | ||
| 32 | // Query the core | |
| 33 | const entry = await db.get('key') // => null or { key, value } | |
| 34 | ||
| 35 | // Read all entries | |
| 36 | for await (const entry of db.createReadStream()) { | |
| 37 | // .. | |
| 38 | } | |
| 39 | ||
| 40 | // Read a range | |
| 41 | for await (const entry of db.createReadStream({ gte: 'a', lt: 'd' })) { | |
| 42 | // Anything >=a and <d | |
| 43 | } | |
| 44 | ||
| 45 | // Get the last written entry | |
| 46 | for await (const entry of db.createHistoryStream({ reverse: true, limit: 1 })) { | |
| 47 | // .. | |
| 48 | } | |
| 49 | ``` | |
| 50 | ||
| 51 | It works with sparse cores, only a small subset of the full core is downloaded to satisfy your queries. | |
| 52 | ||
| 53 | ## API | |
| 54 | ||
| 55 | #### `const db = new Hyperbee(core, [options])` | |
| 56 | ||
| 57 | Make a new Hyperbee instance. `core` should be a [Hypercore](https://github.com/holepunchto/hypercore). | |
| 58 | ||
| 59 | `options` include: | |
| 60 | ||
| 61 | ```js | |
| 62 | { | |
| 63 | keyEncoding: 'binary', // "binary" (default), "utf-8", "ascii", "json", or an abstract-encoding | |
| 64 | valueEncoding: 'binary' // Same options as keyEncoding like "json", etc | |
| 65 | } | |
| 66 | ``` | |
| 67 | ||
| 68 | Note that currently read/diff streams sort based on the encoded value of the keys. | |
| 69 | ||
| 70 | #### `await db.ready()` | |
| 71 | ||
| 72 | Waits until internal state is loaded. | |
| 73 | ||
| 74 | Use it once before reading synchronous properties like `db.version`, unless you called any of the other APIs. | |
| 75 | ||
| 76 | #### `await db.close()` | |
| 77 | ||
| 78 | Fully close this bee, including its core. | |
| 79 | ||
| 80 | #### `db.core` | |
| 81 | ||
| 82 | The underlying Hypercore backing this bee. | |
| 83 | ||
| 84 | #### `db.version` | |
| 85 | ||
| 86 | Number that indicates how many modifications were made, useful as a version identifier. | |
| 87 | ||
| 88 | #### `db.id` | |
| 89 | ||
| 90 | String containing the id (z-base-32 of the public key) identifying this bee. | |
| 91 | ||
| 92 | #### `db.key` | |
| 93 | ||
| 94 | Buffer containing the public key identifying this bee. | |
| 95 | ||
| 96 | #### `db.discoveryKey` | |
| 97 | ||
| 98 | Buffer containing a key derived from `db.key`. | |
| 99 | ||
| 100 | This discovery key does not allow you to verify the data, it's only to announce or look for peers that are sharing the same bee, without leaking the bee key. | |
| 101 | ||
| 102 | #### `db.writable` | |
| 103 | ||
| 104 | Boolean indicating if we can put or delete data in this bee. | |
| 105 | ||
| 106 | #### `db.readable` | |
| 107 | ||
| 108 | Boolean indicating if we can read from this bee. After closing the bee this will be `false`. | |
| 109 | ||
| 110 | #### `await db.put(key, [value], [options])` | |
| 111 | ||
| 112 | Insert a new key. Value can be optional. | |
| 113 | ||
| 114 | If you're inserting a series of data atomically or want more performance then check the `db.batch` API. | |
| 115 | ||
| 116 | `options` includes: | |
| 117 | ||
| 118 | ```js | |
| 119 | { | |
| 120 | cas (prev, next) { return true } | |
| 121 | } | |
| 122 | ``` | |
| 123 | ||
| 124 | ##### Compare And Swap (cas) | |
| 125 | ||
| 126 | `cas` option is a function comparator to control whether the `put` succeeds. | |
| 127 | ||
| 128 | By returning `true` it will insert the value, otherwise it won't. | |
| 129 | ||
| 130 | It receives two args: `prev` is the current node entry, and `next` is the potential new node. | |
| 131 | ||
| 132 | ```js | |
| 133 | await db.put('number', '123', { cas }) | |
| 134 | console.log(await db.get('number')) // => { seq: 1, key: 'number', value: '123' } | |
| 135 | ||
| 136 | await db.put('number', '123', { cas }) | |
| 137 | console.log(await db.get('number')) // => { seq: 1, key: 'number', value: '123' } | |
| 138 | // Without cas this would have been { seq: 2, ... }, and the next { seq: 3 } | |
| 139 | ||
| 140 | await db.put('number', '456', { cas }) | |
| 141 | console.log(await db.get('number')) // => { seq: 2, key: 'number', value: '456' } | |
| 142 | ||
| 143 | function cas(prev, next) { | |
| 144 | // You can use same-data or same-object lib, depending on the value complexity | |
| 145 | return prev.value !== next.value | |
| 146 | } | |
| 147 | ``` | |
| 148 | ||
| 149 | #### `const { seq, key, value } = await db.get(key)` | |
| 150 | ||
| 151 | Get a key's value. Returns `null` if key doesn't exists. | |
| 152 | ||
| 153 | `seq` is the Hypercore index at which this key was inserted. | |
| 154 | ||
| 155 | #### `await db.del(key, [options])` | |
| 156 | ||
| 157 | Delete a key. | |
| 158 | ||
| 159 | `options` include: | |
| 160 | ||
| 161 | ```js | |
| 162 | { | |
| 163 | cas (prev, next) { return true } | |
| 164 | } | |
| 165 | ``` | |
| 166 | ||
| 167 | ##### Compare And Swap (cas) | |
| 168 | ||
| 169 | `cas` option is a function comparator to control whether the `del` succeeds. | |
| 170 | ||
| 171 | By returning `true` it will delete the value, otherwise it won't. | |
| 172 | ||
| 173 | It only receives one arg: `prev` which is the current node entry. | |
| 174 | ||
| 175 | ```js | |
| 176 | // This won't get deleted | |
| 177 | await db.del('number', { cas }) | |
| 178 | console.log(await db.get('number')) // => { seq: 1, key: 'number', value: 'value' } | |
| 179 | ||
| 180 | // Change the value so the next time we try to delete it then "cas" will return true | |
| 181 | await db.put('number', 'can-be-deleted') | |
| 182 | ||
| 183 | await db.del('number', { cas }) | |
| 184 | console.log(await db.get('number')) // => null | |
| 185 | ||
| 186 | function cas(prev) { | |
| 187 | return prev.value === 'can-be-deleted' | |
| 188 | } | |
| 189 | ``` | |
| 190 | ||
| 191 | #### `const { key, value } = await db.getBySeq(seq, [options])` | |
| 192 | ||
| 193 | Get the key and value from a block number. | |
| 194 | ||
| 195 | `seq` is the Hypercore index. Returns `null` if block doesn't exists. | |
| 196 | ||
| 197 | #### `const stream = db.replicate(isInitiatorOrStream)` | |
| 198 | ||
| 199 | See more about how replicate works at [core.replicate][core-replicate-docs]. | |
| 200 | ||
| 201 | #### `const batch = db.batch()` | |
| 202 | ||
| 203 | Make a new atomic batch that is either fully processed or not processed at all. | |
| 204 | ||
| 205 | If you have several inserts and deletions then a batch can be much faster. | |
| 206 | ||
| 207 | #### `await batch.put(key, [value], [options])` | |
| 208 | ||
| 209 | Insert a key into a batch. | |
| 210 | ||
| 211 | `options` are the same as `db.put` method. | |
| 212 | ||
| 213 | #### `const { seq, key, value } = await batch.get(key)` | |
| 214 | ||
| 215 | Get a key, value out of a batch. | |
| 216 | ||
| 217 | #### `await batch.del(key, [options])` | |
| 218 | ||
| 219 | Delete a key into the batch. | |
| 220 | ||
| 221 | `options` are the same as `db.del` method. | |
| 222 | ||
| 223 | #### `await batch.flush()` | |
| 224 | ||
| 225 | Commit the batch to the database, and releases any locks it has acquired. | |
| 226 | ||
| 227 | #### `await batch.close()` | |
| 228 | ||
| 229 | Destroy a batch, and releases any locks it has acquired on the db. | |
| 230 | ||
| 231 | Call this if you want to abort a batch without flushing it. | |
| 232 | ||
| 233 | #### `const stream = db.createReadStream([range], [options])` | |
| 234 | ||
| 235 | Make a read stream. Sort order is based on the binary value of the keys. | |
| 236 | ||
| 237 | All entries in the stream are similar to the ones returned from `db.get`. | |
| 238 | ||
| 239 | `range` should specify the range you want to read and looks like this: | |
| 240 | ||
| 241 | ```js | |
| 242 | { | |
| 243 | gt: 'only return keys > than this', | |
| 244 | gte: 'only return keys >= than this', | |
| 245 | lt: 'only return keys < than this', | |
| 246 | lte: 'only return keys <= than this' | |
| 247 | } | |
| 248 | ``` | |
| 249 | ||
| 250 | `options` include: | |
| 251 | ||
| 252 | ```js | |
| 253 | { | |
| 254 | reverse: false // Set to true to get them in reverse order, | |
| 255 | limit: -1 // Set to the max number of entries you want | |
| 256 | } | |
| 257 | ``` | |
| 258 | ||
| 259 | #### `const { seq, key, value } = await db.peek([range], [options])` | |
| 260 | ||
| 261 | Similar to doing a read stream and returning the first value, but a bit faster than that. | |
| 262 | ||
| 263 | #### `const stream = db.createHistoryStream([options])` | |
| 264 | ||
| 265 | Create a stream of all entries ever inserted or deleted from the db. | |
| 266 | ||
| 267 | Each entry has an additional `type` property indicating if it was a `put` or `del` operation. | |
| 268 | ||
| 269 | `options` include: | |
| 270 | ||
| 271 | ```js | |
| 272 | { | |
| 273 | live: false, // If true the stream will wait for new data and never end | |
| 274 | reverse: false, // If true get from the newest to the oldest | |
| 275 | gte: seq, // Start with this seq (inclusive) | |
| 276 | gt: seq, // Start after this index | |
| 277 | lte: seq, // Stop after this index | |
| 278 | lt: seq, // Stop before this index | |
| 279 | limit: -1 // Set to the max number of entries you want | |
| 280 | } | |
| 281 | ``` | |
| 282 | ||
| 283 | If any of the `gte`, `gt`, `lte`, `lt` arguments are `< 0` then | |
| 284 | they'll implicitly be added with the version before starting so | |
| 285 | doing `{ gte: -1 }` makes a stream starting at the last index. | |
| 286 | ||
| 287 | #### `const stream = db.createDiffStream(otherVersion, [options])` | |
| 288 | ||
| 289 | Efficiently create a stream of the shallow changes between two versions of the db. | |
| 290 | ||
| 291 | `options` are the same as `db.createReadStream`, except for `reverse`. | |
| 292 | ||
| 293 | Each entry is sorted by key and looks like this: | |
| 294 | ||
| 295 | ```js | |
| 296 | { | |
| 297 | left: Object, // The entry in the `db` | |
| 298 | right: Object // The entry in `otherVersion` | |
| 299 | } | |
| 300 | ``` | |
| 301 | ||
| 302 | If an entry exists in db but not in the other version, then `left` is set | |
| 303 | and `right` will be null, and vice versa. | |
| 304 | ||
| 305 | If the entries are causally equal (i.e. the have the same seq), they are not | |
| 306 | returned, only the diff. | |
| 307 | ||
| 308 | #### `const entryWatcher = await db.getAndWatch(key, [options])` | |
| 309 | ||
| 310 | Returns a watcher which listens to changes on the given key. | |
| 311 | ||
| 312 | `entryWatcher.node` contains the current entry in the same format as the result of `bee.get(key)`, and will be updated as it changes. | |
| 313 | ||
| 314 | By default, the node will have the bee's key- and value encoding, but you can overwrite it by setting the `keyEncoding` and `valueEncoding` options. | |
| 315 | ||
| 316 | You can listen to `entryWatcher.on('update')` to be notified when the value of node has changed. | |
| 317 | ||
| 318 | Call `await watcher.close()` to stop the watcher. | |
| 319 | ||
| 320 | #### `const watcher = db.watch([range])` | |
| 321 | ||
| 322 | Listens to changes that are on the optional `range`. | |
| 323 | ||
| 324 | `range` options are the same as `db.createReadStream` except for `reverse`. | |
| 325 | ||
| 326 | By default, the yielded snapshots will have the bee's key- and value encoding, but you can overwrite them by setting the `keyEncoding` and `valueEncoding` options. | |
| 327 | ||
| 328 | Usage example: | |
| 329 | ||
| 330 | ```js | |
| 331 | for await (const [current, previous] of watcher) { | |
| 332 | console.log(current.version) | |
| 333 | console.log(previous.version) | |
| 334 | } | |
| 335 | ``` | |
| 336 | ||
| 337 | Returns a new value after a change, `current` and `previous` are snapshots that are auto-closed before next value. | |
| 338 | ||
| 339 | Don't close those snapshots yourself because they're used internally, let them be auto-closed. | |
| 340 | ||
| 341 | Watchers on subs and checkouts are not supported. Instead, use the range option to limit scope. | |
| 342 | ||
| 343 | `await watcher.ready()` | |
| 344 | ||
| 345 | Waits until the watcher is loaded and detecting changes. | |
| 346 | ||
| 347 | `await watcher.close()` | |
| 348 | ||
| 349 | Stops the watcher. You could also stop it by using `break` in the loop. | |
| 350 | ||
| 351 | #### `const snapshot = db.checkout(version)` | |
| 352 | ||
| 353 | Get a read-only snapshot of a previous version. | |
| 354 | ||
| 355 | #### `const snapshot = db.snapshot()` | |
| 356 | ||
| 357 | Shorthand for getting a checkout for the current version. | |
| 358 | ||
| 359 | #### `const sub = db.sub('sub-prefix', options = {})` | |
| 360 | ||
| 361 | Create a sub-database where all entries will be prefixed by a given value. | |
| 362 | ||
| 363 | This makes it easy to create namespaces within a single Hyperbee. | |
| 364 | ||
| 365 | `options` include: | |
| 366 | ||
| 367 | ``` | |
| 368 | { | |
| 369 | sep: Buffer.alloc(1), // A namespace separator | |
| 370 | valueEncoding, // Optional sub valueEncoding (defaults to the parents) | |
| 371 | keyEncoding // Optional sub keyEncoding (defaults to the parents) | |
| 372 | } | |
| 373 | ``` | |
| 374 | ||
| 375 | For example: | |
| 376 | ||
| 377 | ```js | |
| 378 | const root = new Hyperbee(core) | |
| 379 | const sub = root.sub('a') | |
| 380 | ||
| 381 | // In root, this will have the key ('a' + separator + 'b') | |
| 382 | await sub.put('b', 'hello') | |
| 383 | ||
| 384 | // Returns => { key: 'b', value: 'hello') | |
| 385 | await sub.get('b') | |
| 386 | ``` | |
| 387 | ||
| 388 | #### `const header = await db.getHeader([options])` | |
| 389 | ||
| 390 | Returns the header contained in the first block. Throws if undecodable. | |
| 391 | ||
| 392 | `options` are the same as the `core.get` method. | |
| 393 | ||
| 394 | #### `const isHyperbee = await Hyperbee.isHyperbee(core, [options])` | |
| 395 | ||
| 396 | Returns `true` if the core contains a Hyperbee, `false` otherwise. | |
| 397 | ||
| 398 | This requests the first block on the core, so it can throw depending on the options. | |
| 399 | ||
| 400 | `options` are the same as the `core.get` method. | |
| 401 | ||
| 402 | [core-replicate-docs]: https://github.com/holepunchto/hypercore#const-stream--corereplicateisinitiatororreplicationstream |
| … | … | @@ -0,0 +1,1904 @@ |
| 1 | const codecs = require('codecs') | |
| 2 | const { Readable } = require('streamx') | |
| 3 | const mutexify = require('mutexify/promise') | |
| 4 | const b4a = require('b4a') | |
| 5 | const safetyCatch = require('safety-catch') | |
| 6 | const ReadyResource = require('ready-resource') | |
| 7 | const debounce = require('debounceify') | |
| 8 | const Rache = require('rache') | |
| 9 | const rrp = require('resolve-reject-promise') | |
| 10 | ||
| 11 | const { all: unslabAll } = require('unslab') | |
| 12 | ||
| 13 | const RangeIterator = require('./iterators/range') | |
| 14 | const HistoryIterator = require('./iterators/history') | |
| 15 | const DiffIterator = require('./iterators/diff') | |
| 16 | const LocalBlockIterator = require('./iterators/local') | |
| 17 | const Extension = require('./lib/extension') | |
| 18 | const { YoloIndex, Node, Header } = require('./lib/messages') | |
| 19 | const { BLOCK_NOT_AVAILABLE, DECODING_ERROR } = require('hypercore-errors') | |
| 20 | ||
| 21 | const T = 5 | |
| 22 | const MIN_KEYS = T - 1 | |
| 23 | const MAX_CHILDREN = MIN_KEYS * 2 + 1 | |
| 24 | ||
| 25 | const SEP = b4a.alloc(1) | |
| 26 | const EMPTY = b4a.alloc(0) | |
| 27 | ||
| 28 | class Key { | |
| 29 | constructor(seq, value) { | |
| 30 | this.seq = seq | |
| 31 | this.value = value | |
| 32 | } | |
| 33 | } | |
| 34 | ||
| 35 | class Child { | |
| 36 | constructor(seq, offset, value) { | |
| 37 | this.seq = seq | |
| 38 | this.offset = offset | |
| 39 | this.value = value | |
| 40 | } | |
| 41 | } | |
| 42 | ||
| 43 | class Cache { | |
| 44 | constructor(rache) { | |
| 45 | this.keys = rache | |
| 46 | this.length = 0 | |
| 47 | } | |
| 48 | ||
| 49 | get(seq) { | |
| 50 | return this.keys.get(seq) || null | |
| 51 | } | |
| 52 | ||
| 53 | set(seq, key) { | |
| 54 | this.keys.set(seq, key) | |
| 55 | if (seq >= this.length) this.length = seq + 1 | |
| 56 | } | |
| 57 | ||
| 58 | gc(length) { | |
| 59 | // if we need to "work" more than 128 ticks, just bust the cache... | |
| 60 | if (this.length - length > 128) { | |
| 61 | this.keys.clear() | |
| 62 | } else { | |
| 63 | for (let i = length; i < this.length; i++) { | |
| 64 | this.keys.delete(i) | |
| 65 | } | |
| 66 | } | |
| 67 | ||
| 68 | this.length = length | |
| 69 | } | |
| 70 | ||
| 71 | clear() { | |
| 72 | this.keys.clear() | |
| 73 | } | |
| 74 | } | |
| 75 | ||
| 76 | class Pointers { | |
| 77 | constructor(decoded) { | |
| 78 | this.levels = decoded.levels.map((l) => { | |
| 79 | const children = [] | |
| 80 | const keys = [] | |
| 81 | ||
| 82 | for (let i = 0; i < l.keys.length; i++) { | |
| 83 | keys.push(new Key(l.keys[i], null)) | |
| 84 | } | |
| 85 | ||
| 86 | for (let i = 0; i < l.children.length; i += 2) { | |
| 87 | children.push(new Child(l.children[i], l.children[i + 1], null)) | |
| 88 | } | |
| 89 | ||
| 90 | return { keys, children } | |
| 91 | }) | |
| 92 | } | |
| 93 | ||
| 94 | get(i) { | |
| 95 | return this.levels[i] | |
| 96 | } | |
| 97 | ||
| 98 | hasKey(seq) { | |
| 99 | for (const lvl of this.levels) { | |
| 100 | for (const key of lvl.keys) { | |
| 101 | if (key.seq === seq) return true | |
| 102 | } | |
| 103 | } | |
| 104 | return false | |
| 105 | } | |
| 106 | } | |
| 107 | ||
| 108 | function inflate(entry) { | |
| 109 | if (entry.inflated === null) { | |
| 110 | entry.inflated = YoloIndex.decode(entry.index) | |
| 111 | entry.index = null | |
| 112 | } | |
| 113 | return new Pointers(entry.inflated) | |
| 114 | } | |
| 115 | ||
| 116 | function deflate(index) { | |
| 117 | const levels = index.map((l) => { | |
| 118 | const keys = [] | |
| 119 | const children = [] | |
| 120 | ||
| 121 | for (let i = 0; i < l.value.keys.length; i++) { | |
| 122 | keys.push(l.value.keys[i].seq) | |
| 123 | } | |
| 124 | ||
| 125 | for (let i = 0; i < l.value.children.length; i++) { | |
| 126 | children.push(l.value.children[i].seq, l.value.children[i].offset) | |
| 127 | } | |
| 128 | ||
| 129 | return { keys, children } | |
| 130 | }) | |
| 131 | ||
| 132 | return YoloIndex.encode({ levels }) | |
| 133 | } | |
| 134 | ||
| 135 | class TreeNode { | |
| 136 | constructor(block, keys, children, offset) { | |
| 137 | this.block = block | |
| 138 | this.offset = offset | |
| 139 | this.keys = keys | |
| 140 | this.children = children | |
| 141 | this.changed = false | |
| 142 | ||
| 143 | this.preload() | |
| 144 | } | |
| 145 | ||
| 146 | preload() { | |
| 147 | if (this.block === null) return | |
| 148 | ||
| 149 | const core = getBackingCore(this.block.tree.core) | |
| 150 | if (!core) return | |
| 151 | ||
| 152 | const bitfield = core.core.bitfield | |
| 153 | const blocks = [] | |
| 154 | ||
| 155 | for (let i = 0; i < this.keys.length; i++) { | |
| 156 | const k = this.keys[i] | |
| 157 | if (k.value) continue | |
| 158 | if (k.seq >= core.signedLength || (bitfield && bitfield.get(k.seq))) continue | |
| 159 | blocks.push(k.seq) | |
| 160 | } | |
| 161 | for (let i = 0; i < this.children.length; i++) { | |
| 162 | const c = this.children[i] | |
| 163 | if (c.value) continue | |
| 164 | if (c.seq >= core.signedLength || (bitfield && bitfield.get(c.seq))) continue | |
| 165 | blocks.push(c.seq) | |
| 166 | } | |
| 167 | ||
| 168 | if (blocks.length) core.download({ blocks }) | |
| 169 | } | |
| 170 | ||
| 171 | async insertKey(key, value, child, node, encoding, cas) { | |
| 172 | let s = 0 | |
| 173 | let e = this.keys.length | |
| 174 | let c | |
| 175 | ||
| 176 | while (s < e) { | |
| 177 | const mid = (s + e) >> 1 | |
| 178 | c = b4a.compare(key.value, await this.getKey(mid)) | |
| 179 | ||
| 180 | if (c === 0) { | |
| 181 | if (cas) { | |
| 182 | const prev = await this.getKeyNode(mid) | |
| 183 | if (!(await cas(prev.final(encoding), node))) return true | |
| 184 | } | |
| 185 | if (!this.block.tree.tree.alwaysDuplicate) { | |
| 186 | const prev = await this.getKeyNode(mid) | |
| 187 | if (sameValue(prev.value, value)) return true | |
| 188 | } | |
| 189 | this.changed = true | |
| 190 | this.keys[mid] = key | |
| 191 | return true | |
| 192 | } | |
| 193 | ||
| 194 | if (c < 0) e = mid | |
| 195 | else s = mid + 1 | |
| 196 | } | |
| 197 | ||
| 198 | const i = c < 0 ? e : s | |
| 199 | this.keys.splice(i, 0, key) | |
| 200 | if (child) this.children.splice(i + 1, 0, new Child(0, 0, child)) | |
| 201 | this.changed = true | |
| 202 | ||
| 203 | return this.keys.length < MAX_CHILDREN | |
| 204 | } | |
| 205 | ||
| 206 | removeKey(index) { | |
| 207 | this.keys.splice(index, 1) | |
| 208 | if (this.children.length) { | |
| 209 | this.children[index + 1].seq = 0 // mark as freed | |
| 210 | this.children.splice(index + 1, 1) | |
| 211 | } | |
| 212 | this.changed = true | |
| 213 | } | |
| 214 | ||
| 215 | async siblings(parent) { | |
| 216 | for (let i = 0; i < parent.children.length; i++) { | |
| 217 | if (parent.children[i].value === this) { | |
| 218 | const [left, right] = await Promise.all([ | |
| 219 | i ? parent.getChildNode(i - 1) : null, | |
| 220 | i < parent.children.length - 1 ? parent.getChildNode(i + 1) : null | |
| 221 | ]) | |
| 222 | return { left, index: i, right } | |
| 223 | } | |
| 224 | } | |
| 225 | ||
| 226 | throw new Error('Bad parent') | |
| 227 | } | |
| 228 | ||
| 229 | merge(node, median) { | |
| 230 | this.changed = true | |
| 231 | this.keys.push(median) | |
| 232 | for (let i = 0; i < node.keys.length; i++) this.keys.push(node.keys[i]) | |
| 233 | for (let i = 0; i < node.children.length; i++) this.children.push(node.children[i]) | |
| 234 | } | |
| 235 | ||
| 236 | async split() { | |
| 237 | const len = this.keys.length >> 1 | |
| 238 | const right = TreeNode.create(this.block) | |
| 239 | ||
| 240 | while (right.keys.length < len) right.keys.push(this.keys.pop()) | |
| 241 | right.keys.reverse() | |
| 242 | ||
| 243 | await this.getKey(this.keys.length - 1) // make sure the median is loaded | |
| 244 | const median = this.keys.pop() | |
| 245 | ||
| 246 | if (this.children.length) { | |
| 247 | while (right.children.length < len + 1) right.children.push(this.children.pop()) | |
| 248 | right.children.reverse() | |
| 249 | } | |
| 250 | ||
| 251 | this.changed = true | |
| 252 | ||
| 253 | return { | |
| 254 | left: this, | |
| 255 | median, | |
| 256 | right | |
| 257 | } | |
| 258 | } | |
| 259 | ||
| 260 | getKeyNode(index) { | |
| 261 | return this.block.tree.getBlock(this.keys[index].seq) | |
| 262 | } | |
| 263 | ||
| 264 | async getChildNode(index) { | |
| 265 | const child = this.children[index] | |
| 266 | if (child.value) return child.value | |
| 267 | const block = | |
| 268 | child.seq === this.block.seq ? this.block : await this.block.tree.getBlock(child.seq) | |
| 269 | return (child.value = block.getTreeNode(child.offset)) | |
| 270 | } | |
| 271 | ||
| 272 | setKey(index, key) { | |
| 273 | this.keys[index] = key | |
| 274 | this.changed = true | |
| 275 | } | |
| 276 | ||
| 277 | async getKey(index) { | |
| 278 | const key = this.keys[index] | |
| 279 | if (key.value) return key.value | |
| 280 | const k = key.seq === this.block.seq ? this.block.key : await this.block.tree.getKey(key.seq) | |
| 281 | return (key.value = k) | |
| 282 | } | |
| 283 | ||
| 284 | indexChanges(index, seq) { | |
| 285 | const offset = index.push(null) - 1 | |
| 286 | this.changed = false | |
| 287 | ||
| 288 | for (const child of this.children) { | |
| 289 | if (!child.value || !child.value.changed) continue | |
| 290 | child.seq = seq | |
| 291 | child.offset = child.value.indexChanges(index, seq) | |
| 292 | index[child.offset] = child | |
| 293 | } | |
| 294 | ||
| 295 | return offset | |
| 296 | } | |
| 297 | ||
| 298 | updateChildren(seq, block) { | |
| 299 | for (const child of this.children) { | |
| 300 | if (!child.value || child.seq !== seq) continue | |
| 301 | child.value.block = block | |
| 302 | child.value.updateChildren(seq, block) | |
| 303 | } | |
| 304 | } | |
| 305 | ||
| 306 | static create(block) { | |
| 307 | const node = new TreeNode(block, [], [], 0) | |
| 308 | node.changed = true | |
| 309 | return node | |
| 310 | } | |
| 311 | } | |
| 312 | ||
| 313 | class BlockEntry { | |
| 314 | constructor(seq, tree, entry) { | |
| 315 | this.seq = seq | |
| 316 | this.tree = tree | |
| 317 | this.index = null | |
| 318 | this.entry = entry | |
| 319 | this.key = entry.key | |
| 320 | this.value = entry.value | |
| 321 | } | |
| 322 | ||
| 323 | isTarget(key) { | |
| 324 | return b4a.equals(this.key, key) | |
| 325 | } | |
| 326 | ||
| 327 | inflate() { | |
| 328 | if (this.index === null) { | |
| 329 | this.index = inflate(this.entry) | |
| 330 | } | |
| 331 | } | |
| 332 | ||
| 333 | isDeletion() { | |
| 334 | if (this.value !== null) return false | |
| 335 | ||
| 336 | if (this.index === null) { | |
| 337 | this.index = inflate(this.entry) | |
| 338 | } | |
| 339 | ||
| 340 | return !this.index.hasKey(this.seq) | |
| 341 | } | |
| 342 | ||
| 343 | final(encoding) { | |
| 344 | return { | |
| 345 | seq: this.seq, | |
| 346 | key: encoding.key ? encoding.key.decode(this.key) : this.key, | |
| 347 | value: this.value && (encoding.value ? encoding.value.decode(this.value) : this.value) | |
| 348 | } | |
| 349 | } | |
| 350 | ||
| 351 | getTreeNode(offset) { | |
| 352 | if (this.index === null) { | |
| 353 | this.index = inflate(this.entry) | |
| 354 | } | |
| 355 | const entry = this.index.get(offset) | |
| 356 | return new TreeNode(this, entry.keys, entry.children, offset) | |
| 357 | } | |
| 358 | } | |
| 359 | ||
| 360 | class CacheLock { | |
| 361 | constructor() { | |
| 362 | this.map = new Map() | |
| 363 | } | |
| 364 | ||
| 365 | enter(seq) { | |
| 366 | const pending = this.map.get(seq) | |
| 367 | ||
| 368 | if (!pending) { | |
| 369 | this.map.set(seq, []) | |
| 370 | return Promise.resolve() | |
| 371 | } | |
| 372 | ||
| 373 | const { resolve, promise } = rrp() | |
| 374 | pending.push(resolve) | |
| 375 | return promise | |
| 376 | } | |
| 377 | ||
| 378 | exit(seq) { | |
| 379 | const pending = this.map.get(seq) | |
| 380 | if (!pending.length) { | |
| 381 | this.map.delete(seq) | |
| 382 | return | |
| 383 | } | |
| 384 | ||
| 385 | pending.pop()() | |
| 386 | } | |
| 387 | } | |
| 388 | ||
| 389 | class BatchEntry extends BlockEntry { | |
| 390 | constructor(seq, tree, key, value, index) { | |
| 391 | super(seq, tree, { key, value, index: null, inflated: null }) | |
| 392 | this.pendingIndex = index | |
| 393 | } | |
| 394 | ||
| 395 | isTarget(key) { | |
| 396 | return false | |
| 397 | } | |
| 398 | ||
| 399 | getTreeNode(offset) { | |
| 400 | return this.pendingIndex[offset].value | |
| 401 | } | |
| 402 | } | |
| 403 | ||
| 404 | class Hyperbee extends ReadyResource { | |
| 405 | constructor(core, opts = {}) { | |
| 406 | super() | |
| 407 | // this.feed is now deprecated, and will be this.core going forward | |
| 408 | this.feed = core | |
| 409 | this.core = core | |
| 410 | ||
| 411 | this.keyEncoding = opts.keyEncoding ? codecs(opts.keyEncoding) : null | |
| 412 | this.valueEncoding = opts.valueEncoding ? codecs(opts.valueEncoding) : null | |
| 413 | this.extension = opts.extension !== false ? getExtension(this, opts) : null | |
| 414 | this.metadata = opts.metadata || null | |
| 415 | this.lock = opts.lock || mutexify() | |
| 416 | this.sep = opts.sep || SEP | |
| 417 | this.readonly = !!opts.readonly | |
| 418 | this.prefix = opts.prefix || null | |
| 419 | ||
| 420 | // In a future version, this should be false by default | |
| 421 | this.alwaysDuplicate = opts.alwaysDuplicate !== false | |
| 422 | ||
| 423 | this._unprefixedKeyEncoding = this.keyEncoding | |
| 424 | this._sub = !!this.prefix | |
| 425 | this._checkout = opts.checkout || 0 | |
| 426 | this._view = !!opts._view | |
| 427 | ||
| 428 | this._onappendBound = this._view ? null : this._onappend.bind(this) | |
| 429 | this._ontruncateBound = this._view ? null : this._ontruncate.bind(this) | |
| 430 | this._watchers = this._onappendBound ? [] : null | |
| 431 | this._entryWatchers = this._onappendBound ? [] : null | |
| 432 | this._sessions = opts.sessions !== false | |
| 433 | ||
| 434 | this._keyCache = null | |
| 435 | this._nodeCache = null | |
| 436 | this._cacheLock = new CacheLock() | |
| 437 | ||
| 438 | this._batches = [] | |
| 439 | ||
| 440 | if (this._watchers) { | |
| 441 | this.core.on('append', this._onappendBound) | |
| 442 | this.core.on('truncate', this._ontruncateBound) | |
| 443 | } | |
| 444 | ||
| 445 | if (this.prefix && opts._sub) { | |
| 446 | this.keyEncoding = prefixEncoding(this.prefix, this.keyEncoding) | |
| 447 | } | |
| 448 | ||
| 449 | this.ready().catch(safetyCatch) | |
| 450 | } | |
| 451 | ||
| 452 | async _open() { | |
| 453 | if (this.core.opened === false) await this.core.ready() | |
| 454 | // bump the internal ranges since the nodes are small | |
| 455 | if (this._watchers && this.core.replicator.setInflightRange) { | |
| 456 | this.core.replicator.setInflightRange(256, 512) | |
| 457 | } | |
| 458 | ||
| 459 | // snapshot | |
| 460 | if (this._checkout === -1) this._checkout = Math.max(1, this.core.length) | |
| 461 | ||
| 462 | const baseCache = Rache.from(this.core.globalCache) | |
| 463 | this._keyCache = new Cache(baseCache) | |
| 464 | this._nodeCache = new Cache(Rache.from(baseCache)) | |
| 465 | } | |
| 466 | ||
| 467 | get version() { | |
| 468 | return Math.max(1, this._checkout || this.core.length) | |
| 469 | } | |
| 470 | ||
| 471 | get id() { | |
| 472 | return this.core.id | |
| 473 | } | |
| 474 | ||
| 475 | get key() { | |
| 476 | return this.core.key | |
| 477 | } | |
| 478 | ||
| 479 | get discoveryKey() { | |
| 480 | return this.core.discoveryKey | |
| 481 | } | |
| 482 | ||
| 483 | get writable() { | |
| 484 | return this.core.writable | |
| 485 | } | |
| 486 | ||
| 487 | get readable() { | |
| 488 | return this.core.readable | |
| 489 | } | |
| 490 | ||
| 491 | replicate(isInitiator, opts) { | |
| 492 | return this.core.replicate(isInitiator, opts) | |
| 493 | } | |
| 494 | ||
| 495 | update(opts) { | |
| 496 | return this.core.update(opts) | |
| 497 | } | |
| 498 | ||
| 499 | peek(range, opts) { | |
| 500 | return iteratorPeek(this.createRangeIterator(range, { ...opts, limit: 1 })) | |
| 501 | } | |
| 502 | ||
| 503 | createRangeIterator(range, opts = {}) { | |
| 504 | // backwards compat range arg | |
| 505 | opts = opts ? { ...opts, ...range } : range | |
| 506 | ||
| 507 | const extension = opts.extension === false && opts.limit !== 0 ? null : this.extension | |
| 508 | const keyEncoding = opts.keyEncoding ? codecs(opts.keyEncoding) : this.keyEncoding | |
| 509 | ||
| 510 | if (extension) { | |
| 511 | const { onseq, onwait } = opts | |
| 512 | let version = 0 | |
| 513 | let next = 0 | |
| 514 | ||
| 515 | opts = encRange(keyEncoding, { | |
| 516 | ...opts, | |
| 517 | sub: this._sub, | |
| 518 | onseq(seq) { | |
| 519 | if (!version) version = seq + 1 | |
| 520 | if (next) next-- | |
| 521 | if (onseq) onseq(seq) | |
| 522 | }, | |
| 523 | onwait(seq) { | |
| 524 | if (!next) { | |
| 525 | next = Extension.BATCH_SIZE | |
| 526 | extension.iterator(ite.snapshot(version)) | |
| 527 | } | |
| 528 | if (onwait) onwait(seq) | |
| 529 | } | |
| 530 | }) | |
| 531 | } else { | |
| 532 | opts = encRange(keyEncoding, { ...opts, sub: this._sub }) | |
| 533 | } | |
| 534 | ||
| 535 | const ite = new RangeIterator( | |
| 536 | new Batch(this, this._makeSnapshot(), null, false, opts), | |
| 537 | null, | |
| 538 | opts | |
| 539 | ) | |
| 540 | return ite | |
| 541 | } | |
| 542 | ||
| 543 | createReadStream(range, opts) { | |
| 544 | const signal = (opts && opts.signal) || null | |
| 545 | return iteratorToStream(this.createRangeIterator(range, opts), signal) | |
| 546 | } | |
| 547 | ||
| 548 | createHistoryStream(opts) { | |
| 549 | const session = opts && opts.live ? this.core.session() : this._makeSnapshot() | |
| 550 | const signal = (opts && opts.signal) || null | |
| 551 | return iteratorToStream( | |
| 552 | new HistoryIterator(new Batch(this, session, null, false, opts), opts), | |
| 553 | signal | |
| 554 | ) | |
| 555 | } | |
| 556 | ||
| 557 | createDiffStream(right, range, opts) { | |
| 558 | if (typeof right === 'number') right = this.checkout(Math.max(1, right), { reuseSession: true }) | |
| 559 | ||
| 560 | // backwards compat range arg | |
| 561 | opts = opts ? { ...opts, ...range } : range | |
| 562 | ||
| 563 | const signal = (opts && opts.signal) || null | |
| 564 | ||
| 565 | const keyEncoding = opts && opts.keyEncoding ? codecs(opts.keyEncoding) : this.keyEncoding | |
| 566 | if (keyEncoding) opts = encRange(keyEncoding, { ...opts, sub: this._sub }) | |
| 567 | ||
| 568 | let done | |
| 569 | let closing | |
| 570 | let ite | |
| 571 | ||
| 572 | const left = this | |
| 573 | ||
| 574 | const rs = new Readable({ | |
| 575 | signal, | |
| 576 | eagerOpen: true, | |
| 577 | async open(cb) { | |
| 578 | try { | |
| 579 | if (right.opened === false) await right.ready() | |
| 580 | if (left.opened === false) await left.ready() | |
| 581 | } catch (err) { | |
| 582 | cb(err) | |
| 583 | return | |
| 584 | } | |
| 585 | ||
| 586 | if (closing) { | |
| 587 | cb(null) | |
| 588 | return | |
| 589 | } | |
| 590 | ||
| 591 | if (left.core.closing || right.core.closing) { | |
| 592 | cb(new Error('Bee closed')) | |
| 593 | return | |
| 594 | } | |
| 595 | ||
| 596 | const snapshot = right.version > left.version ? right._makeSnapshot() : left._makeSnapshot() | |
| 597 | ||
| 598 | done = cb | |
| 599 | ite = new DiffIterator( | |
| 600 | new Batch(left, snapshot, null, false, opts), | |
| 601 | new Batch(right, snapshot, null, false, opts), | |
| 602 | opts | |
| 603 | ) | |
| 604 | ite.open().then(fin, fin) | |
| 605 | }, | |
| 606 | read(cb) { | |
| 607 | done = cb | |
| 608 | ite.next().then(push, fin) | |
| 609 | }, | |
| 610 | predestroy() { | |
| 611 | if (!ite) { | |
| 612 | closing = Promise.resolve() | |
| 613 | } else { | |
| 614 | closing = ite.close() | |
| 615 | closing.catch(noop) | |
| 616 | } | |
| 617 | }, | |
| 618 | destroy(cb) { | |
| 619 | done = cb | |
| 620 | if (!closing) closing = ite.close() | |
| 621 | closing.then(fin, fin) | |
| 622 | } | |
| 623 | }) | |
| 624 | ||
| 625 | return rs | |
| 626 | ||
| 627 | function fin(err) { | |
| 628 | done(err) | |
| 629 | } | |
| 630 | ||
| 631 | function push(val) { | |
| 632 | rs.push(val) | |
| 633 | done(null) | |
| 634 | } | |
| 635 | } | |
| 636 | ||
| 637 | get(key, opts) { | |
| 638 | const b = new Batch(this, this._makeSnapshot(), null, true, opts) | |
| 639 | return b.get(key) | |
| 640 | } | |
| 641 | ||
| 642 | getBySeq(seq, opts) { | |
| 643 | const b = new Batch(this, this._makeSnapshot(), null, true, opts) | |
| 644 | return b.getBySeq(seq) | |
| 645 | } | |
| 646 | ||
| 647 | put(key, value, opts) { | |
| 648 | const b = new Batch(this, this.core, null, true, opts) | |
| 649 | return b.put(key, value, opts) | |
| 650 | } | |
| 651 | ||
| 652 | batch(opts) { | |
| 653 | return new Batch(this, this.core, mutexify(), true, opts) | |
| 654 | } | |
| 655 | ||
| 656 | del(key, opts) { | |
| 657 | const b = new Batch(this, this.core, null, true, opts) | |
| 658 | return b.del(key, opts) | |
| 659 | } | |
| 660 | ||
| 661 | watch(range, opts) { | |
| 662 | if (!this._watchers) throw new Error('Can only watch the main bee instance') | |
| 663 | return new Watcher(this, range, opts) | |
| 664 | } | |
| 665 | ||
| 666 | async getAndWatch(key, opts) { | |
| 667 | if (!this._watchers) throw new Error('Can only watch the main bee instance') | |
| 668 | ||
| 669 | const watcher = new EntryWatcher(this, key, opts) | |
| 670 | await watcher._debouncedUpdate() | |
| 671 | ||
| 672 | if (this.closing) { | |
| 673 | await watcher.close() | |
| 674 | throw new Error('Bee closed') | |
| 675 | } | |
| 676 | ||
| 677 | return watcher | |
| 678 | } | |
| 679 | ||
| 680 | _onappend() { | |
| 681 | for (const watcher of this._watchers) { | |
| 682 | watcher._onappend() | |
| 683 | } | |
| 684 | ||
| 685 | for (const watcher of this._entryWatchers) { | |
| 686 | watcher._onappend() | |
| 687 | } | |
| 688 | } | |
| 689 | ||
| 690 | _ontruncate(length) { | |
| 691 | for (const watcher of this._watchers) { | |
| 692 | watcher._ontruncate() | |
| 693 | } | |
| 694 | ||
| 695 | for (const watcher of this._entryWatchers) { | |
| 696 | watcher._ontruncate() | |
| 697 | } | |
| 698 | ||
| 699 | this._nodeCache.gc(length) | |
| 700 | this._keyCache.gc(length) | |
| 701 | } | |
| 702 | ||
| 703 | _makeSnapshot() { | |
| 704 | if (this._sessions === false) return this.core | |
| 705 | // TODO: better if we could encapsulate this in hypercore in the future | |
| 706 | return this._checkout <= this.core.length || this._checkout <= 1 | |
| 707 | ? this.core.snapshot() | |
| 708 | : this.core.session({ snapshot: false }) | |
| 709 | } | |
| 710 | ||
| 711 | async clearUnlinked(options = {}) { | |
| 712 | await this.ready() | |
| 713 | ||
| 714 | const { gte = 0, lt = this.version - 1, batchSize = 4096, wait = true } = options | |
| 715 | const checkout = this.version | |
| 716 | ||
| 717 | let prev = this.batch({ wait: false, checkout: gte }) | |
| 718 | let b = this.batch({ wait, checkout }) | |
| 719 | ||
| 720 | const iteBatch = this.batch({ wait: false, checkout }) | |
| 721 | const ite = new LocalBlockIterator(iteBatch, { gte, lt }) | |
| 722 | await ite.open() | |
| 723 | ||
| 724 | let ticks = 0 | |
| 725 | ||
| 726 | while (true) { | |
| 727 | const data = await ite.next() | |
| 728 | if (!data) break | |
| 729 | ||
| 730 | if (!(await isLinked(b, data))) { | |
| 731 | if (b.core.closing || this.core.closing || this.closing) break | |
| 732 | await this.core.clear(data.seq) | |
| 733 | } | |
| 734 | ||
| 735 | const prevNode = await prev.get(data.key, { finalize: false }).catch(toNull) | |
| 736 | ||
| 737 | if (prevNode && !(await isLinked(b, prevNode))) { | |
| 738 | if (b.core.closing || this.core.closing || this.closing) break | |
| 739 | await this.core.clear(prevNode.seq) | |
| 740 | } | |
| 741 | ||
| 742 | if (ticks++ >= batchSize) { | |
| 743 | ticks = 0 | |
| 744 | await prev.close() | |
| 745 | await b.close() | |
| 746 | ||
| 747 | prev = this.batch({ wait: false, checkout: gte }) | |
| 748 | b = this.batch({ wait, checkout }) | |
| 749 | } | |
| 750 | } | |
| 751 | ||
| 752 | if (b.core.closing || this.core.closing || this.closing) throw new Error('Core is closed') | |
| 753 | ||
| 754 | await b.close() | |
| 755 | await prev.close() | |
| 756 | await ite.close() | |
| 757 | await iteBatch.close() | |
| 758 | ||
| 759 | return lt | |
| 760 | } | |
| 761 | ||
| 762 | checkout(version, opts = {}) { | |
| 763 | if (version === 0) version = 1 | |
| 764 | ||
| 765 | // same as above, just checkout isn't set yet... | |
| 766 | const snap = | |
| 767 | opts.reuseSession || this._sessions === false | |
| 768 | ? this.core | |
| 769 | : version <= this.core.length || version <= 1 | |
| 770 | ? this.core.snapshot() | |
| 771 | : this.core.session({ snapshot: false }) | |
| 772 | ||
| 773 | return new Hyperbee(snap, { | |
| 774 | _view: true, | |
| 775 | _sub: false, | |
| 776 | prefix: this.prefix, | |
| 777 | sep: this.sep, | |
| 778 | lock: this.lock, | |
| 779 | checkout: version, | |
| 780 | keyEncoding: opts.keyEncoding || this.keyEncoding, | |
| 781 | valueEncoding: opts.valueEncoding || this.valueEncoding, | |
| 782 | extension: this.extension !== null ? this.extension : false | |
| 783 | }) | |
| 784 | } | |
| 785 | ||
| 786 | snapshot(opts) { | |
| 787 | return this.checkout( | |
| 788 | this.core.opened === false || this._checkout <= 0 ? -1 : Math.max(1, this.version), | |
| 789 | opts | |
| 790 | ) | |
| 791 | } | |
| 792 | ||
| 793 | sub(prefix, opts = {}) { | |
| 794 | let sep = opts.sep || this.sep | |
| 795 | if (!b4a.isBuffer(sep)) sep = b4a.from(sep) | |
| 796 | ||
| 797 | prefix = b4a.concat([this.prefix || EMPTY, b4a.from(prefix), sep]) | |
| 798 | ||
| 799 | const valueEncoding = codecs(opts.valueEncoding || this.valueEncoding) | |
| 800 | const keyEncoding = codecs(opts.keyEncoding || this._unprefixedKeyEncoding) | |
| 801 | ||
| 802 | return new Hyperbee(this.core, { | |
| 803 | _view: true, | |
| 804 | _sub: true, | |
| 805 | prefix, | |
| 806 | sep: this.sep, | |
| 807 | lock: this.lock, | |
| 808 | checkout: this._checkout, | |
| 809 | valueEncoding, | |
| 810 | keyEncoding, | |
| 811 | extension: this.extension !== null ? this.extension : false, | |
| 812 | metadata: this.metadata | |
| 813 | }) | |
| 814 | } | |
| 815 | ||
| 816 | async getHeader(opts) { | |
| 817 | const blk = await this.core.get(0, opts) | |
| 818 | try { | |
| 819 | return blk && Header.decode(blk) | |
| 820 | } catch { | |
| 821 | throw DECODING_ERROR() | |
| 822 | } | |
| 823 | } | |
| 824 | ||
| 825 | async _close() { | |
| 826 | if (!this._view) { | |
| 827 | if (this._keyCache) this._keyCache.clear() | |
| 828 | if (this._nodeCache) this._nodeCache.clear() | |
| 829 | } | |
| 830 | ||
| 831 | if (this._watchers) { | |
| 832 | this.core.off('append', this._onappendBound) | |
| 833 | this.core.off('truncate', this._ontruncateBound) | |
| 834 | ||
| 835 | while (this._watchers.length) { | |
| 836 | await this._watchers[this._watchers.length - 1].close() | |
| 837 | } | |
| 838 | } | |
| 839 | ||
| 840 | if (this._entryWatchers) { | |
| 841 | while (this._entryWatchers.length) { | |
| 842 | await this._entryWatchers[this._entryWatchers.length - 1].close() | |
| 843 | } | |
| 844 | } | |
| 845 | ||
| 846 | while (this._batches.length) { | |
| 847 | await this._batches[this._batches.length - 1].close() | |
| 848 | } | |
| 849 | ||
| 850 | return this.core.close() | |
| 851 | } | |
| 852 | ||
| 853 | static async isHyperbee(core, opts) { | |
| 854 | await core.ready() | |
| 855 | ||
| 856 | const blk0 = await core.get(0, opts) | |
| 857 | if (blk0 === null) throw BLOCK_NOT_AVAILABLE() | |
| 858 | ||
| 859 | try { | |
| 860 | return Header.decode(blk0).protocol === 'hyperbee' | |
| 861 | } catch (err) { | |
| 862 | // undecodable | |
| 863 | return false | |
| 864 | } | |
| 865 | } | |
| 866 | } | |
| 867 | ||
| 868 | class Batch { | |
| 869 | constructor(tree, core, batchLock, cache, options = {}) { | |
| 870 | this.tree = tree | |
| 871 | // this.feed is now deprecated, and will be this.core going forward | |
| 872 | this.feed = core | |
| 873 | this.core = core | |
| 874 | this.index = tree._batches.push(this) - 1 | |
| 875 | this.blocks = cache ? new Map() : null | |
| 876 | this.autoFlush = !batchLock | |
| 877 | this.maxBlocksCached = options.maxBlocksCached || 128 | |
| 878 | this.rootSeq = 0 | |
| 879 | this.root = null | |
| 880 | this.length = 0 | |
| 881 | this.checkout = options.checkout === undefined ? -1 : options.checkout | |
| 882 | this.options = options | |
| 883 | this.locked = null | |
| 884 | this.batchLock = batchLock | |
| 885 | this.onseq = this.options.onseq || noop | |
| 886 | this.appending = null | |
| 887 | this.isSnapshot = this.core !== this.tree.core | |
| 888 | this.shouldUpdate = this.options.update !== false | |
| 889 | this.updating = null | |
| 890 | this.encoding = { | |
| 891 | key: options.keyEncoding ? codecs(options.keyEncoding) : tree.keyEncoding, | |
| 892 | value: options.valueEncoding ? codecs(options.valueEncoding) : tree.valueEncoding | |
| 893 | } | |
| 894 | } | |
| 895 | ||
| 896 | async ready() { | |
| 897 | if (this.core.opened === false) await this.core.ready() | |
| 898 | if (this.tree.opened === false) await this.tree.ready() | |
| 899 | } | |
| 900 | ||
| 901 | async lock() { | |
| 902 | if (this.tree.readonly) throw new Error('Hyperbee is marked as read-only') | |
| 903 | if (this.locked === null) this.locked = await this.tree.lock() | |
| 904 | } | |
| 905 | ||
| 906 | get version() { | |
| 907 | if (this.checkout !== -1) return Math.max(1, this.checkout) | |
| 908 | return Math.max(1, this.tree._checkout || this.core.length + this.length) | |
| 909 | } | |
| 910 | ||
| 911 | async getRoot(ensureHeader) { | |
| 912 | await this.ready() | |
| 913 | if (ensureHeader) { | |
| 914 | if (this.core.length === 0 && this.core.writable && !this.tree.readonly) { | |
| 915 | await this.core.append( | |
| 916 | Header.encode({ | |
| 917 | protocol: 'hyperbee', | |
| 918 | metadata: this.tree.metadata | |
| 919 | }) | |
| 920 | ) | |
| 921 | } | |
| 922 | } | |
| 923 | if (this.tree._checkout === 0 && this.checkout === -1 && this.shouldUpdate) { | |
| 924 | if (this.updating === null) this.updating = this.core.update() | |
| 925 | await this.updating | |
| 926 | } | |
| 927 | if (this.version < 2) return null | |
| 928 | return (await this.getBlock(this.version - 1)).getTreeNode(0) | |
| 929 | } | |
| 930 | ||
| 931 | async getKey(seq) { | |
| 932 | await this.tree._cacheLock.enter(seq) | |
| 933 | ||
| 934 | try { | |
| 935 | const k = this.core.fork === this.tree.core.fork ? this.tree._keyCache.get(seq) : null | |
| 936 | if (k !== null) return k | |
| 937 | const key = (await this._getBlock(seq)).key | |
| 938 | if (this.core.fork === this.tree.core.fork) this.tree._keyCache.set(seq, key) | |
| 939 | return key | |
| 940 | } finally { | |
| 941 | this.tree._cacheLock.exit(seq) | |
| 942 | } | |
| 943 | } | |
| 944 | ||
| 945 | async _getNode(seq) { | |
| 946 | const cached = | |
| 947 | this.tree._nodeCache !== null && this.core.fork === this.tree.core.fork | |
| 948 | ? this.tree._nodeCache.get(seq) | |
| 949 | : null | |
| 950 | if (cached !== null) return cached | |
| 951 | const entry = await this.core.get(seq, { | |
| 952 | ...this.options, | |
| 953 | valueEncoding: Node | |
| 954 | }) | |
| 955 | if (entry === null) throw BLOCK_NOT_AVAILABLE() | |
| 956 | const wrap = copyEntry(entry) | |
| 957 | if (this.core.fork === this.tree.core.fork && this.tree._nodeCache !== null) { | |
| 958 | this.tree._nodeCache.set(seq, wrap) | |
| 959 | } | |
| 960 | return wrap | |
| 961 | } | |
| 962 | ||
| 963 | async getBlock(seq) { | |
| 964 | if (this.rootSeq === 0) this.rootSeq = seq | |
| 965 | await this.tree._cacheLock.enter(seq) | |
| 966 | ||
| 967 | try { | |
| 968 | return await this._getBlock(seq) | |
| 969 | } finally { | |
| 970 | this.tree._cacheLock.exit(seq) | |
| 971 | } | |
| 972 | } | |
| 973 | ||
| 974 | async _getBlock(seq) { | |
| 975 | let b = this.blocks && this.blocks.get(seq) | |
| 976 | if (b) return b | |
| 977 | this.onseq(seq) | |
| 978 | const entry = await this._getNode(seq) | |
| 979 | b = this.blocks && this.blocks.get(seq) | |
| 980 | if (b) return b | |
| 981 | b = new BlockEntry(seq, this, entry) | |
| 982 | if (this.blocks && this.blocks.size - this.length < this.maxBlocksCached) { | |
| 983 | this.blocks.set(seq, b) | |
| 984 | } | |
| 985 | return b | |
| 986 | } | |
| 987 | ||
| 988 | _onwait(key) { | |
| 989 | this.options.onwait = null | |
| 990 | this.tree.extension.get(this.rootSeq + 1, key) | |
| 991 | } | |
| 992 | ||
| 993 | _getEncoding(opts) { | |
| 994 | if (!opts) return this.encoding | |
| 995 | return { | |
| 996 | key: opts.keyEncoding ? codecs(opts.keyEncoding) : this.encoding.key, | |
| 997 | value: opts.valueEncoding ? codecs(opts.valueEncoding) : this.encoding.value | |
| 998 | } | |
| 999 | } | |
| 1000 | ||
| 1001 | peek(range, opts) { | |
| 1002 | return iteratorPeek(this.createRangeIterator(range, { ...opts, limit: 1 })) | |
| 1003 | } | |
| 1004 | ||
| 1005 | createRangeIterator(range, opts = {}) { | |
| 1006 | // backwards compat range arg | |
| 1007 | opts = opts ? { ...opts, ...range } : range | |
| 1008 | ||
| 1009 | const encoding = this._getEncoding(opts) | |
| 1010 | return new RangeIterator( | |
| 1011 | this, | |
| 1012 | encoding, | |
| 1013 | encRange(encoding.key, { ...opts, sub: this.tree._sub }) | |
| 1014 | ) | |
| 1015 | } | |
| 1016 | ||
| 1017 | createReadStream(range, opts) { | |
| 1018 | const signal = (opts && opts.signal) || null | |
| 1019 | return iteratorToStream(this.createRangeIterator(range, opts), signal) | |
| 1020 | } | |
| 1021 | ||
| 1022 | async getBySeq(seq, opts) { | |
| 1023 | const encoding = this._getEncoding(opts) | |
| 1024 | ||
| 1025 | try { | |
| 1026 | const block = (await this.getBlock(seq)).final(encoding) | |
| 1027 | return { key: block.key, value: block.value } | |
| 1028 | } finally { | |
| 1029 | await this._closeSnapshot() | |
| 1030 | } | |
| 1031 | } | |
| 1032 | ||
| 1033 | async get(key, opts) { | |
| 1034 | const encoding = this._getEncoding(opts) | |
| 1035 | const finalize = opts ? opts.finalize !== false : true | |
| 1036 | ||
| 1037 | try { | |
| 1038 | return await this._get(key, encoding, finalize) | |
| 1039 | } finally { | |
| 1040 | await this._closeSnapshot() | |
| 1041 | } | |
| 1042 | } | |
| 1043 | ||
| 1044 | async _get(key, encoding, finalize) { | |
| 1045 | key = enc(encoding.key, key) | |
| 1046 | ||
| 1047 | if (this.tree.extension !== null && this.options.extension !== false) { | |
| 1048 | this.options.onwait = this._onwait.bind(this, key) | |
| 1049 | } | |
| 1050 | ||
| 1051 | let node = await this.getRoot(false) | |
| 1052 | if (!node) return null | |
| 1053 | ||
| 1054 | while (true) { | |
| 1055 | if (node.block.isTarget(key)) { | |
| 1056 | return node.block.isDeletion() ? null : finalize ? node.block.final(encoding) : node.block | |
| 1057 | } | |
| 1058 | ||
| 1059 | let s = 0 | |
| 1060 | let e = node.keys.length | |
| 1061 | let c | |
| 1062 | ||
| 1063 | while (s < e) { | |
| 1064 | const mid = (s + e) >> 1 | |
| 1065 | ||
| 1066 | c = b4a.compare(key, await node.getKey(mid)) | |
| 1067 | ||
| 1068 | if (c === 0) { | |
| 1069 | const block = await this.getBlock(node.keys[mid].seq) | |
| 1070 | return finalize ? block.final(encoding) : block | |
| 1071 | } | |
| 1072 | ||
| 1073 | if (c < 0) e = mid | |
| 1074 | else s = mid + 1 | |
| 1075 | } | |
| 1076 | ||
| 1077 | if (!node.children.length) return null | |
| 1078 | ||
| 1079 | const i = c < 0 ? e : s | |
| 1080 | node = await node.getChildNode(i) | |
| 1081 | } | |
| 1082 | } | |
| 1083 | ||
| 1084 | async links(key, seq) { | |
| 1085 | let node = await this.getRoot(false) | |
| 1086 | if (!node) return false | |
| 1087 | ||
| 1088 | if (node.block.seq === seq) return true | |
| 1089 | ||
| 1090 | while (true) { | |
| 1091 | if (node.block.isTarget(key)) return false | |
| 1092 | ||
| 1093 | let s = 0 | |
| 1094 | let e = node.keys.length | |
| 1095 | let c | |
| 1096 | ||
| 1097 | while (s < e) { | |
| 1098 | const mid = (s + e) >> 1 | |
| 1099 | ||
| 1100 | if (node.keys[mid].seq === seq) return true | |
| 1101 | c = b4a.compare(key, await node.getKey(mid)) | |
| 1102 | ||
| 1103 | if (c === 0) return false | |
| 1104 | ||
| 1105 | if (c < 0) e = mid | |
| 1106 | else s = mid + 1 | |
| 1107 | } | |
| 1108 | ||
| 1109 | if (!node.children.length) return false | |
| 1110 | ||
| 1111 | const i = c < 0 ? e : s | |
| 1112 | node = await node.getChildNode(i) | |
| 1113 | if (node.block.seq === seq) return true | |
| 1114 | } | |
| 1115 | } | |
| 1116 | ||
| 1117 | async put(key, value, opts) { | |
| 1118 | const release = this.batchLock ? await this.batchLock() : null | |
| 1119 | ||
| 1120 | const cas = (opts && opts.cas) || null | |
| 1121 | const encoding = this._getEncoding(opts) | |
| 1122 | ||
| 1123 | if (!this.locked) await this.lock() | |
| 1124 | if (!release) return this._put(key, value, encoding, cas) | |
| 1125 | ||
| 1126 | try { | |
| 1127 | return await this._put(key, value, encoding, cas) | |
| 1128 | } finally { | |
| 1129 | release() | |
| 1130 | } | |
| 1131 | } | |
| 1132 | ||
| 1133 | async _put(key, value, encoding, cas) { | |
| 1134 | const newNode = { | |
| 1135 | seq: 0, | |
| 1136 | key, | |
| 1137 | value | |
| 1138 | } | |
| 1139 | key = enc(encoding.key, key) | |
| 1140 | value = enc(encoding.value, value) | |
| 1141 | ||
| 1142 | const stack = [] | |
| 1143 | ||
| 1144 | let root | |
| 1145 | let node = (root = await this.getRoot(true)) | |
| 1146 | if (!node) node = root = TreeNode.create(null) | |
| 1147 | ||
| 1148 | const seq = (newNode.seq = this.core.length + this.length) | |
| 1149 | const target = new Key(seq, key) | |
| 1150 | ||
| 1151 | while (node.children.length) { | |
| 1152 | stack.push(node) | |
| 1153 | node.changed = true // changed, but compressible | |
| 1154 | ||
| 1155 | let s = 0 | |
| 1156 | let e = node.keys.length | |
| 1157 | let c | |
| 1158 | ||
| 1159 | while (s < e) { | |
| 1160 | const mid = (s + e) >> 1 | |
| 1161 | c = b4a.compare(target.value, await node.getKey(mid)) | |
| 1162 | ||
| 1163 | if (c === 0) { | |
| 1164 | if (cas) { | |
| 1165 | const prev = await node.getKeyNode(mid) | |
| 1166 | if (!(await cas(prev.final(encoding), newNode))) return this._unlockMaybe() | |
| 1167 | } | |
| 1168 | if (!this.tree.alwaysDuplicate) { | |
| 1169 | const prev = await node.getKeyNode(mid) | |
| 1170 | if (sameValue(prev.value, value)) return this._unlockMaybe() | |
| 1171 | } | |
| 1172 | node.setKey(mid, target) | |
| 1173 | return this._append(root, seq, key, value) | |
| 1174 | } | |
| 1175 | ||
| 1176 | if (c < 0) e = mid | |
| 1177 | else s = mid + 1 | |
| 1178 | } | |
| 1179 | ||
| 1180 | const i = c < 0 ? e : s | |
| 1181 | node = await node.getChildNode(i) | |
| 1182 | } | |
| 1183 | ||
| 1184 | let needsSplit = !(await node.insertKey(target, value, null, newNode, encoding, cas)) | |
| 1185 | if (!node.changed) return this._unlockMaybe() | |
| 1186 | ||
| 1187 | while (needsSplit) { | |
| 1188 | const parent = stack.pop() | |
| 1189 | const { median, right } = await node.split() | |
| 1190 | ||
| 1191 | if (parent) { | |
| 1192 | needsSplit = !(await parent.insertKey(median, value, right, null, encoding, null)) | |
| 1193 | node = parent | |
| 1194 | } else { | |
| 1195 | root = TreeNode.create(node.block) | |
| 1196 | root.changed = true | |
| 1197 | root.keys.push(median) | |
| 1198 | root.children.push(new Child(0, 0, node), new Child(0, 0, right)) | |
| 1199 | needsSplit = false | |
| 1200 | } | |
| 1201 | } | |
| 1202 | ||
| 1203 | return this._append(root, seq, key, value) | |
| 1204 | } | |
| 1205 | ||
| 1206 | async del(key, opts) { | |
| 1207 | const release = this.batchLock ? await this.batchLock() : null | |
| 1208 | const cas = (opts && opts.cas) || null | |
| 1209 | const encoding = this._getEncoding(opts) | |
| 1210 | ||
| 1211 | if (!this.locked) await this.lock() | |
| 1212 | if (!release) return this._del(key, encoding, cas) | |
| 1213 | ||
| 1214 | try { | |
| 1215 | return await this._del(key, encoding, cas) | |
| 1216 | } finally { | |
| 1217 | release() | |
| 1218 | } | |
| 1219 | } | |
| 1220 | ||
| 1221 | async _del(key, encoding, cas) { | |
| 1222 | const delNode = { | |
| 1223 | seq: 0, | |
| 1224 | key, | |
| 1225 | value: null | |
| 1226 | } | |
| 1227 | ||
| 1228 | key = enc(encoding.key, key) | |
| 1229 | ||
| 1230 | const stack = [] | |
| 1231 | ||
| 1232 | let node = await this.getRoot(true) | |
| 1233 | if (!node) return this._unlockMaybe() | |
| 1234 | ||
| 1235 | const seq = (delNode.seq = this.core.length + this.length) | |
| 1236 | ||
| 1237 | while (true) { | |
| 1238 | stack.push(node) | |
| 1239 | ||
| 1240 | let s = 0 | |
| 1241 | let e = node.keys.length | |
| 1242 | let c | |
| 1243 | ||
| 1244 | while (s < e) { | |
| 1245 | const mid = (s + e) >> 1 | |
| 1246 | c = b4a.compare(key, await node.getKey(mid)) | |
| 1247 | ||
| 1248 | if (c === 0) { | |
| 1249 | if (cas) { | |
| 1250 | const prev = await node.getKeyNode(mid) | |
| 1251 | if (!(await cas(prev.final(encoding), delNode))) return this._unlockMaybe() | |
| 1252 | } | |
| 1253 | if (node.children.length) await setKeyToNearestLeaf(node, mid, stack) | |
| 1254 | else node.removeKey(mid) | |
| 1255 | // we mark these as changed late, so we don't rewrite them if it is a 404 | |
| 1256 | for (const node of stack) node.changed = true | |
| 1257 | return this._append(await rebalance(stack), seq, key, null) | |
| 1258 | } | |
| 1259 | ||
| 1260 | if (c < 0) e = mid | |
| 1261 | else s = mid + 1 | |
| 1262 | } | |
| 1263 | ||
| 1264 | if (!node.children.length) return this._unlockMaybe() | |
| 1265 | ||
| 1266 | const i = c < 0 ? e : s | |
| 1267 | node = await node.getChildNode(i) | |
| 1268 | } | |
| 1269 | } | |
| 1270 | ||
| 1271 | async _closeSnapshot() { | |
| 1272 | if (this.isSnapshot) { | |
| 1273 | await this.core.close() | |
| 1274 | this._finalize() | |
| 1275 | } | |
| 1276 | } | |
| 1277 | ||
| 1278 | async close() { | |
| 1279 | if (this.isSnapshot) return this._closeSnapshot() | |
| 1280 | ||
| 1281 | this.root = null | |
| 1282 | if (this.blocks) this.blocks.clear() | |
| 1283 | this.length = 0 | |
| 1284 | this._unlock() | |
| 1285 | } | |
| 1286 | ||
| 1287 | destroy() { | |
| 1288 | // compat, remove later | |
| 1289 | this.close().catch(noop) | |
| 1290 | } | |
| 1291 | ||
| 1292 | toBlocks() { | |
| 1293 | if (this.appending) return this.appending | |
| 1294 | ||
| 1295 | const batch = new Array(this.length) | |
| 1296 | ||
| 1297 | for (let i = 0; i < this.length; i++) { | |
| 1298 | const seq = this.core.length + i | |
| 1299 | const { pendingIndex, key, value } = this.blocks.get(seq) | |
| 1300 | ||
| 1301 | if (i < this.length - 1) { | |
| 1302 | pendingIndex[0] = null | |
| 1303 | let j = 0 | |
| 1304 | ||
| 1305 | while (j < pendingIndex.length) { | |
| 1306 | const idx = pendingIndex[j] | |
| 1307 | if (idx !== null && idx.seq === seq) { | |
| 1308 | idx.offset = j++ | |
| 1309 | continue | |
| 1310 | } | |
| 1311 | if (j === pendingIndex.length - 1) pendingIndex.pop() | |
| 1312 | else pendingIndex[j] = pendingIndex.pop() | |
| 1313 | } | |
| 1314 | } | |
| 1315 | ||
| 1316 | batch[i] = Node.encode({ | |
| 1317 | key, | |
| 1318 | value, | |
| 1319 | index: deflate(pendingIndex) | |
| 1320 | }) | |
| 1321 | } | |
| 1322 | ||
| 1323 | this.appending = batch | |
| 1324 | return batch | |
| 1325 | } | |
| 1326 | ||
| 1327 | flush() { | |
| 1328 | if (!this.length) return this.close() | |
| 1329 | ||
| 1330 | const batch = this.toBlocks() | |
| 1331 | ||
| 1332 | this.root = null | |
| 1333 | this.blocks.clear() | |
| 1334 | this.length = 0 | |
| 1335 | ||
| 1336 | return this._appendBatch(batch) | |
| 1337 | } | |
| 1338 | ||
| 1339 | _unlockMaybe() { | |
| 1340 | if (this.autoFlush) this._unlock() | |
| 1341 | } | |
| 1342 | ||
| 1343 | _unlock() { | |
| 1344 | const locked = this.locked | |
| 1345 | this.locked = null | |
| 1346 | if (locked !== null) locked() | |
| 1347 | this._finalize() | |
| 1348 | } | |
| 1349 | ||
| 1350 | _finalize() { | |
| 1351 | // technically finalize can be called more than once, so here we just check if we already have been removed | |
| 1352 | if (this.index >= this.tree._batches.length || this.tree._batches[this.index] !== this) return | |
| 1353 | const top = this.tree._batches.pop() | |
| 1354 | if (top === this) return | |
| 1355 | top.index = this.index | |
| 1356 | this.tree._batches[top.index] = top | |
| 1357 | } | |
| 1358 | ||
| 1359 | _append(root, seq, key, value) { | |
| 1360 | const index = [] | |
| 1361 | root.indexChanges(index, seq) | |
| 1362 | index[0] = new Child(seq, 0, root) | |
| 1363 | ||
| 1364 | if (!this.autoFlush) { | |
| 1365 | const block = new BatchEntry(seq, this, key, value, index) | |
| 1366 | root.block = block | |
| 1367 | this.root = root | |
| 1368 | this.length++ | |
| 1369 | this.blocks.set(seq, block) | |
| 1370 | ||
| 1371 | root.updateChildren(seq, block) | |
| 1372 | return | |
| 1373 | } | |
| 1374 | ||
| 1375 | return this._appendBatch( | |
| 1376 | Node.encode({ | |
| 1377 | key, | |
| 1378 | value, | |
| 1379 | index: deflate(index) | |
| 1380 | }) | |
| 1381 | ) | |
| 1382 | } | |
| 1383 | ||
| 1384 | async _appendBatch(raw) { | |
| 1385 | try { | |
| 1386 | await this.core.append(raw) | |
| 1387 | } finally { | |
| 1388 | this._unlock() | |
| 1389 | } | |
| 1390 | } | |
| 1391 | } | |
| 1392 | ||
| 1393 | class EntryWatcher extends ReadyResource { | |
| 1394 | constructor(bee, key, opts = {}) { | |
| 1395 | super() | |
| 1396 | ||
| 1397 | this.keyEncoding = opts.keyEncoding || bee.keyEncoding | |
| 1398 | this.valueEncoding = opts.valueEncoding || bee.valueEncoding | |
| 1399 | ||
| 1400 | this.index = bee._entryWatchers.push(this) - 1 | |
| 1401 | this.bee = bee | |
| 1402 | ||
| 1403 | this.key = key | |
| 1404 | this.node = null | |
| 1405 | ||
| 1406 | this._forceUpdate = false | |
| 1407 | this._debouncedUpdate = debounce(this._processUpdate.bind(this)) | |
| 1408 | } | |
| 1409 | ||
| 1410 | _close() { | |
| 1411 | const top = this.bee._entryWatchers.pop() | |
| 1412 | if (top !== this) { | |
| 1413 | top.index = this.index | |
| 1414 | this.bee._entryWatchers[top.index] = top | |
| 1415 | } | |
| 1416 | } | |
| 1417 | ||
| 1418 | _onappend() { | |
| 1419 | this._debouncedUpdate() | |
| 1420 | } | |
| 1421 | ||
| 1422 | _ontruncate() { | |
| 1423 | this._forceUpdate = true | |
| 1424 | this._debouncedUpdate() | |
| 1425 | } | |
| 1426 | ||
| 1427 | async _processUpdate() { | |
| 1428 | const force = this._forceUpdate | |
| 1429 | this._forceUpdate = false | |
| 1430 | ||
| 1431 | let newNode | |
| 1432 | try { | |
| 1433 | newNode = await this.bee.get(this.key, { | |
| 1434 | keyEncoding: this.keyEncoding, | |
| 1435 | valueEncoding: this.valueEncoding | |
| 1436 | }) | |
| 1437 | } catch (e) { | |
| 1438 | if (e.code === 'SNAPSHOT_NOT_AVAILABLE') { | |
| 1439 | // There was a truncate event before the get resolved | |
| 1440 | // So this handler will run again anyway | |
| 1441 | return | |
| 1442 | } else if (this.bee.closing) { | |
| 1443 | this.close().catch(safetyCatch) | |
| 1444 | return | |
| 1445 | } | |
| 1446 | this.emit('error', e) | |
| 1447 | return | |
| 1448 | } | |
| 1449 | ||
| 1450 | if (force || newNode?.seq !== this.node?.seq) { | |
| 1451 | this.node = newNode | |
| 1452 | this.emit('update') | |
| 1453 | } | |
| 1454 | } | |
| 1455 | } | |
| 1456 | ||
| 1457 | class Watcher extends ReadyResource { | |
| 1458 | constructor(bee, range, opts = {}) { | |
| 1459 | super() | |
| 1460 | ||
| 1461 | this.keyEncoding = opts.keyEncoding || bee.keyEncoding | |
| 1462 | this.valueEncoding = opts.valueEncoding || bee.valueEncoding | |
| 1463 | this.index = bee._watchers.push(this) - 1 | |
| 1464 | this.bee = bee | |
| 1465 | this.core = bee.core | |
| 1466 | ||
| 1467 | this.latestDiff = 0 | |
| 1468 | this.range = range | |
| 1469 | this.map = opts.map || defaultWatchMap | |
| 1470 | ||
| 1471 | this.current = null | |
| 1472 | this.previous = null | |
| 1473 | this.currentMapped = null | |
| 1474 | this.previousMapped = null | |
| 1475 | this.stream = null | |
| 1476 | ||
| 1477 | this._lock = mutexify() | |
| 1478 | this._flowing = false | |
| 1479 | this._resolveOnChange = null | |
| 1480 | this._differ = opts.differ || defaultDiffer | |
| 1481 | this._eager = !!opts.eager | |
| 1482 | this._onchange = opts.onchange || null | |
| 1483 | ||
| 1484 | this.on('newListener', autoFlowOnUpdate) | |
| 1485 | ||
| 1486 | this.ready().catch(safetyCatch) | |
| 1487 | } | |
| 1488 | ||
| 1489 | async _consume() { | |
| 1490 | if (this._flowing) return | |
| 1491 | try { | |
| 1492 | for await (const _ of this) { | |
| 1493 | } // eslint-disable-line | |
| 1494 | } catch {} | |
| 1495 | } | |
| 1496 | ||
| 1497 | async _open() { | |
| 1498 | await this.bee.ready() | |
| 1499 | ||
| 1500 | const opts = { | |
| 1501 | keyEncoding: this.keyEncoding, | |
| 1502 | valueEncoding: this.valueEncoding | |
| 1503 | } | |
| 1504 | ||
| 1505 | // Point from which to start watching | |
| 1506 | this.current = this._eager ? this.bee.checkout(1, opts) : this.bee.snapshot(opts) | |
| 1507 | await this.current.ready() | |
| 1508 | ||
| 1509 | if (this._onchange) { | |
| 1510 | if (this._eager) await this._onchange() | |
| 1511 | this._consume() | |
| 1512 | } | |
| 1513 | } | |
| 1514 | ||
| 1515 | [Symbol.asyncIterator]() { | |
| 1516 | this._flowing = true | |
| 1517 | return this | |
| 1518 | } | |
| 1519 | ||
| 1520 | _ontruncate() { | |
| 1521 | this._onappend() | |
| 1522 | } | |
| 1523 | ||
| 1524 | _onappend() { | |
| 1525 | const resolve = this._resolveOnChange | |
| 1526 | this._resolveOnChange = null | |
| 1527 | if (resolve) resolve() | |
| 1528 | } | |
| 1529 | ||
| 1530 | async _waitForChanges() { | |
| 1531 | if (this.current.version < this.bee.version || this.closing) return | |
| 1532 | ||
| 1533 | await new Promise((resolve) => { | |
| 1534 | this._resolveOnChange = resolve | |
| 1535 | }) | |
| 1536 | } | |
| 1537 | ||
| 1538 | async next() { | |
| 1539 | try { | |
| 1540 | return await this._next() | |
| 1541 | } catch (err) { | |
| 1542 | if (this.closing) return { value: undefined, done: true } | |
| 1543 | await this.close() | |
| 1544 | throw err | |
| 1545 | } | |
| 1546 | } | |
| 1547 | ||
| 1548 | async _next() { | |
| 1549 | const release = await this._lock() | |
| 1550 | ||
| 1551 | try { | |
| 1552 | if (this.closing) return { value: undefined, done: true } | |
| 1553 | ||
| 1554 | if (!this.opened) await this.ready() | |
| 1555 | ||
| 1556 | while (true) { | |
| 1557 | await this._waitForChanges() | |
| 1558 | ||
| 1559 | if (this.closing) return { value: undefined, done: true } | |
| 1560 | ||
| 1561 | await this._closePrevious() | |
| 1562 | this.previous = this.current.snapshot() | |
| 1563 | ||
| 1564 | await this._closeCurrent() | |
| 1565 | this.current = this.bee.snapshot({ | |
| 1566 | keyEncoding: this.keyEncoding, | |
| 1567 | valueEncoding: this.valueEncoding | |
| 1568 | }) | |
| 1569 | ||
| 1570 | await this.current.ready() | |
| 1571 | await this.previous.ready() | |
| 1572 | ||
| 1573 | if (this.current.core.fork !== this.previous.core.fork) { | |
| 1574 | return await this._yield() | |
| 1575 | } | |
| 1576 | ||
| 1577 | this.stream = this._differ(this.current, this.previous, this.range) | |
| 1578 | ||
| 1579 | try { | |
| 1580 | for await (const data of this.stream) { | |
| 1581 | // eslint-disable-line | |
| 1582 | return await this._yield() | |
| 1583 | } | |
| 1584 | } finally { | |
| 1585 | this.stream = null | |
| 1586 | } | |
| 1587 | } | |
| 1588 | } finally { | |
| 1589 | release() | |
| 1590 | } | |
| 1591 | } | |
| 1592 | ||
| 1593 | async _yield() { | |
| 1594 | this.currentMapped = this.map(this.current) | |
| 1595 | this.previousMapped = this.map(this.previous) | |
| 1596 | ||
| 1597 | if (this._onchange) { | |
| 1598 | try { | |
| 1599 | await this._onchange() | |
| 1600 | } catch (err) { | |
| 1601 | safetyCatch(err) | |
| 1602 | } | |
| 1603 | } | |
| 1604 | ||
| 1605 | this.emit('update') | |
| 1606 | return { done: false, value: [this.currentMapped, this.previousMapped] } | |
| 1607 | } | |
| 1608 | ||
| 1609 | async return() { | |
| 1610 | await this.close() | |
| 1611 | return { done: true } | |
| 1612 | } | |
| 1613 | ||
| 1614 | async _close() { | |
| 1615 | const top = this.bee._watchers.pop() | |
| 1616 | if (top !== this) { | |
| 1617 | top.index = this.index | |
| 1618 | this.bee._watchers[top.index] = top | |
| 1619 | } | |
| 1620 | ||
| 1621 | if (this.stream && !this.stream.destroying) { | |
| 1622 | this.stream.destroy() | |
| 1623 | } | |
| 1624 | ||
| 1625 | this._onappend() // Continue execution being closed | |
| 1626 | ||
| 1627 | await this._closeCurrent().catch(safetyCatch) | |
| 1628 | await this._closePrevious().catch(safetyCatch) | |
| 1629 | ||
| 1630 | const release = await this._lock() | |
| 1631 | release() | |
| 1632 | } | |
| 1633 | ||
| 1634 | destroy() { | |
| 1635 | return this.close() | |
| 1636 | } | |
| 1637 | ||
| 1638 | async _closeCurrent() { | |
| 1639 | if (this.currentMapped) await this.currentMapped.close() | |
| 1640 | if (this.current) await this.current.close() | |
| 1641 | this.current = this.currentMapped = null | |
| 1642 | } | |
| 1643 | ||
| 1644 | async _closePrevious() { | |
| 1645 | if (this.previousMapped) await this.previousMapped.close() | |
| 1646 | if (this.previous) await this.previous.close() | |
| 1647 | this.previous = this.previousMapped = null | |
| 1648 | } | |
| 1649 | } | |
| 1650 | ||
| 1651 | function autoFlowOnUpdate(name) { | |
| 1652 | if (name === 'update') this._consume() | |
| 1653 | } | |
| 1654 | ||
| 1655 | function defaultWatchMap(snapshot) { | |
| 1656 | return snapshot | |
| 1657 | } | |
| 1658 | ||
| 1659 | async function leafSize(node, goLeft) { | |
| 1660 | while (node.children.length) node = await node.getChildNode(goLeft ? 0 : node.children.length - 1) | |
| 1661 | return node.keys.length | |
| 1662 | } | |
| 1663 | ||
| 1664 | async function setKeyToNearestLeaf(node, index, stack) { | |
| 1665 | let [left, right] = await Promise.all([node.getChildNode(index), node.getChildNode(index + 1)]) | |
| 1666 | const [ls, rs] = await Promise.all([leafSize(left, false), leafSize(right, true)]) | |
| 1667 | ||
| 1668 | if (ls < rs) { | |
| 1669 | // if fewer leaves on the left | |
| 1670 | stack.push(right) | |
| 1671 | while (right.children.length) stack.push((right = right.children[0].value)) | |
| 1672 | node.keys[index] = right.keys.shift() | |
| 1673 | } else { | |
| 1674 | // if fewer leaves on the right | |
| 1675 | stack.push(left) | |
| 1676 | while (left.children.length) stack.push((left = left.children[left.children.length - 1].value)) | |
| 1677 | node.keys[index] = left.keys.pop() | |
| 1678 | } | |
| 1679 | } | |
| 1680 | ||
| 1681 | async function rebalance(stack) { | |
| 1682 | const root = stack[0] | |
| 1683 | ||
| 1684 | while (stack.length > 1) { | |
| 1685 | const node = stack.pop() | |
| 1686 | const parent = stack[stack.length - 1] | |
| 1687 | ||
| 1688 | if (node.keys.length >= MIN_KEYS) return root | |
| 1689 | ||
| 1690 | let { left, index, right } = await node.siblings(parent) | |
| 1691 | ||
| 1692 | // maybe borrow from left sibling? | |
| 1693 | if (left && left.keys.length > MIN_KEYS) { | |
| 1694 | left.changed = true | |
| 1695 | node.keys.unshift(parent.keys[index - 1]) | |
| 1696 | if (left.children.length) node.children.unshift(left.children.pop()) | |
| 1697 | parent.keys[index - 1] = left.keys.pop() | |
| 1698 | return root | |
| 1699 | } | |
| 1700 | ||
| 1701 | // maybe borrow from right sibling? | |
| 1702 | if (right && right.keys.length > MIN_KEYS) { | |
| 1703 | right.changed = true | |
| 1704 | node.keys.push(parent.keys[index]) | |
| 1705 | if (right.children.length) node.children.push(right.children.shift()) | |
| 1706 | parent.keys[index] = right.keys.shift() | |
| 1707 | return root | |
| 1708 | } | |
| 1709 | ||
| 1710 | // merge node with another sibling | |
| 1711 | if (left) { | |
| 1712 | index-- | |
| 1713 | right = node | |
| 1714 | } else { | |
| 1715 | left = node | |
| 1716 | } | |
| 1717 | ||
| 1718 | left.merge(right, parent.keys[index]) | |
| 1719 | parent.removeKey(index) | |
| 1720 | } | |
| 1721 | ||
| 1722 | // check if the tree shrunk | |
| 1723 | if (!root.keys.length && root.children.length) return root.getChildNode(0) | |
| 1724 | return root | |
| 1725 | } | |
| 1726 | ||
| 1727 | function iteratorToStream(ite, signal) { | |
| 1728 | let done | |
| 1729 | let closing | |
| 1730 | ||
| 1731 | const rs = new Readable({ | |
| 1732 | signal, | |
| 1733 | open(cb) { | |
| 1734 | done = cb | |
| 1735 | ite.open().then(fin, fin) | |
| 1736 | }, | |
| 1737 | read(cb) { | |
| 1738 | done = cb | |
| 1739 | ite.next().then(push, fin) | |
| 1740 | }, | |
| 1741 | predestroy() { | |
| 1742 | closing = ite.close() | |
| 1743 | closing.catch(noop) | |
| 1744 | }, | |
| 1745 | destroy(cb) { | |
| 1746 | done = cb | |
| 1747 | if (!closing) closing = ite.close() | |
| 1748 | closing.then(fin, fin) | |
| 1749 | } | |
| 1750 | }) | |
| 1751 | ||
| 1752 | return rs | |
| 1753 | ||
| 1754 | function fin(err) { | |
| 1755 | done(err) | |
| 1756 | } | |
| 1757 | ||
| 1758 | function push(val) { | |
| 1759 | rs.push(val) | |
| 1760 | done(null) | |
| 1761 | } | |
| 1762 | } | |
| 1763 | ||
| 1764 | async function iteratorPeek(ite) { | |
| 1765 | try { | |
| 1766 | await ite.open() | |
| 1767 | return await ite.next() | |
| 1768 | } finally { | |
| 1769 | await ite.close() | |
| 1770 | } | |
| 1771 | } | |
| 1772 | ||
| 1773 | function encRange(e, opts) { | |
| 1774 | if (!e) return opts | |
| 1775 | ||
| 1776 | if (e.encodeRange) { | |
| 1777 | const r = e.encodeRange({ | |
| 1778 | gt: opts.gt, | |
| 1779 | gte: opts.gte, | |
| 1780 | lt: opts.lt, | |
| 1781 | lte: opts.lte | |
| 1782 | }) | |
| 1783 | opts.gt = r.gt | |
| 1784 | opts.gte = r.gte | |
| 1785 | opts.lt = r.lt | |
| 1786 | opts.lte = r.lte | |
| 1787 | return opts | |
| 1788 | } | |
| 1789 | ||
| 1790 | if (opts.gt !== undefined) opts.gt = enc(e, opts.gt) | |
| 1791 | if (opts.gte !== undefined) opts.gte = enc(e, opts.gte) | |
| 1792 | if (opts.lt !== undefined) opts.lt = enc(e, opts.lt) | |
| 1793 | if (opts.lte !== undefined) opts.lte = enc(e, opts.lte) | |
| 1794 | if (opts.sub && !opts.gt && !opts.gte) opts.gt = enc(e, SEP) | |
| 1795 | if (opts.sub && !opts.lt && !opts.lte) opts.lt = bump(enc(e, EMPTY)) | |
| 1796 | ||
| 1797 | return opts | |
| 1798 | } | |
| 1799 | ||
| 1800 | function bump(key) { | |
| 1801 | // key should have been copied by enc above before hitting this | |
| 1802 | key[key.length - 1]++ | |
| 1803 | return key | |
| 1804 | } | |
| 1805 | ||
| 1806 | function enc(e, v) { | |
| 1807 | if (v === undefined || v === null) return null | |
| 1808 | if (e !== null) return e.encode(v) | |
| 1809 | if (typeof v === 'string') return b4a.from(v) | |
| 1810 | return v | |
| 1811 | } | |
| 1812 | ||
| 1813 | function prefixEncoding(prefix, keyEncoding) { | |
| 1814 | return { | |
| 1815 | encode(key) { | |
| 1816 | return b4a.concat([prefix, b4a.isBuffer(key) ? key : enc(keyEncoding, key)]) | |
| 1817 | }, | |
| 1818 | decode(key) { | |
| 1819 | const sliced = key.slice(prefix.length, key.length) | |
| 1820 | return keyEncoding ? keyEncoding.decode(sliced) : sliced | |
| 1821 | } | |
| 1822 | } | |
| 1823 | } | |
| 1824 | ||
| 1825 | function copyEntry(entry) { | |
| 1826 | let key = entry.key | |
| 1827 | let value = entry.value | |
| 1828 | let index = entry.index | |
| 1829 | ||
| 1830 | // key, value and index all refer to the same buffer (one hypercore block) | |
| 1831 | // If together they are larger than half the buffer's byteLength, | |
| 1832 | // this means that they got their own private slab (see Buffer.allocUnsafe docs) | |
| 1833 | // so no need to unslab | |
| 1834 | const size = | |
| 1835 | key.byteLength + | |
| 1836 | (value === null ? 0 : value.byteLength) + | |
| 1837 | (index === null ? 0 : index.byteLength) | |
| 1838 | if (2 * size < key.buffer.byteLength) { | |
| 1839 | const [newKey, newValue, newIndex] = unslabAll([entry.key, entry.value, entry.index]) | |
| 1840 | key = newKey | |
| 1841 | value = newValue | |
| 1842 | index = newIndex | |
| 1843 | } | |
| 1844 | ||
| 1845 | return { | |
| 1846 | key, | |
| 1847 | value, | |
| 1848 | index, | |
| 1849 | inflated: null | |
| 1850 | } | |
| 1851 | } | |
| 1852 | ||
| 1853 | function defaultDiffer(currentSnap, previousSnap, opts) { | |
| 1854 | return currentSnap.createDiffStream(previousSnap, opts) | |
| 1855 | } | |
| 1856 | ||
| 1857 | function toNull() { | |
| 1858 | return null | |
| 1859 | } | |
| 1860 | ||
| 1861 | function getBackingCore(core) { | |
| 1862 | if (core.core) return core | |
| 1863 | if (core.getBackingCore) return core.getBackingCore().session | |
| 1864 | return null | |
| 1865 | } | |
| 1866 | ||
| 1867 | function sameValue(a, b) { | |
| 1868 | return a === b || (a !== null && b !== null && b4a.equals(a, b)) | |
| 1869 | } | |
| 1870 | ||
| 1871 | function noop() {} | |
| 1872 | ||
| 1873 | function getExtension(db, opts) { | |
| 1874 | if (opts.extension === false) return null | |
| 1875 | if (opts.extension && opts.extension !== true) return opts.extension | |
| 1876 | return Extension.register(db) | |
| 1877 | } | |
| 1878 | ||
| 1879 | async function isLinked(batch, block) { | |
| 1880 | const seq = block.seq | |
| 1881 | block.inflate() | |
| 1882 | ||
| 1883 | const keys = [block.key] | |
| 1884 | const wait = batch.options.wait | |
| 1885 | ||
| 1886 | for (const l of block.index.levels) { | |
| 1887 | if (!l.keys.length) continue | |
| 1888 | batch.options.wait = false | |
| 1889 | try { | |
| 1890 | keys.push(await batch.getKey(l.keys[0].seq)) | |
| 1891 | } catch {} | |
| 1892 | batch.options.wait = wait | |
| 1893 | } | |
| 1894 | ||
| 1895 | for (const k of keys) { | |
| 1896 | if (await batch.links(k, seq)) return true | |
| 1897 | } | |
| 1898 | ||
| 1899 | if (batch.core.closing) throw new Error('Core is closed') | |
| 1900 | ||
| 1901 | return false | |
| 1902 | } | |
| 1903 | ||
| 1904 | module.exports = Hyperbee |
| … | … | @@ -0,0 +1,220 @@ |
| 1 | const b4a = require('b4a') | |
| 2 | ||
| 3 | class SubTree { | |
| 4 | constructor(node, parent) { | |
| 5 | this.node = node | |
| 6 | this.parent = parent | |
| 7 | ||
| 8 | this.isKey = node.children.length === 0 | |
| 9 | this.i = this.isKey ? 1 : 0 | |
| 10 | this.n = 0 | |
| 11 | ||
| 12 | const child = this.isKey ? null : this.node.children[0] | |
| 13 | this.seq = child !== null ? child.seq : this.node.keys[0].seq | |
| 14 | this.offset = child !== null ? child.offset : 0 | |
| 15 | } | |
| 16 | ||
| 17 | next() { | |
| 18 | this.i++ | |
| 19 | this.isKey = (this.i & 1) === 1 | |
| 20 | if (!this.isKey && !this.node.children.length) this.i++ | |
| 21 | return this.update() | |
| 22 | } | |
| 23 | ||
| 24 | async bisect(key, incl) { | |
| 25 | let s = 0 | |
| 26 | let e = this.node.keys.length | |
| 27 | let c | |
| 28 | ||
| 29 | while (s < e) { | |
| 30 | const mid = (s + e) >> 1 | |
| 31 | c = cmp(key, await this.node.getKey(mid)) | |
| 32 | ||
| 33 | if (c === 0) { | |
| 34 | if (incl) this.i = mid * 2 + 1 | |
| 35 | else this.i = mid * 2 + (this.node.children.length ? 2 : 3) | |
| 36 | return true | |
| 37 | } | |
| 38 | ||
| 39 | if (c < 0) e = mid | |
| 40 | else s = mid + 1 | |
| 41 | } | |
| 42 | ||
| 43 | const i = c < 0 ? e : s | |
| 44 | this.i = 2 * i + (this.node.children.length ? 0 : 1) | |
| 45 | return this.node.children.length === 0 | |
| 46 | } | |
| 47 | ||
| 48 | update() { | |
| 49 | this.isKey = (this.i & 1) === 1 | |
| 50 | this.n = this.i >> 1 | |
| 51 | if (this.n >= (this.isKey ? this.node.keys.length : this.node.children.length)) return false | |
| 52 | const child = this.isKey ? null : this.node.children[this.n] | |
| 53 | this.seq = child !== null ? child.seq : this.node.keys[this.n].seq | |
| 54 | this.offset = child !== null ? child.offset : 0 | |
| 55 | return true | |
| 56 | } | |
| 57 | ||
| 58 | async key() { | |
| 59 | return this.n < this.node.keys.length | |
| 60 | ? this.node.getKey(this.n) | |
| 61 | : this.parent && this.parent.key() | |
| 62 | } | |
| 63 | ||
| 64 | async compare(tree) { | |
| 65 | const [a, b] = await Promise.all([this.key(), tree.key()]) | |
| 66 | return cmp(a, b) | |
| 67 | } | |
| 68 | } | |
| 69 | ||
| 70 | class TreeIterator { | |
| 71 | constructor(batch, opts) { | |
| 72 | this.batch = batch | |
| 73 | this.stack = [] | |
| 74 | this.lt = opts.lt || opts.lte || null | |
| 75 | this.lte = !!opts.lte | |
| 76 | this.gt = opts.gt || opts.gte || null | |
| 77 | this.gte = !!opts.gte | |
| 78 | this.seeking = !!this.gt | |
| 79 | this.encoding = opts.encoding || batch.encoding | |
| 80 | } | |
| 81 | ||
| 82 | async open() { | |
| 83 | const node = await this.batch.getRoot(false) | |
| 84 | if (!node || !node.keys.length) return | |
| 85 | const tree = new SubTree(node, null) | |
| 86 | if (this.seeking && !(await this._seek(tree))) return | |
| 87 | this.stack.push(tree) | |
| 88 | } | |
| 89 | ||
| 90 | async _seek(tree) { | |
| 91 | const done = await tree.bisect(this.gt, this.gte) | |
| 92 | const oob = !tree.update() | |
| 93 | if (done || oob) { | |
| 94 | this.seeking = false | |
| 95 | if (oob) return false | |
| 96 | } | |
| 97 | return true | |
| 98 | } | |
| 99 | ||
| 100 | peek() { | |
| 101 | if (!this.stack.length) return null | |
| 102 | return this.stack[this.stack.length - 1] | |
| 103 | } | |
| 104 | ||
| 105 | skip() { | |
| 106 | if (!this.stack.length) return | |
| 107 | if (!this.stack[this.stack.length - 1].next()) this.stack.pop() | |
| 108 | } | |
| 109 | ||
| 110 | async nextKey() { | |
| 111 | let n = null | |
| 112 | while (this.stack.length && n === null) n = await this.next() | |
| 113 | if (n === null) return null | |
| 114 | if (!this.lt) return n.final(this.encoding) | |
| 115 | ||
| 116 | const c = cmp(n.key, this.lt) | |
| 117 | if (this.lte ? c <= 0 : c < 0) return n.final(this.encoding) | |
| 118 | this.stack = [] | |
| 119 | return null | |
| 120 | } | |
| 121 | ||
| 122 | async next() { | |
| 123 | if (!this.stack.length) return null | |
| 124 | ||
| 125 | const top = this.stack[this.stack.length - 1] | |
| 126 | const { isKey, n, seq } = top | |
| 127 | ||
| 128 | if (!top.next()) { | |
| 129 | this.stack.pop() | |
| 130 | } | |
| 131 | ||
| 132 | if (isKey) { | |
| 133 | this.seeking = false | |
| 134 | return this.batch.getBlock(seq) | |
| 135 | } | |
| 136 | ||
| 137 | const child = await top.node.getChildNode(n) | |
| 138 | top.node.children[n] = null // unlink to save memory | |
| 139 | const tree = new SubTree(child, top) | |
| 140 | if (this.seeking && !(await this._seek(tree))) return null | |
| 141 | this.stack.push(tree) | |
| 142 | ||
| 143 | return null | |
| 144 | } | |
| 145 | ||
| 146 | close() { | |
| 147 | return this.batch._closeSnapshot() | |
| 148 | } | |
| 149 | } | |
| 150 | ||
| 151 | module.exports = class DiffIterator { | |
| 152 | constructor(left, right, opts = {}) { | |
| 153 | this.left = new TreeIterator(left, opts) | |
| 154 | this.right = new TreeIterator(right, opts) | |
| 155 | this.limit = typeof opts.limit === 'number' ? opts.limit : -1 | |
| 156 | } | |
| 157 | ||
| 158 | async open() { | |
| 159 | await Promise.all([this.left.open(), this.right.open()]) | |
| 160 | } | |
| 161 | ||
| 162 | async next() { | |
| 163 | if (this.limit === 0) return null | |
| 164 | const res = await this._next() | |
| 165 | if (!res || (res.left === null && res.right === null)) return null | |
| 166 | this.limit-- | |
| 167 | return res | |
| 168 | } | |
| 169 | ||
| 170 | async _next() { | |
| 171 | const a = this.left | |
| 172 | const b = this.right | |
| 173 | ||
| 174 | while (true) { | |
| 175 | const [l, r] = await Promise.all([a.peek(), b.peek()]) | |
| 176 | ||
| 177 | if (!l && !r) return null | |
| 178 | if (!l) return { left: null, right: await b.nextKey() } | |
| 179 | if (!r) return { left: await a.nextKey(), right: null } | |
| 180 | ||
| 181 | if (l.seq === r.seq && l.isKey === r.isKey && l.offset === r.offset) { | |
| 182 | a.skip() | |
| 183 | b.skip() | |
| 184 | continue | |
| 185 | } | |
| 186 | ||
| 187 | const c = await l.compare(r) | |
| 188 | ||
| 189 | if (l.isKey && !r.isKey) { | |
| 190 | await b.next() | |
| 191 | continue | |
| 192 | } | |
| 193 | ||
| 194 | if (!l.isKey && r.isKey) { | |
| 195 | await a.next() | |
| 196 | continue | |
| 197 | } | |
| 198 | ||
| 199 | if (l.isKey && r.isKey) { | |
| 200 | if (c === 0) return { left: await a.nextKey(), right: await b.nextKey() } | |
| 201 | if (c < 0) return { left: await a.nextKey(), right: null } | |
| 202 | return { left: null, right: await b.nextKey() } | |
| 203 | } | |
| 204 | ||
| 205 | if (c === 0) await Promise.all([a.next(), b.next()]) | |
| 206 | else if (c < 0) await b.next() | |
| 207 | else await a.next() | |
| 208 | } | |
| 209 | } | |
| 210 | ||
| 211 | async close() { | |
| 212 | await Promise.all([this.left.close(), this.right.close()]) | |
| 213 | } | |
| 214 | } | |
| 215 | ||
| 216 | function cmp(a, b) { | |
| 217 | if (!a) return b ? 1 : 0 | |
| 218 | if (!b) return a ? -1 : 0 | |
| 219 | return b4a.compare(a, b) | |
| 220 | } |
| … | … | @@ -0,0 +1,57 @@ |
| 1 | module.exports = class HistoryIterator { | |
| 2 | constructor(batch, opts = {}) { | |
| 3 | this.batch = batch | |
| 4 | this.options = opts | |
| 5 | this.live = !!opts.live | |
| 6 | this.gte = 0 | |
| 7 | this.lt = 0 | |
| 8 | this.reverse = !!opts.reverse | |
| 9 | this.limit = typeof opts.limit === 'number' ? opts.limit : -1 | |
| 10 | this.encoding = opts.encoding || batch.encoding | |
| 11 | if (this.live && this.reverse) { | |
| 12 | throw new Error('Cannot have both live and reverse enabled') | |
| 13 | } | |
| 14 | } | |
| 15 | ||
| 16 | async open() { | |
| 17 | await this.batch.getRoot(false) // does the update dance | |
| 18 | this.gte = gte(this.options, this.batch.version) | |
| 19 | this.lt = this.live ? Infinity : lt(this.options, this.batch.version) | |
| 20 | } | |
| 21 | ||
| 22 | async next() { | |
| 23 | if (this.limit === 0) return null | |
| 24 | if (this.limit > 0) this.limit-- | |
| 25 | ||
| 26 | if (this.gte >= this.lt) return null | |
| 27 | ||
| 28 | if (this.reverse) { | |
| 29 | if (this.lt <= 1) return null | |
| 30 | return final(await this.batch.getBlock(--this.lt), this.encoding) | |
| 31 | } | |
| 32 | ||
| 33 | return final(await this.batch.getBlock(this.gte++), this.encoding) | |
| 34 | } | |
| 35 | ||
| 36 | close() { | |
| 37 | return this.batch._closeSnapshot() | |
| 38 | } | |
| 39 | } | |
| 40 | ||
| 41 | function final(node, encoding) { | |
| 42 | const type = node.isDeletion() ? 'del' : 'put' | |
| 43 | return { type, ...node.final(encoding) } | |
| 44 | } | |
| 45 | ||
| 46 | function gte(opts, version) { | |
| 47 | if (opts.gt) return (opts.gt < 0 ? opts.gt + version : opts.gt) + 1 | |
| 48 | const gte = opts.gte || opts.since || 1 | |
| 49 | return gte < 0 ? gte + version : gte | |
| 50 | } | |
| 51 | ||
| 52 | function lt(opts, version) { | |
| 53 | if (opts.lte === 0 || opts.lt === 0 || opts.end === 0) return 0 | |
| 54 | if (opts.lte) return (opts.lte < 0 ? opts.lte + version : opts.lte) + 1 | |
| 55 | const lt = opts.lt || opts.end || version | |
| 56 | return lt < 0 ? lt + version : lt | |
| 57 | } |
| … | … | @@ -0,0 +1,47 @@ |
| 1 | module.exports = class LocalBlocksIterator { | |
| 2 | constructor(batch, opts = {}) { | |
| 3 | this.batch = batch | |
| 4 | this.options = opts | |
| 5 | this.gte = 0 | |
| 6 | this.lt = 0 | |
| 7 | this.limit = typeof opts.limit === 'number' ? opts.limit : -1 | |
| 8 | } | |
| 9 | ||
| 10 | async open() { | |
| 11 | await this.batch.getRoot(false) // does the update dance | |
| 12 | this.gte = gte(this.options, this.batch.version) | |
| 13 | this.lt = lt(this.options, this.batch.version) | |
| 14 | } | |
| 15 | ||
| 16 | async next() { | |
| 17 | if (this.limit === 0) return null | |
| 18 | if (this.limit > 0) this.limit-- | |
| 19 | ||
| 20 | while (this.gte < this.lt) { | |
| 21 | try { | |
| 22 | return await this.batch.getBlock(this.gte++) | |
| 23 | } catch { | |
| 24 | continue | |
| 25 | } | |
| 26 | } | |
| 27 | ||
| 28 | return null | |
| 29 | } | |
| 30 | ||
| 31 | close() { | |
| 32 | return this.batch._closeSnapshot() | |
| 33 | } | |
| 34 | } | |
| 35 | ||
| 36 | function gte(opts, version) { | |
| 37 | if (opts.gt) return (opts.gt < 0 ? opts.gt + version : opts.gt) + 1 | |
| 38 | const gte = opts.gte || opts.since || 1 | |
| 39 | return gte < 0 ? gte + version : gte | |
| 40 | } | |
| 41 | ||
| 42 | function lt(opts, version) { | |
| 43 | if (opts.lte === 0 || opts.lt === 0 || opts.end === 0) return 0 | |
| 44 | if (opts.lte) return (opts.lte < 0 ? opts.lte + version : opts.lte) + 1 | |
| 45 | const lt = opts.lt || opts.end || version | |
| 46 | return lt < 0 ? lt + version : lt | |
| 47 | } |
| … | … | @@ -0,0 +1,207 @@ |
| 1 | const b4a = require('b4a') | |
| 2 | ||
| 3 | module.exports = class RangeIterator { | |
| 4 | constructor(batch, encoding, opts = {}) { | |
| 5 | this.batch = batch | |
| 6 | this.stack = [] | |
| 7 | this.opened = false | |
| 8 | this.encoding = encoding || batch.encoding | |
| 9 | ||
| 10 | this._limit = typeof opts.limit === 'number' ? opts.limit : -1 | |
| 11 | this._gIncl = !opts.gt | |
| 12 | this._gKey = opts.gt || opts.gte || null | |
| 13 | this._lIncl = !opts.lt | |
| 14 | this._lKey = opts.lt || opts.lte || null | |
| 15 | this._reverse = !!opts.reverse | |
| 16 | this._version = 0 | |
| 17 | this._checkpoint = opts.checkpoint && opts.checkpoint.length ? opts.checkpoint : null | |
| 18 | this._nexting = false | |
| 19 | this._closed = false | |
| 20 | } | |
| 21 | ||
| 22 | snapshot(version = this.batch.version) { | |
| 23 | const checkpoint = [] | |
| 24 | for (const s of this.stack) { | |
| 25 | let { node, i } = s | |
| 26 | if (this._nexting && s === this.stack[this.stack.length - 1]) { | |
| 27 | i = this._reverse ? i + 1 : i - 1 | |
| 28 | } | |
| 29 | if (!node.block) continue | |
| 30 | if (i < 0) continue | |
| 31 | checkpoint.push(node.block.seq, node.offset, i) | |
| 32 | } | |
| 33 | ||
| 34 | return { | |
| 35 | version, | |
| 36 | gte: this._gIncl ? this._gKey : null, | |
| 37 | gt: this._gIncl ? null : this._gKey, | |
| 38 | lte: this._lIncl ? this._lKey : null, | |
| 39 | lt: this._lIncl ? null : this._lKey, | |
| 40 | limit: this._limit, | |
| 41 | reverse: this._reverse, | |
| 42 | ended: this.opened && !checkpoint.length, | |
| 43 | checkpoint: this.opened ? checkpoint : [] | |
| 44 | } | |
| 45 | } | |
| 46 | ||
| 47 | async open() { | |
| 48 | await this._open() | |
| 49 | this.opened = true | |
| 50 | } | |
| 51 | ||
| 52 | async _open() { | |
| 53 | if (this._checkpoint) { | |
| 54 | for (let j = 0; j < this._checkpoint.length; j += 3) { | |
| 55 | const seq = this._checkpoint[j] | |
| 56 | const offset = this._checkpoint[j + 1] | |
| 57 | const i = this._checkpoint[j + 2] | |
| 58 | this.stack.push({ | |
| 59 | node: (await this.batch.getBlock(seq)).getTreeNode(offset), | |
| 60 | i | |
| 61 | }) | |
| 62 | } | |
| 63 | return | |
| 64 | } | |
| 65 | ||
| 66 | this._nexting = true | |
| 67 | ||
| 68 | let node = await this.batch.getRoot(false) | |
| 69 | if (!node) { | |
| 70 | this._nexting = false | |
| 71 | return | |
| 72 | } | |
| 73 | ||
| 74 | const incl = this._reverse ? this._lIncl : this._gIncl | |
| 75 | const start = this._reverse ? this._lKey : this._gKey | |
| 76 | ||
| 77 | if (!start) { | |
| 78 | this.stack.push({ node, i: this._reverse ? node.keys.length << 1 : 0 }) | |
| 79 | this._nexting = false | |
| 80 | this._preloadQuery() | |
| 81 | return | |
| 82 | } | |
| 83 | ||
| 84 | while (true) { | |
| 85 | const entry = { node, i: this._reverse ? node.keys.length << 1 : 0 } | |
| 86 | ||
| 87 | let s = 0 | |
| 88 | let e = node.keys.length | |
| 89 | let c | |
| 90 | ||
| 91 | while (s < e) { | |
| 92 | const mid = (s + e) >> 1 | |
| 93 | c = b4a.compare(start, await node.getKey(mid)) | |
| 94 | ||
| 95 | if (c === 0) { | |
| 96 | if (incl) entry.i = mid * 2 + 1 | |
| 97 | else entry.i = mid * 2 + (this._reverse ? 0 : 2) | |
| 98 | this.stack.push(entry) | |
| 99 | this._nexting = false | |
| 100 | this._preloadQuery() | |
| 101 | return | |
| 102 | } | |
| 103 | ||
| 104 | if (c < 0) e = mid | |
| 105 | else s = mid + 1 | |
| 106 | } | |
| 107 | ||
| 108 | const i = c < 0 ? e : s | |
| 109 | entry.i = 2 * i + (this._reverse ? -1 : 1) | |
| 110 | ||
| 111 | if (entry.i >= 0 && entry.i <= node.keys.length << 1) this.stack.push(entry) | |
| 112 | if (!node.children.length) { | |
| 113 | this._nexting = false | |
| 114 | this._preloadQuery() | |
| 115 | return | |
| 116 | } | |
| 117 | ||
| 118 | node = await node.getChildNode(i) | |
| 119 | } | |
| 120 | } | |
| 121 | ||
| 122 | _preloadQuery() { | |
| 123 | const max = { nodes: 0, max: 2048 } | |
| 124 | ||
| 125 | if (this._limit === -1 && !this._reverse && this.stack.length) { | |
| 126 | for (const s of this.stack) { | |
| 127 | const k = (s.i - (s.i & 1)) / 2 | |
| 128 | this._preload(s.node, k + 1, this._lKey, max).catch(noop) | |
| 129 | } | |
| 130 | } | |
| 131 | } | |
| 132 | ||
| 133 | async _preload(node, i, end, max) { | |
| 134 | for (; i < node.keys.length; i++) { | |
| 135 | const key = node.keys[i] | |
| 136 | const block = await this.batch.getBlock(key.seq) | |
| 137 | if (this._closed) return | |
| 138 | const c = end ? b4a.compare(block.key, end) : -1 | |
| 139 | if (c >= 0 || max.nodes >= max.max || i >= node.children.length) return | |
| 140 | max.nodes++ | |
| 141 | const next = await node.getChildNode(i) | |
| 142 | if (this._closed) return | |
| 143 | this._preload(next, 0, end, max).catch(noop) | |
| 144 | } | |
| 145 | ||
| 146 | if (node.children.length && max.nodes < max.max) { | |
| 147 | const next = await node.getChildNode(node.children.length - 1) | |
| 148 | if (this._closed) return | |
| 149 | this._preload(next, 0, end, max).catch(noop) | |
| 150 | } | |
| 151 | } | |
| 152 | ||
| 153 | async next() { | |
| 154 | // TODO: this nexting flag is only needed if someone asks for a snapshot during | |
| 155 | // a lookup (ie the extension, pretty important...). | |
| 156 | // A better solution would be to refactor this so top.i is incremented eagerly | |
| 157 | // to get the current block instead of the way it is done now (++i vs i++) | |
| 158 | this._nexting = true | |
| 159 | ||
| 160 | const end = this._reverse ? this._gKey : this._lKey | |
| 161 | const incl = this._reverse ? this._gIncl : this._lIncl | |
| 162 | ||
| 163 | while (this.stack.length && (this._limit === -1 || this._limit > 0)) { | |
| 164 | const top = this.stack[this.stack.length - 1] | |
| 165 | const isKey = (top.i & 1) === 1 | |
| 166 | const n = this._reverse ? (top.i < 0 ? top.node.keys.length : top.i-- >> 1) : top.i++ >> 1 | |
| 167 | ||
| 168 | if (!isKey) { | |
| 169 | if (!top.node.children.length) continue | |
| 170 | const node = await top.node.getChildNode(n) | |
| 171 | if (top.node.block.seq < this.batch.core.length) { | |
| 172 | top.node.children[n].value = null // unlink it to save memory | |
| 173 | } | |
| 174 | this.stack.push({ i: this._reverse ? node.keys.length << 1 : 0, node }) | |
| 175 | continue | |
| 176 | } | |
| 177 | ||
| 178 | if (n >= top.node.keys.length) { | |
| 179 | this.stack.pop() | |
| 180 | continue | |
| 181 | } | |
| 182 | ||
| 183 | const key = top.node.keys[n] | |
| 184 | const block = await this.batch.getBlock(key.seq) | |
| 185 | if (end) { | |
| 186 | const c = b4a.compare(block.key, end) | |
| 187 | if (c === 0 ? !incl : this._reverse ? c < 0 : c > 0) { | |
| 188 | this._limit = 0 | |
| 189 | break | |
| 190 | } | |
| 191 | } | |
| 192 | if (this._limit > 0) this._limit-- | |
| 193 | this._nexting = false | |
| 194 | return block.final(this.encoding) | |
| 195 | } | |
| 196 | ||
| 197 | this._nexting = false | |
| 198 | return null | |
| 199 | } | |
| 200 | ||
| 201 | close() { | |
| 202 | this._closed = true | |
| 203 | return this.batch._closeSnapshot() | |
| 204 | } | |
| 205 | } | |
| 206 | ||
| 207 | function noop() {} |
| … | … | @@ -0,0 +1,161 @@ |
| 1 | const { Extension } = require('./messages') | |
| 2 | ||
| 3 | // const MAX_ACTIVE = 32 | |
| 4 | const FLUSH_BATCH = 128 | |
| 5 | const MAX_PASSIVE_BATCH = 2048 | |
| 6 | const MAX_ACTIVE_BATCH = MAX_PASSIVE_BATCH + FLUSH_BATCH | |
| 7 | ||
| 8 | class Batch { | |
| 9 | constructor(outgoing, from) { | |
| 10 | this.blocks = [] | |
| 11 | this.start = 0 | |
| 12 | this.end = 0 | |
| 13 | this.outgoing = outgoing | |
| 14 | this.from = from | |
| 15 | } | |
| 16 | ||
| 17 | push(seq) { | |
| 18 | const len = this.blocks.push(seq) | |
| 19 | if (len === 1 || seq < this.start) this.start = seq | |
| 20 | if (len === 1 || seq >= this.end) this.end = seq + 1 | |
| 21 | if (len >= FLUSH_BATCH) { | |
| 22 | this.send() | |
| 23 | this.clear() | |
| 24 | } | |
| 25 | } | |
| 26 | ||
| 27 | send() { | |
| 28 | if (!this.blocks.length) return | |
| 29 | this.outgoing.send( | |
| 30 | Extension.encode({ | |
| 31 | cache: { blocks: this.blocks, start: this.start, end: this.end } | |
| 32 | }), | |
| 33 | this.from | |
| 34 | ) | |
| 35 | } | |
| 36 | ||
| 37 | clear() { | |
| 38 | this.start = this.end = 0 | |
| 39 | this.blocks = [] | |
| 40 | } | |
| 41 | } | |
| 42 | ||
| 43 | class HyperbeeExtension { | |
| 44 | constructor(db) { | |
| 45 | this.encoding = null | |
| 46 | this.outgoing = null | |
| 47 | this.db = db | |
| 48 | this.active = 0 | |
| 49 | } | |
| 50 | ||
| 51 | get(version, key) { | |
| 52 | this.outgoing.broadcast(Extension.encode({ get: { version, key } })) | |
| 53 | } | |
| 54 | ||
| 55 | iterator(snapshot) { | |
| 56 | if (snapshot.ended) return | |
| 57 | if (snapshot.limit === 0) return | |
| 58 | if (snapshot.limit === -1) snapshot.limit = 0 | |
| 59 | this.outgoing.broadcast(Extension.encode({ iterator: snapshot })) | |
| 60 | } | |
| 61 | ||
| 62 | onmessage(buf, from) { | |
| 63 | // TODO: handle max active extension messages | |
| 64 | // this.active++ | |
| 65 | ||
| 66 | const message = decode(buf) | |
| 67 | if (!message) return | |
| 68 | ||
| 69 | if (message.cache) this.oncache(message.cache, from) | |
| 70 | if (message.get) this.onget(message.get, from) | |
| 71 | if (message.iterator) this.oniterator(message.iterator, from) | |
| 72 | } | |
| 73 | ||
| 74 | oncache(message, from) { | |
| 75 | if (!message.blocks.length) return | |
| 76 | this.db.core.download(message) | |
| 77 | } | |
| 78 | ||
| 79 | onget(message, from) { | |
| 80 | if (!message.version || message.version > this.db.version) return | |
| 81 | ||
| 82 | const b = new Batch(this.outgoing, from) | |
| 83 | const db = this.db.checkout(message.version) | |
| 84 | ||
| 85 | db.get(message.key, { | |
| 86 | extension: false, | |
| 87 | wait: false, | |
| 88 | update: false, | |
| 89 | onseq | |
| 90 | }).then(done, done) | |
| 91 | ||
| 92 | function done() { | |
| 93 | db.close().catch(noop) | |
| 94 | b.send() | |
| 95 | } | |
| 96 | ||
| 97 | function onseq(seq) { | |
| 98 | b.push(seq) | |
| 99 | } | |
| 100 | } | |
| 101 | ||
| 102 | async oniterator(message, from) { | |
| 103 | if (!message.version || message.version > this.db.version) return | |
| 104 | ||
| 105 | const b = new Batch(this.outgoing, from) | |
| 106 | const seqs = new Set() | |
| 107 | ||
| 108 | let skip = message.checkpoint.length | |
| 109 | let work = 0 | |
| 110 | ||
| 111 | const db = this.db.checkout(message.version) | |
| 112 | const ite = db.createRangeIterator({ | |
| 113 | ...message, | |
| 114 | wait: false, | |
| 115 | extension: false, | |
| 116 | update: false, | |
| 117 | limit: message.limit === 0 ? -1 : message.limit, | |
| 118 | onseq(seq) { | |
| 119 | if (skip && skip--) return | |
| 120 | if (seqs.has(seq)) return | |
| 121 | work++ | |
| 122 | seqs.add(seq) | |
| 123 | b.push(seq) | |
| 124 | } | |
| 125 | }) | |
| 126 | ||
| 127 | try { | |
| 128 | await ite.open() | |
| 129 | // eslint-disable-next-line no-unmodified-loop-condition | |
| 130 | while (work < MAX_ACTIVE_BATCH) { | |
| 131 | if (!(await ite.next())) break | |
| 132 | } | |
| 133 | } catch (_) { | |
| 134 | // do nothing | |
| 135 | } finally { | |
| 136 | ite.close().catch(noop) | |
| 137 | db.close().catch(noop) | |
| 138 | b.send() | |
| 139 | } | |
| 140 | } | |
| 141 | ||
| 142 | static register(db) { | |
| 143 | const e = new this(db) | |
| 144 | e.outgoing = db.core.registerExtension('hyperbee', e) | |
| 145 | return e | |
| 146 | } | |
| 147 | } | |
| 148 | ||
| 149 | HyperbeeExtension.BATCH_SIZE = MAX_PASSIVE_BATCH | |
| 150 | ||
| 151 | module.exports = HyperbeeExtension | |
| 152 | ||
| 153 | function decode(buf) { | |
| 154 | try { | |
| 155 | return Extension.decode(buf) | |
| 156 | } catch (err) { | |
| 157 | return null | |
| 158 | } | |
| 159 | } | |
| 160 | ||
| 161 | function noop() {} |
| … | … | @@ -0,0 +1,956 @@ |
| 1 | // This file is auto generated by the protocol-buffers compiler | |
| 2 | ||
| 3 | /* eslint-disable quotes */ | |
| 4 | /* eslint-disable indent */ | |
| 5 | /* eslint-disable no-redeclare */ | |
| 6 | /* eslint-disable camelcase */ | |
| 7 | /* eslint-disable no-var */ | |
| 8 | ||
| 9 | // Remember to `npm install --save protocol-buffers-encodings` | |
| 10 | var encodings = require('protocol-buffers-encodings') | |
| 11 | var b4a = require('b4a') | |
| 12 | var varint = encodings.varint | |
| 13 | var skip = encodings.skip | |
| 14 | ||
| 15 | var YoloIndex = exports.YoloIndex = { | |
| 16 | buffer: true, | |
| 17 | encodingLength: null, | |
| 18 | encode: null, | |
| 19 | decode: null | |
| 20 | } | |
| 21 | ||
| 22 | var Header = exports.Header = { | |
| 23 | buffer: true, | |
| 24 | encodingLength: null, | |
| 25 | encode: null, | |
| 26 | decode: null | |
| 27 | } | |
| 28 | ||
| 29 | var Node = exports.Node = { | |
| 30 | buffer: true, | |
| 31 | encodingLength: null, | |
| 32 | encode: null, | |
| 33 | decode: null | |
| 34 | } | |
| 35 | ||
| 36 | var Extension = exports.Extension = { | |
| 37 | buffer: true, | |
| 38 | encodingLength: null, | |
| 39 | encode: null, | |
| 40 | decode: null | |
| 41 | } | |
| 42 | ||
| 43 | defineYoloIndex() | |
| 44 | defineHeader() | |
| 45 | defineNode() | |
| 46 | defineExtension() | |
| 47 | ||
| 48 | function defineYoloIndex () { | |
| 49 | var Level = YoloIndex.Level = { | |
| 50 | buffer: true, | |
| 51 | encodingLength: null, | |
| 52 | encode: null, | |
| 53 | decode: null | |
| 54 | } | |
| 55 | ||
| 56 | defineLevel() | |
| 57 | ||
| 58 | function defineLevel () { | |
| 59 | Level.encodingLength = encodingLength | |
| 60 | Level.encode = encode | |
| 61 | Level.decode = decode | |
| 62 | ||
| 63 | function encodingLength (obj) { | |
| 64 | var length = 0 | |
| 65 | if (defined(obj.keys)) { | |
| 66 | var packedLen = 0 | |
| 67 | for (var i = 0; i < obj.keys.length; i++) { | |
| 68 | if (!defined(obj.keys[i])) continue | |
| 69 | var len = encodings.varint.encodingLength(obj.keys[i]) | |
| 70 | packedLen += len | |
| 71 | } | |
| 72 | if (packedLen) { | |
| 73 | length += 1 + packedLen + varint.encodingLength(packedLen) | |
| 74 | } | |
| 75 | } | |
| 76 | if (defined(obj.children)) { | |
| 77 | var packedLen = 0 | |
| 78 | for (var i = 0; i < obj.children.length; i++) { | |
| 79 | if (!defined(obj.children[i])) continue | |
| 80 | var len = encodings.varint.encodingLength(obj.children[i]) | |
| 81 | packedLen += len | |
| 82 | } | |
| 83 | if (packedLen) { | |
| 84 | length += 1 + packedLen + varint.encodingLength(packedLen) | |
| 85 | } | |
| 86 | } | |
| 87 | return length | |
| 88 | } | |
| 89 | ||
| 90 | function encode (obj, buf, offset) { | |
| 91 | if (!offset) offset = 0 | |
| 92 | if (!buf) buf = b4a.allocUnsafe(encodingLength(obj)) | |
| 93 | var oldOffset = offset | |
| 94 | if (defined(obj.keys)) { | |
| 95 | var packedLen = 0 | |
| 96 | for (var i = 0; i < obj.keys.length; i++) { | |
| 97 | if (!defined(obj.keys[i])) continue | |
| 98 | packedLen += encodings.varint.encodingLength(obj.keys[i]) | |
| 99 | } | |
| 100 | if (packedLen) { | |
| 101 | buf[offset++] = 10 | |
| 102 | varint.encode(packedLen, buf, offset) | |
| 103 | offset += varint.encode.bytes | |
| 104 | } | |
| 105 | for (var i = 0; i < obj.keys.length; i++) { | |
| 106 | if (!defined(obj.keys[i])) continue | |
| 107 | encodings.varint.encode(obj.keys[i], buf, offset) | |
| 108 | offset += encodings.varint.encode.bytes | |
| 109 | } | |
| 110 | } | |
| 111 | if (defined(obj.children)) { | |
| 112 | var packedLen = 0 | |
| 113 | for (var i = 0; i < obj.children.length; i++) { | |
| 114 | if (!defined(obj.children[i])) continue | |
| 115 | packedLen += encodings.varint.encodingLength(obj.children[i]) | |
| 116 | } | |
| 117 | if (packedLen) { | |
| 118 | buf[offset++] = 18 | |
| 119 | varint.encode(packedLen, buf, offset) | |
| 120 | offset += varint.encode.bytes | |
| 121 | } | |
| 122 | for (var i = 0; i < obj.children.length; i++) { | |
| 123 | if (!defined(obj.children[i])) continue | |
| 124 | encodings.varint.encode(obj.children[i], buf, offset) | |
| 125 | offset += encodings.varint.encode.bytes | |
| 126 | } | |
| 127 | } | |
| 128 | encode.bytes = offset - oldOffset | |
| 129 | return buf | |
| 130 | } | |
| 131 | ||
| 132 | function decode (buf, offset, end) { | |
| 133 | if (!offset) offset = 0 | |
| 134 | if (!end) end = buf.length | |
| 135 | if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") | |
| 136 | var oldOffset = offset | |
| 137 | var obj = { | |
| 138 | keys: [], | |
| 139 | children: [] | |
| 140 | } | |
| 141 | while (true) { | |
| 142 | if (end <= offset) { | |
| 143 | decode.bytes = offset - oldOffset | |
| 144 | return obj | |
| 145 | } | |
| 146 | var prefix = varint.decode(buf, offset) | |
| 147 | offset += varint.decode.bytes | |
| 148 | var tag = prefix >> 3 | |
| 149 | switch (tag) { | |
| 150 | case 1: | |
| 151 | var packedEnd = varint.decode(buf, offset) | |
| 152 | offset += varint.decode.bytes | |
| 153 | packedEnd += offset | |
| 154 | while (offset < packedEnd) { | |
| 155 | obj.keys.push(encodings.varint.decode(buf, offset)) | |
| 156 | offset += encodings.varint.decode.bytes | |
| 157 | } | |
| 158 | break | |
| 159 | case 2: | |
| 160 | var packedEnd = varint.decode(buf, offset) | |
| 161 | offset += varint.decode.bytes | |
| 162 | packedEnd += offset | |
| 163 | while (offset < packedEnd) { | |
| 164 | obj.children.push(encodings.varint.decode(buf, offset)) | |
| 165 | offset += encodings.varint.decode.bytes | |
| 166 | } | |
| 167 | break | |
| 168 | default: | |
| 169 | offset = skip(prefix & 7, buf, offset) | |
| 170 | } | |
| 171 | } | |
| 172 | } | |
| 173 | } | |
| 174 | ||
| 175 | YoloIndex.encodingLength = encodingLength | |
| 176 | YoloIndex.encode = encode | |
| 177 | YoloIndex.decode = decode | |
| 178 | ||
| 179 | function encodingLength (obj) { | |
| 180 | var length = 0 | |
| 181 | if (defined(obj.levels)) { | |
| 182 | for (var i = 0; i < obj.levels.length; i++) { | |
| 183 | if (!defined(obj.levels[i])) continue | |
| 184 | var len = Level.encodingLength(obj.levels[i]) | |
| 185 | length += varint.encodingLength(len) | |
| 186 | length += 1 + len | |
| 187 | } | |
| 188 | } | |
| 189 | return length | |
| 190 | } | |
| 191 | ||
| 192 | function encode (obj, buf, offset) { | |
| 193 | if (!offset) offset = 0 | |
| 194 | if (!buf) buf = b4a.allocUnsafe(encodingLength(obj)) | |
| 195 | var oldOffset = offset | |
| 196 | if (defined(obj.levels)) { | |
| 197 | for (var i = 0; i < obj.levels.length; i++) { | |
| 198 | if (!defined(obj.levels[i])) continue | |
| 199 | buf[offset++] = 10 | |
| 200 | varint.encode(Level.encodingLength(obj.levels[i]), buf, offset) | |
| 201 | offset += varint.encode.bytes | |
| 202 | Level.encode(obj.levels[i], buf, offset) | |
| 203 | offset += Level.encode.bytes | |
| 204 | } | |
| 205 | } | |
| 206 | encode.bytes = offset - oldOffset | |
| 207 | return buf | |
| 208 | } | |
| 209 | ||
| 210 | function decode (buf, offset, end) { | |
| 211 | if (!offset) offset = 0 | |
| 212 | if (!end) end = buf.length | |
| 213 | if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") | |
| 214 | var oldOffset = offset | |
| 215 | var obj = { | |
| 216 | levels: [] | |
| 217 | } | |
| 218 | while (true) { | |
| 219 | if (end <= offset) { | |
| 220 | decode.bytes = offset - oldOffset | |
| 221 | return obj | |
| 222 | } | |
| 223 | var prefix = varint.decode(buf, offset) | |
| 224 | offset += varint.decode.bytes | |
| 225 | var tag = prefix >> 3 | |
| 226 | switch (tag) { | |
| 227 | case 1: | |
| 228 | var len = varint.decode(buf, offset) | |
| 229 | offset += varint.decode.bytes | |
| 230 | obj.levels.push(Level.decode(buf, offset, offset + len)) | |
| 231 | offset += Level.decode.bytes | |
| 232 | break | |
| 233 | default: | |
| 234 | offset = skip(prefix & 7, buf, offset) | |
| 235 | } | |
| 236 | } | |
| 237 | } | |
| 238 | } | |
| 239 | ||
| 240 | function defineHeader () { | |
| 241 | var Metadata = Header.Metadata = { | |
| 242 | buffer: true, | |
| 243 | encodingLength: null, | |
| 244 | encode: null, | |
| 245 | decode: null | |
| 246 | } | |
| 247 | ||
| 248 | defineMetadata() | |
| 249 | ||
| 250 | function defineMetadata () { | |
| 251 | Metadata.encodingLength = encodingLength | |
| 252 | Metadata.encode = encode | |
| 253 | Metadata.decode = decode | |
| 254 | ||
| 255 | function encodingLength (obj) { | |
| 256 | var length = 0 | |
| 257 | if (defined(obj.contentFeed)) { | |
| 258 | var len = encodings.bytes.encodingLength(obj.contentFeed) | |
| 259 | length += 1 + len | |
| 260 | } | |
| 261 | if (defined(obj.userData)) { | |
| 262 | var len = encodings.bytes.encodingLength(obj.userData) | |
| 263 | length += 1 + len | |
| 264 | } | |
| 265 | return length | |
| 266 | } | |
| 267 | ||
| 268 | function encode (obj, buf, offset) { | |
| 269 | if (!offset) offset = 0 | |
| 270 | if (!buf) buf = b4a.allocUnsafe(encodingLength(obj)) | |
| 271 | var oldOffset = offset | |
| 272 | if (defined(obj.contentFeed)) { | |
| 273 | buf[offset++] = 10 | |
| 274 | encodings.bytes.encode(obj.contentFeed, buf, offset) | |
| 275 | offset += encodings.bytes.encode.bytes | |
| 276 | } | |
| 277 | if (defined(obj.userData)) { | |
| 278 | buf[offset++] = 18 | |
| 279 | encodings.bytes.encode(obj.userData, buf, offset) | |
| 280 | offset += encodings.bytes.encode.bytes | |
| 281 | } | |
| 282 | encode.bytes = offset - oldOffset | |
| 283 | return buf | |
| 284 | } | |
| 285 | ||
| 286 | function decode (buf, offset, end) { | |
| 287 | if (!offset) offset = 0 | |
| 288 | if (!end) end = buf.length | |
| 289 | if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") | |
| 290 | var oldOffset = offset | |
| 291 | var obj = { | |
| 292 | contentFeed: null, | |
| 293 | userData: null | |
| 294 | } | |
| 295 | while (true) { | |
| 296 | if (end <= offset) { | |
| 297 | decode.bytes = offset - oldOffset | |
| 298 | return obj | |
| 299 | } | |
| 300 | var prefix = varint.decode(buf, offset) | |
| 301 | offset += varint.decode.bytes | |
| 302 | var tag = prefix >> 3 | |
| 303 | switch (tag) { | |
| 304 | case 1: | |
| 305 | obj.contentFeed = encodings.bytes.decode(buf, offset) | |
| 306 | offset += encodings.bytes.decode.bytes | |
| 307 | break | |
| 308 | case 2: | |
| 309 | obj.userData = encodings.bytes.decode(buf, offset) | |
| 310 | offset += encodings.bytes.decode.bytes | |
| 311 | break | |
| 312 | default: | |
| 313 | offset = skip(prefix & 7, buf, offset) | |
| 314 | } | |
| 315 | } | |
| 316 | } | |
| 317 | } | |
| 318 | ||
| 319 | Header.encodingLength = encodingLength | |
| 320 | Header.encode = encode | |
| 321 | Header.decode = decode | |
| 322 | ||
| 323 | function encodingLength (obj) { | |
| 324 | var length = 0 | |
| 325 | if (!defined(obj.protocol)) throw new Error("protocol is required") | |
| 326 | var len = encodings.string.encodingLength(obj.protocol) | |
| 327 | length += 1 + len | |
| 328 | if (defined(obj.metadata)) { | |
| 329 | var len = Metadata.encodingLength(obj.metadata) | |
| 330 | length += varint.encodingLength(len) | |
| 331 | length += 1 + len | |
| 332 | } | |
| 333 | return length | |
| 334 | } | |
| 335 | ||
| 336 | function encode (obj, buf, offset) { | |
| 337 | if (!offset) offset = 0 | |
| 338 | if (!buf) buf = b4a.allocUnsafe(encodingLength(obj)) | |
| 339 | var oldOffset = offset | |
| 340 | if (!defined(obj.protocol)) throw new Error("protocol is required") | |
| 341 | buf[offset++] = 10 | |
| 342 | encodings.string.encode(obj.protocol, buf, offset) | |
| 343 | offset += encodings.string.encode.bytes | |
| 344 | if (defined(obj.metadata)) { | |
| 345 | buf[offset++] = 18 | |
| 346 | varint.encode(Metadata.encodingLength(obj.metadata), buf, offset) | |
| 347 | offset += varint.encode.bytes | |
| 348 | Metadata.encode(obj.metadata, buf, offset) | |
| 349 | offset += Metadata.encode.bytes | |
| 350 | } | |
| 351 | encode.bytes = offset - oldOffset | |
| 352 | return buf | |
| 353 | } | |
| 354 | ||
| 355 | function decode (buf, offset, end) { | |
| 356 | if (!offset) offset = 0 | |
| 357 | if (!end) end = buf.length | |
| 358 | if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") | |
| 359 | var oldOffset = offset | |
| 360 | var obj = { | |
| 361 | protocol: "", | |
| 362 | metadata: null | |
| 363 | } | |
| 364 | var found0 = false | |
| 365 | while (true) { | |
| 366 | if (end <= offset) { | |
| 367 | if (!found0) throw new Error("Decoded message is not valid") | |
| 368 | decode.bytes = offset - oldOffset | |
| 369 | return obj | |
| 370 | } | |
| 371 | var prefix = varint.decode(buf, offset) | |
| 372 | offset += varint.decode.bytes | |
| 373 | var tag = prefix >> 3 | |
| 374 | switch (tag) { | |
| 375 | case 1: | |
| 376 | obj.protocol = encodings.string.decode(buf, offset) | |
| 377 | offset += encodings.string.decode.bytes | |
| 378 | found0 = true | |
| 379 | break | |
| 380 | case 2: | |
| 381 | var len = varint.decode(buf, offset) | |
| 382 | offset += varint.decode.bytes | |
| 383 | obj.metadata = Metadata.decode(buf, offset, offset + len) | |
| 384 | offset += Metadata.decode.bytes | |
| 385 | break | |
| 386 | default: | |
| 387 | offset = skip(prefix & 7, buf, offset) | |
| 388 | } | |
| 389 | } | |
| 390 | } | |
| 391 | } | |
| 392 | ||
| 393 | function defineNode () { | |
| 394 | Node.encodingLength = encodingLength | |
| 395 | Node.encode = encode | |
| 396 | Node.decode = decode | |
| 397 | ||
| 398 | function encodingLength (obj) { | |
| 399 | var length = 0 | |
| 400 | if (!defined(obj.index)) throw new Error("index is required") | |
| 401 | var len = encodings.bytes.encodingLength(obj.index) | |
| 402 | length += 1 + len | |
| 403 | if (!defined(obj.key)) throw new Error("key is required") | |
| 404 | var len = encodings.bytes.encodingLength(obj.key) | |
| 405 | length += 1 + len | |
| 406 | if (defined(obj.value)) { | |
| 407 | var len = encodings.bytes.encodingLength(obj.value) | |
| 408 | length += 1 + len | |
| 409 | } | |
| 410 | return length | |
| 411 | } | |
| 412 | ||
| 413 | function encode (obj, buf, offset) { | |
| 414 | if (!offset) offset = 0 | |
| 415 | if (!buf) buf = b4a.allocUnsafe(encodingLength(obj)) | |
| 416 | var oldOffset = offset | |
| 417 | if (!defined(obj.index)) throw new Error("index is required") | |
| 418 | buf[offset++] = 10 | |
| 419 | encodings.bytes.encode(obj.index, buf, offset) | |
| 420 | offset += encodings.bytes.encode.bytes | |
| 421 | if (!defined(obj.key)) throw new Error("key is required") | |
| 422 | buf[offset++] = 18 | |
| 423 | encodings.bytes.encode(obj.key, buf, offset) | |
| 424 | offset += encodings.bytes.encode.bytes | |
| 425 | if (defined(obj.value)) { | |
| 426 | buf[offset++] = 26 | |
| 427 | encodings.bytes.encode(obj.value, buf, offset) | |
| 428 | offset += encodings.bytes.encode.bytes | |
| 429 | } | |
| 430 | encode.bytes = offset - oldOffset | |
| 431 | return buf | |
| 432 | } | |
| 433 | ||
| 434 | function decode (buf, offset, end) { | |
| 435 | if (!offset) offset = 0 | |
| 436 | if (!end) end = buf.length | |
| 437 | if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") | |
| 438 | var oldOffset = offset | |
| 439 | var obj = { | |
| 440 | index: null, | |
| 441 | key: null, | |
| 442 | value: null | |
| 443 | } | |
| 444 | var found0 = false | |
| 445 | var found1 = false | |
| 446 | while (true) { | |
| 447 | if (end <= offset) { | |
| 448 | if (!found0 || !found1) throw new Error("Decoded message is not valid") | |
| 449 | decode.bytes = offset - oldOffset | |
| 450 | return obj | |
| 451 | } | |
| 452 | var prefix = varint.decode(buf, offset) | |
| 453 | offset += varint.decode.bytes | |
| 454 | var tag = prefix >> 3 | |
| 455 | switch (tag) { | |
| 456 | case 1: | |
| 457 | obj.index = encodings.bytes.decode(buf, offset) | |
| 458 | offset += encodings.bytes.decode.bytes | |
| 459 | found0 = true | |
| 460 | break | |
| 461 | case 2: | |
| 462 | obj.key = encodings.bytes.decode(buf, offset) | |
| 463 | offset += encodings.bytes.decode.bytes | |
| 464 | found1 = true | |
| 465 | break | |
| 466 | case 3: | |
| 467 | obj.value = encodings.bytes.decode(buf, offset) | |
| 468 | offset += encodings.bytes.decode.bytes | |
| 469 | break | |
| 470 | default: | |
| 471 | offset = skip(prefix & 7, buf, offset) | |
| 472 | } | |
| 473 | } | |
| 474 | } | |
| 475 | } | |
| 476 | ||
| 477 | function defineExtension () { | |
| 478 | var Get = Extension.Get = { | |
| 479 | buffer: true, | |
| 480 | encodingLength: null, | |
| 481 | encode: null, | |
| 482 | decode: null | |
| 483 | } | |
| 484 | ||
| 485 | var Iterator = Extension.Iterator = { | |
| 486 | buffer: true, | |
| 487 | encodingLength: null, | |
| 488 | encode: null, | |
| 489 | decode: null | |
| 490 | } | |
| 491 | ||
| 492 | var Cache = Extension.Cache = { | |
| 493 | buffer: true, | |
| 494 | encodingLength: null, | |
| 495 | encode: null, | |
| 496 | decode: null | |
| 497 | } | |
| 498 | ||
| 499 | defineGet() | |
| 500 | defineIterator() | |
| 501 | defineCache() | |
| 502 | ||
| 503 | function defineGet () { | |
| 504 | Get.encodingLength = encodingLength | |
| 505 | Get.encode = encode | |
| 506 | Get.decode = decode | |
| 507 | ||
| 508 | function encodingLength (obj) { | |
| 509 | var length = 0 | |
| 510 | if (defined(obj.version)) { | |
| 511 | var len = encodings.varint.encodingLength(obj.version) | |
| 512 | length += 1 + len | |
| 513 | } | |
| 514 | if (defined(obj.key)) { | |
| 515 | var len = encodings.bytes.encodingLength(obj.key) | |
| 516 | length += 1 + len | |
| 517 | } | |
| 518 | return length | |
| 519 | } | |
| 520 | ||
| 521 | function encode (obj, buf, offset) { | |
| 522 | if (!offset) offset = 0 | |
| 523 | if (!buf) buf = b4a.allocUnsafe(encodingLength(obj)) | |
| 524 | var oldOffset = offset | |
| 525 | if (defined(obj.version)) { | |
| 526 | buf[offset++] = 8 | |
| 527 | encodings.varint.encode(obj.version, buf, offset) | |
| 528 | offset += encodings.varint.encode.bytes | |
| 529 | } | |
| 530 | if (defined(obj.key)) { | |
| 531 | buf[offset++] = 18 | |
| 532 | encodings.bytes.encode(obj.key, buf, offset) | |
| 533 | offset += encodings.bytes.encode.bytes | |
| 534 | } | |
| 535 | encode.bytes = offset - oldOffset | |
| 536 | return buf | |
| 537 | } | |
| 538 | ||
| 539 | function decode (buf, offset, end) { | |
| 540 | if (!offset) offset = 0 | |
| 541 | if (!end) end = buf.length | |
| 542 | if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") | |
| 543 | var oldOffset = offset | |
| 544 | var obj = { | |
| 545 | version: 0, | |
| 546 | key: null | |
| 547 | } | |
| 548 | while (true) { | |
| 549 | if (end <= offset) { | |
| 550 | decode.bytes = offset - oldOffset | |
| 551 | return obj | |
| 552 | } | |
| 553 | var prefix = varint.decode(buf, offset) | |
| 554 | offset += varint.decode.bytes | |
| 555 | var tag = prefix >> 3 | |
| 556 | switch (tag) { | |
| 557 | case 1: | |
| 558 | obj.version = encodings.varint.decode(buf, offset) | |
| 559 | offset += encodings.varint.decode.bytes | |
| 560 | break | |
| 561 | case 2: | |
| 562 | obj.key = encodings.bytes.decode(buf, offset) | |
| 563 | offset += encodings.bytes.decode.bytes | |
| 564 | break | |
| 565 | default: | |
| 566 | offset = skip(prefix & 7, buf, offset) | |
| 567 | } | |
| 568 | } | |
| 569 | } | |
| 570 | } | |
| 571 | ||
| 572 | function defineIterator () { | |
| 573 | Iterator.encodingLength = encodingLength | |
| 574 | Iterator.encode = encode | |
| 575 | Iterator.decode = decode | |
| 576 | ||
| 577 | function encodingLength (obj) { | |
| 578 | var length = 0 | |
| 579 | if (defined(obj.version)) { | |
| 580 | var len = encodings.varint.encodingLength(obj.version) | |
| 581 | length += 1 + len | |
| 582 | } | |
| 583 | if (defined(obj.gte)) { | |
| 584 | var len = encodings.bytes.encodingLength(obj.gte) | |
| 585 | length += 1 + len | |
| 586 | } | |
| 587 | if (defined(obj.gt)) { | |
| 588 | var len = encodings.bytes.encodingLength(obj.gt) | |
| 589 | length += 1 + len | |
| 590 | } | |
| 591 | if (defined(obj.lte)) { | |
| 592 | var len = encodings.bytes.encodingLength(obj.lte) | |
| 593 | length += 1 + len | |
| 594 | } | |
| 595 | if (defined(obj.lt)) { | |
| 596 | var len = encodings.bytes.encodingLength(obj.lt) | |
| 597 | length += 1 + len | |
| 598 | } | |
| 599 | if (defined(obj.limit)) { | |
| 600 | var len = encodings.varint.encodingLength(obj.limit) | |
| 601 | length += 1 + len | |
| 602 | } | |
| 603 | if (defined(obj.reverse)) { | |
| 604 | var len = encodings.bool.encodingLength(obj.reverse) | |
| 605 | length += 1 + len | |
| 606 | } | |
| 607 | if (defined(obj.checkpoint)) { | |
| 608 | var packedLen = 0 | |
| 609 | for (var i = 0; i < obj.checkpoint.length; i++) { | |
| 610 | if (!defined(obj.checkpoint[i])) continue | |
| 611 | var len = encodings.varint.encodingLength(obj.checkpoint[i]) | |
| 612 | packedLen += len | |
| 613 | } | |
| 614 | if (packedLen) { | |
| 615 | length += 1 + packedLen + varint.encodingLength(packedLen) | |
| 616 | } | |
| 617 | } | |
| 618 | return length | |
| 619 | } | |
| 620 | ||
| 621 | function encode (obj, buf, offset) { | |
| 622 | if (!offset) offset = 0 | |
| 623 | if (!buf) buf = b4a.allocUnsafe(encodingLength(obj)) | |
| 624 | var oldOffset = offset | |
| 625 | if (defined(obj.version)) { | |
| 626 | buf[offset++] = 8 | |
| 627 | encodings.varint.encode(obj.version, buf, offset) | |
| 628 | offset += encodings.varint.encode.bytes | |
| 629 | } | |
| 630 | if (defined(obj.gte)) { | |
| 631 | buf[offset++] = 18 | |
| 632 | encodings.bytes.encode(obj.gte, buf, offset) | |
| 633 | offset += encodings.bytes.encode.bytes | |
| 634 | } | |
| 635 | if (defined(obj.gt)) { | |
| 636 | buf[offset++] = 26 | |
| 637 | encodings.bytes.encode(obj.gt, buf, offset) | |
| 638 | offset += encodings.bytes.encode.bytes | |
| 639 | } | |
| 640 | if (defined(obj.lte)) { | |
| 641 | buf[offset++] = 34 | |
| 642 | encodings.bytes.encode(obj.lte, buf, offset) | |
| 643 | offset += encodings.bytes.encode.bytes | |
| 644 | } | |
| 645 | if (defined(obj.lt)) { | |
| 646 | buf[offset++] = 42 | |
| 647 | encodings.bytes.encode(obj.lt, buf, offset) | |
| 648 | offset += encodings.bytes.encode.bytes | |
| 649 | } | |
| 650 | if (defined(obj.limit)) { | |
| 651 | buf[offset++] = 48 | |
| 652 | encodings.varint.encode(obj.limit, buf, offset) | |
| 653 | offset += encodings.varint.encode.bytes | |
| 654 | } | |
| 655 | if (defined(obj.reverse)) { | |
| 656 | buf[offset++] = 56 | |
| 657 | encodings.bool.encode(obj.reverse, buf, offset) | |
| 658 | offset += encodings.bool.encode.bytes | |
| 659 | } | |
| 660 | if (defined(obj.checkpoint)) { | |
| 661 | var packedLen = 0 | |
| 662 | for (var i = 0; i < obj.checkpoint.length; i++) { | |
| 663 | if (!defined(obj.checkpoint[i])) continue | |
| 664 | packedLen += encodings.varint.encodingLength(obj.checkpoint[i]) | |
| 665 | } | |
| 666 | if (packedLen) { | |
| 667 | buf[offset++] = 66 | |
| 668 | varint.encode(packedLen, buf, offset) | |
| 669 | offset += varint.encode.bytes | |
| 670 | } | |
| 671 | for (var i = 0; i < obj.checkpoint.length; i++) { | |
| 672 | if (!defined(obj.checkpoint[i])) continue | |
| 673 | encodings.varint.encode(obj.checkpoint[i], buf, offset) | |
| 674 | offset += encodings.varint.encode.bytes | |
| 675 | } | |
| 676 | } | |
| 677 | encode.bytes = offset - oldOffset | |
| 678 | return buf | |
| 679 | } | |
| 680 | ||
| 681 | function decode (buf, offset, end) { | |
| 682 | if (!offset) offset = 0 | |
| 683 | if (!end) end = buf.length | |
| 684 | if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") | |
| 685 | var oldOffset = offset | |
| 686 | var obj = { | |
| 687 | version: 0, | |
| 688 | gte: null, | |
| 689 | gt: null, | |
| 690 | lte: null, | |
| 691 | lt: null, | |
| 692 | limit: 0, | |
| 693 | reverse: false, | |
| 694 | checkpoint: [] | |
| 695 | } | |
| 696 | while (true) { | |
| 697 | if (end <= offset) { | |
| 698 | decode.bytes = offset - oldOffset | |
| 699 | return obj | |
| 700 | } | |
| 701 | var prefix = varint.decode(buf, offset) | |
| 702 | offset += varint.decode.bytes | |
| 703 | var tag = prefix >> 3 | |
| 704 | switch (tag) { | |
| 705 | case 1: | |
| 706 | obj.version = encodings.varint.decode(buf, offset) | |
| 707 | offset += encodings.varint.decode.bytes | |
| 708 | break | |
| 709 | case 2: | |
| 710 | obj.gte = encodings.bytes.decode(buf, offset) | |
| 711 | offset += encodings.bytes.decode.bytes | |
| 712 | break | |
| 713 | case 3: | |
| 714 | obj.gt = encodings.bytes.decode(buf, offset) | |
| 715 | offset += encodings.bytes.decode.bytes | |
| 716 | break | |
| 717 | case 4: | |
| 718 | obj.lte = encodings.bytes.decode(buf, offset) | |
| 719 | offset += encodings.bytes.decode.bytes | |
| 720 | break | |
| 721 | case 5: | |
| 722 | obj.lt = encodings.bytes.decode(buf, offset) | |
| 723 | offset += encodings.bytes.decode.bytes | |
| 724 | break | |
| 725 | case 6: | |
| 726 | obj.limit = encodings.varint.decode(buf, offset) | |
| 727 | offset += encodings.varint.decode.bytes | |
| 728 | break | |
| 729 | case 7: | |
| 730 | obj.reverse = encodings.bool.decode(buf, offset) | |
| 731 | offset += encodings.bool.decode.bytes | |
| 732 | break | |
| 733 | case 8: | |
| 734 | var packedEnd = varint.decode(buf, offset) | |
| 735 | offset += varint.decode.bytes | |
| 736 | packedEnd += offset | |
| 737 | while (offset < packedEnd) { | |
| 738 | obj.checkpoint.push(encodings.varint.decode(buf, offset)) | |
| 739 | offset += encodings.varint.decode.bytes | |
| 740 | } | |
| 741 | break | |
| 742 | default: | |
| 743 | offset = skip(prefix & 7, buf, offset) | |
| 744 | } | |
| 745 | } | |
| 746 | } | |
| 747 | } | |
| 748 | ||
| 749 | function defineCache () { | |
| 750 | Cache.encodingLength = encodingLength | |
| 751 | Cache.encode = encode | |
| 752 | Cache.decode = decode | |
| 753 | ||
| 754 | function encodingLength (obj) { | |
| 755 | var length = 0 | |
| 756 | if (!defined(obj.start)) throw new Error("start is required") | |
| 757 | var len = encodings.varint.encodingLength(obj.start) | |
| 758 | length += 1 + len | |
| 759 | if (!defined(obj.end)) throw new Error("end is required") | |
| 760 | var len = encodings.varint.encodingLength(obj.end) | |
| 761 | length += 1 + len | |
| 762 | if (defined(obj.blocks)) { | |
| 763 | var packedLen = 0 | |
| 764 | for (var i = 0; i < obj.blocks.length; i++) { | |
| 765 | if (!defined(obj.blocks[i])) continue | |
| 766 | var len = encodings.varint.encodingLength(obj.blocks[i]) | |
| 767 | packedLen += len | |
| 768 | } | |
| 769 | if (packedLen) { | |
| 770 | length += 1 + packedLen + varint.encodingLength(packedLen) | |
| 771 | } | |
| 772 | } | |
| 773 | return length | |
| 774 | } | |
| 775 | ||
| 776 | function encode (obj, buf, offset) { | |
| 777 | if (!offset) offset = 0 | |
| 778 | if (!buf) buf = b4a.allocUnsafe(encodingLength(obj)) | |
| 779 | var oldOffset = offset | |
| 780 | if (!defined(obj.start)) throw new Error("start is required") | |
| 781 | buf[offset++] = 8 | |
| 782 | encodings.varint.encode(obj.start, buf, offset) | |
| 783 | offset += encodings.varint.encode.bytes | |
| 784 | if (!defined(obj.end)) throw new Error("end is required") | |
| 785 | buf[offset++] = 16 | |
| 786 | encodings.varint.encode(obj.end, buf, offset) | |
| 787 | offset += encodings.varint.encode.bytes | |
| 788 | if (defined(obj.blocks)) { | |
| 789 | var packedLen = 0 | |
| 790 | for (var i = 0; i < obj.blocks.length; i++) { | |
| 791 | if (!defined(obj.blocks[i])) continue | |
| 792 | packedLen += encodings.varint.encodingLength(obj.blocks[i]) | |
| 793 | } | |
| 794 | if (packedLen) { | |
| 795 | buf[offset++] = 26 | |
| 796 | varint.encode(packedLen, buf, offset) | |
| 797 | offset += varint.encode.bytes | |
| 798 | } | |
| 799 | for (var i = 0; i < obj.blocks.length; i++) { | |
| 800 | if (!defined(obj.blocks[i])) continue | |
| 801 | encodings.varint.encode(obj.blocks[i], buf, offset) | |
| 802 | offset += encodings.varint.encode.bytes | |
| 803 | } | |
| 804 | } | |
| 805 | encode.bytes = offset - oldOffset | |
| 806 | return buf | |
| 807 | } | |
| 808 | ||
| 809 | function decode (buf, offset, end) { | |
| 810 | if (!offset) offset = 0 | |
| 811 | if (!end) end = buf.length | |
| 812 | if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") | |
| 813 | var oldOffset = offset | |
| 814 | var obj = { | |
| 815 | start: 0, | |
| 816 | end: 0, | |
| 817 | blocks: [] | |
| 818 | } | |
| 819 | var found0 = false | |
| 820 | var found1 = false | |
| 821 | while (true) { | |
| 822 | if (end <= offset) { | |
| 823 | if (!found0 || !found1) throw new Error("Decoded message is not valid") | |
| 824 | decode.bytes = offset - oldOffset | |
| 825 | return obj | |
| 826 | } | |
| 827 | var prefix = varint.decode(buf, offset) | |
| 828 | offset += varint.decode.bytes | |
| 829 | var tag = prefix >> 3 | |
| 830 | switch (tag) { | |
| 831 | case 1: | |
| 832 | obj.start = encodings.varint.decode(buf, offset) | |
| 833 | offset += encodings.varint.decode.bytes | |
| 834 | found0 = true | |
| 835 | break | |
| 836 | case 2: | |
| 837 | obj.end = encodings.varint.decode(buf, offset) | |
| 838 | offset += encodings.varint.decode.bytes | |
| 839 | found1 = true | |
| 840 | break | |
| 841 | case 3: | |
| 842 | var packedEnd = varint.decode(buf, offset) | |
| 843 | offset += varint.decode.bytes | |
| 844 | packedEnd += offset | |
| 845 | while (offset < packedEnd) { | |
| 846 | obj.blocks.push(encodings.varint.decode(buf, offset)) | |
| 847 | offset += encodings.varint.decode.bytes | |
| 848 | } | |
| 849 | break | |
| 850 | default: | |
| 851 | offset = skip(prefix & 7, buf, offset) | |
| 852 | } | |
| 853 | } | |
| 854 | } | |
| 855 | } | |
| 856 | ||
| 857 | Extension.encodingLength = encodingLength | |
| 858 | Extension.encode = encode | |
| 859 | Extension.decode = decode | |
| 860 | ||
| 861 | function encodingLength (obj) { | |
| 862 | var length = 0 | |
| 863 | if (defined(obj.cache)) { | |
| 864 | var len = Cache.encodingLength(obj.cache) | |
| 865 | length += varint.encodingLength(len) | |
| 866 | length += 1 + len | |
| 867 | } | |
| 868 | if (defined(obj.get)) { | |
| 869 | var len = Get.encodingLength(obj.get) | |
| 870 | length += varint.encodingLength(len) | |
| 871 | length += 1 + len | |
| 872 | } | |
| 873 | if (defined(obj.iterator)) { | |
| 874 | var len = Iterator.encodingLength(obj.iterator) | |
| 875 | length += varint.encodingLength(len) | |
| 876 | length += 1 + len | |
| 877 | } | |
| 878 | return length | |
| 879 | } | |
| 880 | ||
| 881 | function encode (obj, buf, offset) { | |
| 882 | if (!offset) offset = 0 | |
| 883 | if (!buf) buf = b4a.allocUnsafe(encodingLength(obj)) | |
| 884 | var oldOffset = offset | |
| 885 | if (defined(obj.cache)) { | |
| 886 | buf[offset++] = 10 | |
| 887 | varint.encode(Cache.encodingLength(obj.cache), buf, offset) | |
| 888 | offset += varint.encode.bytes | |
| 889 | Cache.encode(obj.cache, buf, offset) | |
| 890 | offset += Cache.encode.bytes | |
| 891 | } | |
| 892 | if (defined(obj.get)) { | |
| 893 | buf[offset++] = 18 | |
| 894 | varint.encode(Get.encodingLength(obj.get), buf, offset) | |
| 895 | offset += varint.encode.bytes | |
| 896 | Get.encode(obj.get, buf, offset) | |
| 897 | offset += Get.encode.bytes | |
| 898 | } | |
| 899 | if (defined(obj.iterator)) { | |
| 900 | buf[offset++] = 26 | |
| 901 | varint.encode(Iterator.encodingLength(obj.iterator), buf, offset) | |
| 902 | offset += varint.encode.bytes | |
| 903 | Iterator.encode(obj.iterator, buf, offset) | |
| 904 | offset += Iterator.encode.bytes | |
| 905 | } | |
| 906 | encode.bytes = offset - oldOffset | |
| 907 | return buf | |
| 908 | } | |
| 909 | ||
| 910 | function decode (buf, offset, end) { | |
| 911 | if (!offset) offset = 0 | |
| 912 | if (!end) end = buf.length | |
| 913 | if (!(end <= buf.length && offset <= buf.length)) throw new Error("Decoded message is not valid") | |
| 914 | var oldOffset = offset | |
| 915 | var obj = { | |
| 916 | cache: null, | |
| 917 | get: null, | |
| 918 | iterator: null | |
| 919 | } | |
| 920 | while (true) { | |
| 921 | if (end <= offset) { | |
| 922 | decode.bytes = offset - oldOffset | |
| 923 | return obj | |
| 924 | } | |
| 925 | var prefix = varint.decode(buf, offset) | |
| 926 | offset += varint.decode.bytes | |
| 927 | var tag = prefix >> 3 | |
| 928 | switch (tag) { | |
| 929 | case 1: | |
| 930 | var len = varint.decode(buf, offset) | |
| 931 | offset += varint.decode.bytes | |
| 932 | obj.cache = Cache.decode(buf, offset, offset + len) | |
| 933 | offset += Cache.decode.bytes | |
| 934 | break | |
| 935 | case 2: | |
| 936 | var len = varint.decode(buf, offset) | |
| 937 | offset += varint.decode.bytes | |
| 938 | obj.get = Get.decode(buf, offset, offset + len) | |
| 939 | offset += Get.decode.bytes | |
| 940 | break | |
| 941 | case 3: | |
| 942 | var len = varint.decode(buf, offset) | |
| 943 | offset += varint.decode.bytes | |
| 944 | obj.iterator = Iterator.decode(buf, offset, offset + len) | |
| 945 | offset += Iterator.decode.bytes | |
| 946 | break | |
| 947 | default: | |
| 948 | offset = skip(prefix & 7, buf, offset) | |
| 949 | } | |
| 950 | } | |
| 951 | } | |
| 952 | } | |
| 953 | ||
| 954 | function defined (val) { | |
| 955 | return val !== null && val !== undefined && (typeof val !== 'number' || !isNaN(val)) | |
| 956 | } |
| … | … | @@ -0,0 +1,60 @@ |
| 1 | { | |
| 2 | "name": "hyperbee", | |
| 3 | "version": "2.27.3", | |
| 4 | "description": "An append-only B-tree running on a Hypercore.", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js", | |
| 8 | "lib/**.js", | |
| 9 | "iterators/**.js" | |
| 10 | ], | |
| 11 | "dependencies": { | |
| 12 | "b4a": "^1.6.0", | |
| 13 | "codecs": "^3.0.0", | |
| 14 | "debounceify": "^1.0.0", | |
| 15 | "hypercore-errors": "^1.0.0", | |
| 16 | "mutexify": "^1.4.0", | |
| 17 | "protocol-buffers-encodings": "^1.2.0", | |
| 18 | "rache": "^1.0.0", | |
| 19 | "ready-resource": "^1.0.0", | |
| 20 | "resolve-reject-promise": "^1.1.0", | |
| 21 | "safety-catch": "^1.0.2", | |
| 22 | "streamx": "^2.12.4", | |
| 23 | "unslab": "^1.2.0" | |
| 24 | }, | |
| 25 | "devDependencies": { | |
| 26 | "bare-process": "^4.2.2", | |
| 27 | "brittle": "^3.1.0", | |
| 28 | "hypercore": "^11.0.0", | |
| 29 | "lunte": "^1.0.0", | |
| 30 | "prettier": "^3.6.2", | |
| 31 | "prettier-config-holepunch": "^2.0.0", | |
| 32 | "protocol-buffers": "^4.2.0", | |
| 33 | "sub-encoder": "^1.0.6", | |
| 34 | "tree-to-string": "^1.1.1" | |
| 35 | }, | |
| 36 | "imports": { | |
| 37 | "process": { | |
| 38 | "bare": "bare-process", | |
| 39 | "default": "process" | |
| 40 | } | |
| 41 | }, | |
| 42 | "scripts": { | |
| 43 | "format": "prettier --write .", | |
| 44 | "lint": "prettier --check . && lunte", | |
| 45 | "test": "npm run test:node && npm run test:bare", | |
| 46 | "test:node": "brittle-node test/*.js", | |
| 47 | "test:bare": "brittle-bare test/*.js", | |
| 48 | "protobuf": "protocol-buffers schema.proto -o ./lib/messages.js" | |
| 49 | }, | |
| 50 | "repository": { | |
| 51 | "type": "git", | |
| 52 | "url": "https://github.com/holepunchto/hyperbee.git" | |
| 53 | }, | |
| 54 | "author": "Mathias Buus (@mafintosh)", | |
| 55 | "license": "MIT", | |
| 56 | "bugs": { | |
| 57 | "url": "https://github.com/holepunchto/hyperbee/issues" | |
| 58 | }, | |
| 59 | "homepage": "https://github.com/holepunchto/hyperbee" | |
| 60 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,101 @@ |
| 1 | # hyperblobs | |
| 2 | ||
| 3 | A simple blob store for Hypercore. | |
| 4 | ||
| 5 | Each blob is identified by its unique bounds within the Hypercore, e.g. `{ byteOffset: 0, blockOffset: 0, blockLength: 5, byteLength: 327680 }`, which makes them easy to save and retrieve: | |
| 6 | ||
| 7 | ```js | |
| 8 | const blobs = new Hyperblobs(core) | |
| 9 | // ID is { byteOffset: 0, blockOffset: 0, blockLength: 1, byteLength: 11 } | |
| 10 | const id = await blobs.put(Buffer.from('hello world', 'utf-8')) | |
| 11 | await blobs.get(id) // Buffer.from('hello world', 'utf-8') | |
| 12 | ``` | |
| 13 | ||
| 14 | You can also get from start/end bounds within a single blob: | |
| 15 | ||
| 16 | ```js | |
| 17 | const blobs = new Hyperblobs(core) | |
| 18 | // ID is { byteOffset: 0, blockOffset: 0, blockLength: 1, byteLength: 11 } | |
| 19 | const id = await blobs.put(Buffer.from('hello world', 'utf-8')) | |
| 20 | await blobs.get(id, { start: 1, length: 2 }) // Buffer.from('el', 'utf-8') | |
| 21 | ``` | |
| 22 | ||
| 23 | If the blob is large, there's a Streams interface (`createReadStream` and `createWriteStream`) too. | |
| 24 | ||
| 25 | ## Installation | |
| 26 | ||
| 27 | ``` | |
| 28 | npm i hyperblobs | |
| 29 | ``` | |
| 30 | ||
| 31 | ## API | |
| 32 | ||
| 33 | ```js | |
| 34 | const Hyperblobs = require('hyperblobs') | |
| 35 | ``` | |
| 36 | ||
| 37 | #### `const blobs = new Hyperblobs(core, opts)` | |
| 38 | ||
| 39 | Create a new blob store wrapping a single Hypercore. | |
| 40 | ||
| 41 | Options can include: | |
| 42 | ||
| 43 | ```js | |
| 44 | { | |
| 45 | blockSize: 64KB // The block size that will be used when storing large blobs. | |
| 46 | } | |
| 47 | ``` | |
| 48 | ||
| 49 | #### `const id = await blobs.put(blob, opts)` | |
| 50 | ||
| 51 | Store a new blob. If the blob is large, it will be chunked according to `opts.blockSize` (default 64KB). | |
| 52 | ||
| 53 | Options can include: | |
| 54 | ||
| 55 | ```js | |
| 56 | { | |
| 57 | blockSize: 64KB, // The block size that will be used when storing large blobs. | |
| 58 | start: 0, // Relative offset to start within the blob | |
| 59 | end: blob.length - 1, // End offset within the blob (inclusive) | |
| 60 | length: blob.length, // Number of bytes to read. | |
| 61 | core // A custom core to write (overrides the default core) | |
| 62 | } | |
| 63 | ``` | |
| 64 | ||
| 65 | #### `const content = await blobs.get(id, opts)` | |
| 66 | ||
| 67 | Return a complete blob as a `Buffer`. | |
| 68 | ||
| 69 | `id` is the value returned by `put` | |
| 70 | ||
| 71 | Options can include: | |
| 72 | ||
| 73 | ```js | |
| 74 | { | |
| 75 | core, // A custom core to read from (overrides the default core) | |
| 76 | wait: true, // Wait for block to be downloaded | |
| 77 | timeout: 0 // Wait at max some milliseconds (0 means no timeout) | |
| 78 | } | |
| 79 | ``` | |
| 80 | ||
| 81 | #### `await blobs.clear(id, opts)` | |
| 82 | ||
| 83 | Remove a blob from the core. | |
| 84 | ||
| 85 | `opts` are the same as `Hypercore.clear` method. | |
| 86 | ||
| 87 | #### `const stream = blobs.createReadStream(id, opts)` | |
| 88 | ||
| 89 | Create a Readable stream that will yield the `id` blob. | |
| 90 | ||
| 91 | Options match the `get` options. | |
| 92 | ||
| 93 | #### `const stream = blobs.createWriteStream(opts)` | |
| 94 | ||
| 95 | Create a Writable stream that will save a blob. | |
| 96 | ||
| 97 | The corresponding ID will be set on the stream at `stream.id`. | |
| 98 | ||
| 99 | ## License | |
| 100 | ||
| 101 | Apache-2.0 |
| … | … | @@ -0,0 +1,241 @@ |
| 1 | const mutexify = require('mutexify') | |
| 2 | const b4a = require('b4a') | |
| 3 | ||
| 4 | const { BlockMapReadStream, BlobReadStream, BlobWriteStream } = require('./lib/streams') | |
| 5 | const Monitor = require('./lib/monitor') | |
| 6 | const blockMap = require('./lib/block-map') | |
| 7 | ||
| 8 | const DEFAULT_BLOCK_SIZE = 2 ** 16 | |
| 9 | ||
| 10 | class HyperBlobsBatch { | |
| 11 | constructor(blobs) { | |
| 12 | this.blobs = blobs | |
| 13 | this.blocks = [] | |
| 14 | this.bytes = 0 | |
| 15 | } | |
| 16 | ||
| 17 | ready() { | |
| 18 | return this.blobs.ready() | |
| 19 | } | |
| 20 | ||
| 21 | async put(buffer) { | |
| 22 | if (!this.blobs.core.opened) await this.blobs.core.ready() | |
| 23 | ||
| 24 | const blockSize = this.blobs.blockSize | |
| 25 | const result = { | |
| 26 | blockOffset: this.blobs.core.length + this.blocks.length, | |
| 27 | blockLength: 0, | |
| 28 | byteOffset: this.blobs.core.byteLength + this.bytes, | |
| 29 | byteLength: 0 | |
| 30 | } | |
| 31 | ||
| 32 | let offset = 0 | |
| 33 | while (offset < buffer.byteLength) { | |
| 34 | const blk = buffer.subarray(offset, offset + blockSize) | |
| 35 | offset += blockSize | |
| 36 | ||
| 37 | result.blockLength++ | |
| 38 | result.byteLength += blk.byteLength | |
| 39 | this.bytes += blk.byteLength | |
| 40 | this.blocks.push(blk) | |
| 41 | } | |
| 42 | ||
| 43 | return result | |
| 44 | } | |
| 45 | ||
| 46 | async get(id) { | |
| 47 | if (id.blockOffset < this.blobs.core.length) { | |
| 48 | return this.blobs.get(id) | |
| 49 | } | |
| 50 | ||
| 51 | const bufs = [] | |
| 52 | ||
| 53 | for ( | |
| 54 | let i = id.blockOffset - this.blobs.core.length; | |
| 55 | i < id.blockOffset + id.blockLength; | |
| 56 | i++ | |
| 57 | ) { | |
| 58 | if (i >= this.blocks.length) return null | |
| 59 | bufs.push(this.blocks[i]) | |
| 60 | } | |
| 61 | ||
| 62 | return bufs.length === 1 ? bufs[0] : b4a.concat(bufs) | |
| 63 | } | |
| 64 | ||
| 65 | async flush() { | |
| 66 | await this.blobs.core.append(this.blocks) | |
| 67 | this.blocks = [] | |
| 68 | this.bytes = 0 | |
| 69 | } | |
| 70 | ||
| 71 | close() { | |
| 72 | // noop, atm nothing to unlink | |
| 73 | } | |
| 74 | } | |
| 75 | ||
| 76 | class Hyperblobs { | |
| 77 | constructor(core, opts = {}) { | |
| 78 | this.core = core | |
| 79 | this.blockSize = opts.blockSize || DEFAULT_BLOCK_SIZE | |
| 80 | ||
| 81 | this._lock = mutexify() | |
| 82 | this._monitors = new Set() | |
| 83 | ||
| 84 | this._boundUpdatePeers = this._updatePeers.bind(this) | |
| 85 | this._boundOnUpload = this._onUpload.bind(this) | |
| 86 | this._boundOnDownload = this._onDownload.bind(this) | |
| 87 | } | |
| 88 | ||
| 89 | get key() { | |
| 90 | return this.core.key | |
| 91 | } | |
| 92 | ||
| 93 | get discoveryKey() { | |
| 94 | return this.core.discoveryKey | |
| 95 | } | |
| 96 | ||
| 97 | get feed() { | |
| 98 | return this.core | |
| 99 | } | |
| 100 | ||
| 101 | get locked() { | |
| 102 | return this._lock.locked | |
| 103 | } | |
| 104 | ||
| 105 | replicate(isInitiator, opts) { | |
| 106 | return this.core.replicate(isInitiator, opts) | |
| 107 | } | |
| 108 | ||
| 109 | ready() { | |
| 110 | return this.core.ready() | |
| 111 | } | |
| 112 | ||
| 113 | close() { | |
| 114 | return this.core.close() | |
| 115 | } | |
| 116 | ||
| 117 | batch() { | |
| 118 | return new HyperBlobsBatch(this) | |
| 119 | } | |
| 120 | ||
| 121 | snapshot() { | |
| 122 | return new Hyperblobs(this.core.snapshot()) | |
| 123 | } | |
| 124 | ||
| 125 | async put(blob, opts) { | |
| 126 | if (!b4a.isBuffer(blob)) blob = b4a.from(blob) | |
| 127 | const blockSize = (opts && opts.blockSize) || this.blockSize | |
| 128 | ||
| 129 | const stream = this.createWriteStream(opts) | |
| 130 | for (let i = 0; i < blob.length; i += blockSize) { | |
| 131 | stream.write(blob.subarray(i, i + blockSize)) | |
| 132 | } | |
| 133 | stream.end() | |
| 134 | ||
| 135 | return new Promise((resolve, reject) => { | |
| 136 | stream.once('error', reject) | |
| 137 | stream.once('close', () => resolve(stream.id)) | |
| 138 | }) | |
| 139 | } | |
| 140 | ||
| 141 | async _getAll(id, opts) { | |
| 142 | if (id.blockLength === 1) return this.core.get(id.blockOffset, opts) | |
| 143 | ||
| 144 | const promises = new Array(id.blockLength) | |
| 145 | for (let i = 0; i < id.blockLength; i++) { | |
| 146 | promises[i] = this.core.get(id.blockOffset + i, opts) | |
| 147 | } | |
| 148 | ||
| 149 | const blocks = await Promise.all(promises) | |
| 150 | for (let i = 0; i < id.blockLength; i++) { | |
| 151 | if (blocks[i] === null) return null | |
| 152 | } | |
| 153 | return b4a.concat(blocks) | |
| 154 | } | |
| 155 | ||
| 156 | async get(id, opts) { | |
| 157 | if (isAll(id, opts)) return this._getAll(id, opts) | |
| 158 | ||
| 159 | const res = [] | |
| 160 | try { | |
| 161 | for await (const block of this.createReadStream(id, opts)) { | |
| 162 | res.push(block) | |
| 163 | } | |
| 164 | } catch (error) { | |
| 165 | if (error.code === 'BLOCK_NOT_AVAILABLE') return null | |
| 166 | throw error | |
| 167 | } | |
| 168 | ||
| 169 | if (res.length === 1) return res[0] | |
| 170 | return b4a.concat(res) | |
| 171 | } | |
| 172 | ||
| 173 | async clear(id, opts) { | |
| 174 | if (id.blockMap) { | |
| 175 | const map = await blockMap.get(this.core, id, { wait: false }) | |
| 176 | if (map) { | |
| 177 | for (const b of map.blocks) await this.core.clear(b.index, b.index + 1, opts) | |
| 178 | } | |
| 179 | } | |
| 180 | return this.core.clear(id.blockOffset, id.blockOffset + id.blockLength, opts) | |
| 181 | } | |
| 182 | ||
| 183 | createReadStream(id, opts) { | |
| 184 | const core = opts && opts.core ? opts.core : this.core | |
| 185 | return id.blockMap ? new BlockMapReadStream(core, id, opts) : new BlobReadStream(core, id, opts) | |
| 186 | } | |
| 187 | ||
| 188 | createWriteStream(opts) { | |
| 189 | const core = opts && opts.core ? opts.core : this.core | |
| 190 | return new BlobWriteStream(core, this._lock, opts) | |
| 191 | } | |
| 192 | ||
| 193 | monitor(id) { | |
| 194 | const monitor = new Monitor(this, id) | |
| 195 | if (this._monitors.size === 0) this._startListening() | |
| 196 | this._monitors.add(monitor) | |
| 197 | return monitor | |
| 198 | } | |
| 199 | ||
| 200 | _removeMonitor(mon) { | |
| 201 | this._monitors.delete(mon) | |
| 202 | if (this._monitors.size === 0) this._stopListening() | |
| 203 | } | |
| 204 | ||
| 205 | _updatePeers() { | |
| 206 | for (const m of this._monitors) m._updatePeers() | |
| 207 | } | |
| 208 | ||
| 209 | _onUpload(index, bytes, from) { | |
| 210 | for (const m of this._monitors) m._onUpload(index, bytes, from) | |
| 211 | } | |
| 212 | ||
| 213 | _onDownload(index, bytes, from) { | |
| 214 | for (const m of this._monitors) m._onDownload(index, bytes, from) | |
| 215 | } | |
| 216 | ||
| 217 | _startListening() { | |
| 218 | this.core.on('peer-add', this._boundUpdatePeers) | |
| 219 | this.core.on('peer-remove', this._boundUpdatePeers) | |
| 220 | this.core.on('upload', this._boundOnUpload) | |
| 221 | this.core.on('download', this._boundOnDownload) | |
| 222 | } | |
| 223 | ||
| 224 | _stopListening() { | |
| 225 | this.core.off('peer-add', this._boundUpdatePeers) | |
| 226 | this.core.off('peer-remove', this._boundUpdatePeers) | |
| 227 | this.core.off('upload', this._boundOnUpload) | |
| 228 | this.core.off('download', this._boundOnDownload) | |
| 229 | } | |
| 230 | } | |
| 231 | ||
| 232 | module.exports = Hyperblobs | |
| 233 | ||
| 234 | function isAll(id, opts) { | |
| 235 | if (id.blockMap) return false | |
| 236 | if (!opts) return true | |
| 237 | if (opts.start) return false | |
| 238 | if (opts.length !== undefined || opts.end !== undefined) return false | |
| 239 | if (opts.core) return false | |
| 240 | return true | |
| 241 | } |
| … | … | @@ -0,0 +1,157 @@ |
| 1 | const c = require('compact-encoding') | |
| 2 | const b4a = require('b4a') | |
| 3 | const crypto = require('hypercore-crypto') | |
| 4 | ||
| 5 | const block = { | |
| 6 | preencode(state, m) { | |
| 7 | c.uint.preencode(state, m.index) | |
| 8 | c.uint.preencode(state, m.byteLength) | |
| 9 | }, | |
| 10 | encode(state, m) { | |
| 11 | c.uint.encode(state, m.index) | |
| 12 | c.uint.encode(state, m.byteLength) | |
| 13 | }, | |
| 14 | decode(state) { | |
| 15 | return { | |
| 16 | index: c.uint.decode(state), | |
| 17 | byteLength: c.uint.decode(state) | |
| 18 | } | |
| 19 | } | |
| 20 | } | |
| 21 | ||
| 22 | const list = c.array(block) | |
| 23 | ||
| 24 | const map = { | |
| 25 | preencode(state, m) { | |
| 26 | c.uint.preencode(state, 0) | |
| 27 | list.preencode(state, m.blocks) | |
| 28 | }, | |
| 29 | encode(state, m) { | |
| 30 | c.uint.encode(state, 0) | |
| 31 | list.encode(state, m.blocks) | |
| 32 | }, | |
| 33 | decode(state) { | |
| 34 | const version = c.uint.decode(state) | |
| 35 | if (version > 0) throw new Error('Unsupported block map version') | |
| 36 | ||
| 37 | return { | |
| 38 | version, | |
| 39 | blocks: list.decode(state) | |
| 40 | } | |
| 41 | } | |
| 42 | } | |
| 43 | ||
| 44 | exports.hash = hashId | |
| 45 | ||
| 46 | function hashId(block) { | |
| 47 | return b4a.toString(crypto.hash(block), 'hex') | |
| 48 | } | |
| 49 | ||
| 50 | exports.get = getBlockMap | |
| 51 | ||
| 52 | async function inferBlockMap(core, id, opts = {}) { | |
| 53 | const hashes = !!opts.hashes | |
| 54 | ||
| 55 | const map = { | |
| 56 | hashes: hashes ? new Map() : null, | |
| 57 | blocks: [] | |
| 58 | } | |
| 59 | ||
| 60 | for (let i = id.blockOffset; i < id.blockOffset + id.blockLength; i++) { | |
| 61 | const block = await core.get(i) | |
| 62 | const entry = { index: i, byteLength: block.byteLength } | |
| 63 | map.blocks.push(entry) | |
| 64 | if (hashes) map.hashes.set(hashId(block), entry) | |
| 65 | } | |
| 66 | ||
| 67 | return map | |
| 68 | } | |
| 69 | ||
| 70 | async function getBlockMap(core, id, opts = {}) { | |
| 71 | if (!id.blockMap) return inferBlockMap(core, id, opts) | |
| 72 | ||
| 73 | if (id.blockLength > 64) { | |
| 74 | throw new Error('Block map is too large') | |
| 75 | } | |
| 76 | ||
| 77 | const hashes = !!opts.hashes | |
| 78 | ||
| 79 | const map = { | |
| 80 | hashes: hashes ? new Map() : null, | |
| 81 | blocks: null | |
| 82 | } | |
| 83 | ||
| 84 | const promises = [] | |
| 85 | ||
| 86 | for (let i = id.blockOffset; i < id.blockOffset + id.blockLength; i++) { | |
| 87 | promises.push(core.get(i)) | |
| 88 | } | |
| 89 | ||
| 90 | const buffers = await Promise.all(promises) | |
| 91 | const m = decodeBlockMap(buffers) | |
| 92 | if (!m) return null | |
| 93 | ||
| 94 | map.blocks = m.blocks | |
| 95 | ||
| 96 | if (hashes && !core.writable) { | |
| 97 | const blocks = [] | |
| 98 | for (let i = 0; i < map.blocks.length; i++) blocks.push(map.blocks[i].index) | |
| 99 | core.download({ blocks }) | |
| 100 | } | |
| 101 | ||
| 102 | if (hashes) { | |
| 103 | for (let i = 0; i < map.blocks.length; i++) { | |
| 104 | const b = map.blocks[i] | |
| 105 | const block = await core.get(b.index) | |
| 106 | if (block === null) return null | |
| 107 | map.hashes.set(hashId(block), b) | |
| 108 | } | |
| 109 | } | |
| 110 | ||
| 111 | return map | |
| 112 | } | |
| 113 | ||
| 114 | exports.encode = encodeBlockMap | |
| 115 | ||
| 116 | function encodeBlockMap(header) { | |
| 117 | const result = [] | |
| 118 | ||
| 119 | for (let i = 0; i < header.blocks.length; i += 8192) { | |
| 120 | const blocks = | |
| 121 | i === 0 && header.blocks.length < 8192 ? header.blocks : header.blocks.slice(i, i + 8192) | |
| 122 | ||
| 123 | const state = { start: 0, end: 0, buffer: null } | |
| 124 | const m = { version: 0, blocks } | |
| 125 | ||
| 126 | map.preencode(state, m) | |
| 127 | state.buffer = b4a.allocUnsafe(state.end) | |
| 128 | map.encode(state, m) | |
| 129 | ||
| 130 | result.push(state.buffer) | |
| 131 | } | |
| 132 | ||
| 133 | return result | |
| 134 | } | |
| 135 | ||
| 136 | exports.decode = decodeBlockMap | |
| 137 | ||
| 138 | function decodeBlockMap(buffers) { | |
| 139 | const result = { | |
| 140 | version: 0, | |
| 141 | blocks: null | |
| 142 | } | |
| 143 | ||
| 144 | for (let i = 0; i < buffers.length; i++) { | |
| 145 | if (!buffers[i]) return null | |
| 146 | ||
| 147 | const state = { start: 0, end: buffers[i].byteLength, buffer: buffers[i] } | |
| 148 | const r = map.decode(state) | |
| 149 | ||
| 150 | result.version = r.version | |
| 151 | ||
| 152 | if (result.blocks) result.blocks.push(...r.blocks) | |
| 153 | else result.blocks = r.blocks | |
| 154 | } | |
| 155 | ||
| 156 | return result | |
| 157 | } |
| … | … | @@ -0,0 +1,92 @@ |
| 1 | const EventEmitter = require('events') | |
| 2 | const speedometer = require('speedometer') | |
| 3 | ||
| 4 | module.exports = class Monitor extends EventEmitter { | |
| 5 | constructor(blobs, id) { | |
| 6 | super() | |
| 7 | ||
| 8 | if (!id) throw new Error('id is required') | |
| 9 | ||
| 10 | this.blobs = blobs | |
| 11 | this.id = id | |
| 12 | this.peers = 0 | |
| 13 | this.uploadSpeedometer = null | |
| 14 | this.downloadSpeedometer = null | |
| 15 | ||
| 16 | const stats = { | |
| 17 | startTime: 0, | |
| 18 | percentage: 0, | |
| 19 | peers: 0, | |
| 20 | speed: 0, | |
| 21 | blocks: 0, | |
| 22 | totalBytes: 0, // local + bytes loaded during monitoring | |
| 23 | monitoringBytes: 0, // bytes loaded during monitoring | |
| 24 | targetBytes: 0, | |
| 25 | targetBlocks: 0 | |
| 26 | } | |
| 27 | ||
| 28 | this.uploadStats = { ...stats } | |
| 29 | this.downloadStats = { ...stats } | |
| 30 | this.uploadStats.targetBytes = this.downloadStats.targetBytes = this.id.byteLength | |
| 31 | this.uploadStats.targetBlocks = this.downloadStats.targetBlocks = this.id.blockLength | |
| 32 | this.uploadStats.peers = this.downloadStats.peers = this.peers = this.blobs.core.peers.length | |
| 33 | ||
| 34 | this.uploadSpeedometer = speedometer() | |
| 35 | this.downloadSpeedometer = speedometer() | |
| 36 | ||
| 37 | // Handlers | |
| 38 | } | |
| 39 | ||
| 40 | // just an alias | |
| 41 | destroy() { | |
| 42 | return this.close() | |
| 43 | } | |
| 44 | ||
| 45 | close() { | |
| 46 | this.blobs._removeMonitor(this) | |
| 47 | } | |
| 48 | ||
| 49 | _onUpload(index, bytes, from) { | |
| 50 | this._updateStats(this.uploadSpeedometer, this.uploadStats, index, bytes, from) | |
| 51 | } | |
| 52 | ||
| 53 | _onDownload(index, bytes, from) { | |
| 54 | this._updateStats(this.downloadSpeedometer, this.downloadStats, index, bytes, from) | |
| 55 | } | |
| 56 | ||
| 57 | _updatePeers() { | |
| 58 | this.uploadStats.peers = this.downloadStats.peers = this.peers = this.blobs.core.peers.length | |
| 59 | this.emit('update') | |
| 60 | } | |
| 61 | ||
| 62 | _updateStats(speed, stats, index, bytes) { | |
| 63 | if (this.closing) return | |
| 64 | if (!isWithinRange(index, this.id)) return | |
| 65 | ||
| 66 | if (!stats.startTime) stats.startTime = Date.now() | |
| 67 | ||
| 68 | stats.speed = speed(bytes) | |
| 69 | stats.blocks++ | |
| 70 | stats.totalBytes += bytes | |
| 71 | stats.monitoringBytes += bytes | |
| 72 | stats.percentage = toFixed((stats.blocks / stats.targetBlocks) * 100) | |
| 73 | ||
| 74 | this.emit('update') | |
| 75 | } | |
| 76 | ||
| 77 | downloadSpeed() { | |
| 78 | return this.downloadSpeedometer ? this.downloadSpeedometer() : 0 | |
| 79 | } | |
| 80 | ||
| 81 | uploadSpeed() { | |
| 82 | return this.uploadSpeedometer ? this.uploadSpeedometer() : 0 | |
| 83 | } | |
| 84 | } | |
| 85 | ||
| 86 | function isWithinRange(index, { blockOffset, blockLength }) { | |
| 87 | return index >= blockOffset && index < blockOffset + blockLength | |
| 88 | } | |
| 89 | ||
| 90 | function toFixed(n) { | |
| 91 | return Math.round(n * 100) / 100 | |
| 92 | } |
| … | … | @@ -0,0 +1,67 @@ |
| 1 | // should move to hypercore itself | |
| 2 | ||
| 3 | const MAX_READAHEAD_TARGET = 0.05 // aim to buffer 5% always | |
| 4 | ||
| 5 | module.exports = class Prefetcher { | |
| 6 | constructor(core, { max = 64, start = 0, end = core.length, linear = true } = {}) { | |
| 7 | this.core = core | |
| 8 | this.max = max | |
| 9 | this.range = null | |
| 10 | this.startBound = start | |
| 11 | this.endBound = end | |
| 12 | this.maxReadAhead = Math.max(max * 2, Math.floor((end - start) * MAX_READAHEAD_TARGET)) | |
| 13 | ||
| 14 | this.start = start | |
| 15 | this.end = start | |
| 16 | this.linear = linear | |
| 17 | this.missing = 0 | |
| 18 | ||
| 19 | this._ondownloadBound = this._ondownload.bind(this) | |
| 20 | this.core.on('download', this._ondownloadBound) | |
| 21 | } | |
| 22 | ||
| 23 | _ondownload(index) { | |
| 24 | if (this.range && index < this.end && this.start <= index) { | |
| 25 | this.missing-- | |
| 26 | this._update() | |
| 27 | } | |
| 28 | } | |
| 29 | ||
| 30 | destroy() { | |
| 31 | this.core.off('download', this._ondownloadBound) | |
| 32 | if (this.range) this.range.destroy() | |
| 33 | this.range = null | |
| 34 | this.max = 0 | |
| 35 | } | |
| 36 | ||
| 37 | update(position) { | |
| 38 | this.start = position | |
| 39 | if (!this.range) this._update() | |
| 40 | } | |
| 41 | ||
| 42 | _update() { | |
| 43 | if (this.missing >= this.max) return | |
| 44 | if (this.range) this.range.destroy() | |
| 45 | ||
| 46 | let end = this.end | |
| 47 | ||
| 48 | while (end < this.endBound && this.missing < this.max) { | |
| 49 | end = this.core.core.bitfield.firstUnset(end) + 1 | |
| 50 | if (end >= this.endBound) break | |
| 51 | this.missing++ | |
| 52 | } | |
| 53 | ||
| 54 | if (end > this.start + this.maxReadAhead) end = this.start + this.maxReadAhead | |
| 55 | if (end >= this.endBound) end = this.endBound | |
| 56 | ||
| 57 | this.end = end | |
| 58 | ||
| 59 | if (this.start >= this.end) return | |
| 60 | ||
| 61 | this.range = this.core.download({ | |
| 62 | start: this.start, | |
| 63 | end: this.end, | |
| 64 | linear: this.linear | |
| 65 | }) | |
| 66 | } | |
| 67 | } |
| … | … | @@ -0,0 +1,343 @@ |
| 1 | const { Readable, Writable } = require('streamx') | |
| 2 | const { BLOCK_NOT_AVAILABLE } = require('hypercore-errors') | |
| 3 | const Prefetcher = require('./prefetcher') | |
| 4 | const blockMap = require('./block-map') | |
| 5 | ||
| 6 | class BlobWriteStream extends Writable { | |
| 7 | constructor(core, lock, opts = {}) { | |
| 8 | super(opts) | |
| 9 | this.id = { blockOffset: 0, byteOffset: 0, blockLength: 0, byteLength: 0 } | |
| 10 | this.core = core | |
| 11 | ||
| 12 | this._dedup = !!opts.dedup | |
| 13 | this._blob = opts.blob || null | |
| 14 | this._hashes = null | |
| 15 | this._addBlockMap = !!opts.blockMap || this._dedup | |
| 16 | this._blockMap = this._addBlockMap ? { version: 0, blocks: [] } : null | |
| 17 | this._lock = lock | |
| 18 | this._release = null | |
| 19 | this._batch = [] | |
| 20 | ||
| 21 | if (this._addBlockMap) this.id.blockMap = true | |
| 22 | } | |
| 23 | ||
| 24 | async _openp() { | |
| 25 | await this.core.ready() | |
| 26 | const release = await new Promise((resolve) => this._lock(resolve)) | |
| 27 | ||
| 28 | this._release = release | |
| 29 | this.id.byteOffset = this.core.byteLength | |
| 30 | this.id.blockOffset = this.core.length | |
| 31 | ||
| 32 | if (!this._dedup) return | |
| 33 | ||
| 34 | if (this._blob) { | |
| 35 | const map = await blockMap.get(this.core, this._blob, { hashes: true }) | |
| 36 | this._hashes = map.hashes | |
| 37 | } else { | |
| 38 | this._hashes = new Map() | |
| 39 | } | |
| 40 | } | |
| 41 | ||
| 42 | _open(cb) { | |
| 43 | this._openp().then(cb, cb) | |
| 44 | } | |
| 45 | ||
| 46 | async _finalp() { | |
| 47 | await this._append() | |
| 48 | ||
| 49 | if (this._blockMap) { | |
| 50 | const buffers = await blockMap.encode(this._blockMap) | |
| 51 | this.id.blockOffset = this.core.length | |
| 52 | this.id.byteOffset = this.core.byteLength | |
| 53 | await this.core.append(buffers) | |
| 54 | } | |
| 55 | ||
| 56 | this.id.blockLength = this.core.length - this.id.blockOffset | |
| 57 | this.id.byteLength = this.core.byteLength - this.id.byteOffset | |
| 58 | } | |
| 59 | ||
| 60 | _final(cb) { | |
| 61 | this._finalp().then(cb, cb) | |
| 62 | } | |
| 63 | ||
| 64 | _destroy(cb) { | |
| 65 | if (this._release) this._release() | |
| 66 | cb(null) | |
| 67 | } | |
| 68 | ||
| 69 | async _append() { | |
| 70 | if (!this._batch.length) return | |
| 71 | ||
| 72 | const batch = this._batch | |
| 73 | this._batch = [] | |
| 74 | ||
| 75 | await this.core.append(batch) | |
| 76 | } | |
| 77 | ||
| 78 | _write(data, cb) { | |
| 79 | let dup = false | |
| 80 | ||
| 81 | if (this._blockMap) { | |
| 82 | let entry = { | |
| 83 | index: this.core.length + this._batch.length, | |
| 84 | byteLength: data.byteLength | |
| 85 | } | |
| 86 | ||
| 87 | if (this._hashes) { | |
| 88 | const id = blockMap.hash(data) | |
| 89 | const existing = this._hashes.get(id) | |
| 90 | ||
| 91 | if (existing) { | |
| 92 | entry = existing | |
| 93 | dup = true | |
| 94 | } else { | |
| 95 | this._hashes.set(id, entry) | |
| 96 | } | |
| 97 | } | |
| 98 | ||
| 99 | this._blockMap.blocks.push(entry) | |
| 100 | } | |
| 101 | ||
| 102 | if (dup) return cb() | |
| 103 | ||
| 104 | this._batch.push(data) | |
| 105 | ||
| 106 | if (this._batch.length >= 16) { | |
| 107 | this._append().then(cb, cb) | |
| 108 | return | |
| 109 | } | |
| 110 | ||
| 111 | return cb() | |
| 112 | } | |
| 113 | } | |
| 114 | ||
| 115 | class BlockMapReadStream extends Readable { | |
| 116 | constructor(core, id, opts = {}) { | |
| 117 | super(opts) | |
| 118 | this.id = id | |
| 119 | this.core = core.session({ wait: opts.wait, timeout: opts.timeout }) | |
| 120 | ||
| 121 | const noPrefetch = opts.wait === false || opts.prefetch === false || !core.core | |
| 122 | const start = opts.start || 0 | |
| 123 | const end = | |
| 124 | opts.end === undefined ? (opts.length === undefined ? -1 : start + opts.length) : opts.end + 1 | |
| 125 | ||
| 126 | this._blockMap = null | |
| 127 | ||
| 128 | this._rangeStart = start | |
| 129 | this._rangeEnd = end | |
| 130 | ||
| 131 | this._startIndex = 0 | |
| 132 | this._startOffset = 0 | |
| 133 | this._endIndex = -1 | |
| 134 | this._endOffset = -1 | |
| 135 | ||
| 136 | this._range = null | |
| 137 | this._noPrefetch = noPrefetch | |
| 138 | } | |
| 139 | ||
| 140 | async _openp() { | |
| 141 | this._blockMap = await blockMap.get(this.core, this.id) | |
| 142 | ||
| 143 | const [startIndex, startOffset, endIndex, endLength] = seekBlockMap( | |
| 144 | this._blockMap, | |
| 145 | this._rangeStart, | |
| 146 | this._rangeEnd | |
| 147 | ) | |
| 148 | ||
| 149 | this._startIndex = startIndex | |
| 150 | this._startOffset = startOffset | |
| 151 | this._endIndex = endIndex | |
| 152 | this._endOffset = endLength | |
| 153 | ||
| 154 | if (this._endIndex === -1) { | |
| 155 | this._endIndex = this._blockMap.blocks.length | |
| 156 | this._endOffset = 0 | |
| 157 | } | |
| 158 | } | |
| 159 | ||
| 160 | _open(cb) { | |
| 161 | this._openp().then(cb, cb) | |
| 162 | } | |
| 163 | ||
| 164 | _predestroy() { | |
| 165 | if (this._range) this._range.destroy() | |
| 166 | this.core.close().then(noop, noop) | |
| 167 | } | |
| 168 | ||
| 169 | _destroy(cb) { | |
| 170 | if (this._range) this._range.destroy() | |
| 171 | this.core.close().then(cb, cb) | |
| 172 | } | |
| 173 | ||
| 174 | _prefetch(index) { | |
| 175 | const blocks = [] | |
| 176 | for (; index < this._endIndex; index++) blocks.push(this._blockMap.blocks[index]) | |
| 177 | this._range = this.core.download({ blocks }) | |
| 178 | } | |
| 179 | ||
| 180 | async _readp() { | |
| 181 | if (this._startIndex >= this._endIndex) { | |
| 182 | this.push(null) | |
| 183 | return | |
| 184 | } | |
| 185 | ||
| 186 | let block = null | |
| 187 | ||
| 188 | const index = this._startIndex++ | |
| 189 | const b = this._blockMap.blocks[index] | |
| 190 | ||
| 191 | if (!this._range && !this._noPrefetch) { | |
| 192 | block = await this.core.get(b.index, { wait: false }) | |
| 193 | if (!block) this._prefetch(index) | |
| 194 | } | |
| 195 | ||
| 196 | if (!block) { | |
| 197 | block = await this.core.get(b.index) | |
| 198 | } | |
| 199 | ||
| 200 | if (!block) throw BLOCK_NOT_AVAILABLE() | |
| 201 | ||
| 202 | if (this._startOffset) { | |
| 203 | block = block.subarray(this._startOffset) | |
| 204 | this._startOffset = 0 | |
| 205 | } | |
| 206 | ||
| 207 | if (this._startIndex === this._endIndex && this._endOffset) { | |
| 208 | block = block.subarray(0, block.byteLength - this._endOffset) | |
| 209 | } | |
| 210 | ||
| 211 | this.push(block) | |
| 212 | } | |
| 213 | ||
| 214 | _read(cb) { | |
| 215 | this._readp().then(cb, cb) | |
| 216 | } | |
| 217 | } | |
| 218 | ||
| 219 | class BlobReadStream extends Readable { | |
| 220 | constructor(core, id, opts = {}) { | |
| 221 | super(opts) | |
| 222 | this.id = id | |
| 223 | this.core = core.session({ wait: opts.wait, timeout: opts.timeout }) | |
| 224 | ||
| 225 | const start = id.blockOffset | |
| 226 | const end = id.blockOffset + id.blockLength | |
| 227 | const noPrefetch = opts.wait === false || opts.prefetch === false || !core.core | |
| 228 | ||
| 229 | this._prefetch = noPrefetch | |
| 230 | ? null | |
| 231 | : new Prefetcher(this.core, { max: opts.prefetch, start, end }) | |
| 232 | this._lastPrefetch = null | |
| 233 | ||
| 234 | this._pos = opts.start !== undefined ? id.byteOffset + opts.start : id.byteOffset | |
| 235 | ||
| 236 | if (opts.length !== undefined) this._end = this._pos + opts.length | |
| 237 | else if (opts.end !== undefined) this._end = id.byteOffset + opts.end + 1 | |
| 238 | else this._end = id.byteOffset + id.byteLength | |
| 239 | ||
| 240 | this._index = 0 | |
| 241 | this._relativeOffset = 0 | |
| 242 | this._bytesRead = 0 | |
| 243 | } | |
| 244 | ||
| 245 | async _openp() { | |
| 246 | if (this._pos === this.id.byteOffset) { | |
| 247 | this._index = this.id.blockOffset | |
| 248 | this._relativeOffset = 0 | |
| 249 | return | |
| 250 | } | |
| 251 | ||
| 252 | const result = await this.core.seek(this._pos, { | |
| 253 | start: this.id.blockOffset, | |
| 254 | end: this.id.blockOffset + this.id.blockLength | |
| 255 | }) | |
| 256 | ||
| 257 | if (!result) throw BLOCK_NOT_AVAILABLE() | |
| 258 | ||
| 259 | this._index = result[0] | |
| 260 | this._relativeOffset = result[1] | |
| 261 | } | |
| 262 | ||
| 263 | _open(cb) { | |
| 264 | this._openp().then(cb, cb) | |
| 265 | } | |
| 266 | ||
| 267 | _predestroy() { | |
| 268 | if (this._prefetch) this._prefetch.destroy() | |
| 269 | this.core.close().then(noop, noop) | |
| 270 | } | |
| 271 | ||
| 272 | _destroy(cb) { | |
| 273 | if (this._prefetch) this._prefetch.destroy() | |
| 274 | this.core.close().then(cb, cb) | |
| 275 | } | |
| 276 | ||
| 277 | async _readp() { | |
| 278 | if (this._pos >= this._end) { | |
| 279 | this.push(null) | |
| 280 | return | |
| 281 | } | |
| 282 | ||
| 283 | if (this._prefetch) this._prefetch.update(this._index) | |
| 284 | ||
| 285 | let block = await this.core.get(this._index) | |
| 286 | if (!block) throw BLOCK_NOT_AVAILABLE() | |
| 287 | ||
| 288 | const remainder = this._end - this._pos | |
| 289 | if (this._relativeOffset || remainder < block.length) { | |
| 290 | block = block.subarray(this._relativeOffset, this._relativeOffset + remainder) | |
| 291 | } | |
| 292 | ||
| 293 | this._index++ | |
| 294 | this._relativeOffset = 0 | |
| 295 | this._pos += block.length | |
| 296 | this._bytesRead += block.length | |
| 297 | ||
| 298 | this.push(block) | |
| 299 | } | |
| 300 | ||
| 301 | _read(cb) { | |
| 302 | this._readp().then(cb, cb) | |
| 303 | } | |
| 304 | } | |
| 305 | ||
| 306 | module.exports = { | |
| 307 | BlockMapReadStream, | |
| 308 | BlobReadStream, | |
| 309 | BlobWriteStream | |
| 310 | } | |
| 311 | ||
| 312 | function noop() {} | |
| 313 | ||
| 314 | function seekBlockMap(map, start, end) { | |
| 315 | let s = -1 | |
| 316 | let so = -1 | |
| 317 | let e = -1 | |
| 318 | let eo = -1 | |
| 319 | ||
| 320 | for (let i = 0; i < map.blocks.length; i++) { | |
| 321 | const b = map.blocks[i] | |
| 322 | ||
| 323 | if (s === -1) { | |
| 324 | if (start < b.byteLength) { | |
| 325 | s = i | |
| 326 | so = start | |
| 327 | } else { | |
| 328 | start -= b.byteLength | |
| 329 | } | |
| 330 | } | |
| 331 | ||
| 332 | if (e === -1 && end > -1) { | |
| 333 | if (end <= b.byteLength) { | |
| 334 | e = i + 1 | |
| 335 | eo = b.byteLength - end | |
| 336 | } else { | |
| 337 | end -= b.byteLength | |
| 338 | } | |
| 339 | } | |
| 340 | } | |
| 341 | ||
| 342 | return [s, so, e, eo] | |
| 343 | } |
| … | … | @@ -0,0 +1,55 @@ |
| 1 | { | |
| 2 | "name": "hyperblobs", | |
| 3 | "version": "2.9.0", | |
| 4 | "description": "A blob store for Hypercore", | |
| 5 | "main": "index.js", | |
| 6 | "scripts": { | |
| 7 | "format": "prettier --write .", | |
| 8 | "lint": "prettier --check . && lunte", | |
| 9 | "test": "npm run test:node && npm run test:bare", | |
| 10 | "test:node": "brittle-node test/*.js", | |
| 11 | "test:bare": "brittle-bare test/*.js" | |
| 12 | }, | |
| 13 | "repository": { | |
| 14 | "type": "git", | |
| 15 | "url": "git+https://github.com/holepunchto/hyperblobs.git" | |
| 16 | }, | |
| 17 | "keywords": [ | |
| 18 | "hypercore", | |
| 19 | "blob", | |
| 20 | "store" | |
| 21 | ], | |
| 22 | "author": "Andrew Osheroff <andrewosh@gmail.com>", | |
| 23 | "license": "Apache-2.0", | |
| 24 | "bugs": { | |
| 25 | "url": "https://github.com/holepunchto/hyperblobs/issues" | |
| 26 | }, | |
| 27 | "homepage": "https://github.com/holepunchto/hyperblobs#readme", | |
| 28 | "files": [ | |
| 29 | "index.js", | |
| 30 | "lib/**.js" | |
| 31 | ], | |
| 32 | "imports": { | |
| 33 | "events": { | |
| 34 | "bare": "bare-events", | |
| 35 | "default": "events" | |
| 36 | } | |
| 37 | }, | |
| 38 | "dependencies": { | |
| 39 | "b4a": "^1.6.1", | |
| 40 | "bare-events": "^2.5.0", | |
| 41 | "compact-encoding": "^2.18.0", | |
| 42 | "hypercore-crypto": "^3.6.1", | |
| 43 | "hypercore-errors": "^1.1.1", | |
| 44 | "mutexify": "^1.4.0", | |
| 45 | "speedometer": "^1.1.0", | |
| 46 | "streamx": "^2.13.2" | |
| 47 | }, | |
| 48 | "devDependencies": { | |
| 49 | "brittle": "^3.1.0", | |
| 50 | "hypercore": "^11.0.0", | |
| 51 | "lunte": "^1.0.0", | |
| 52 | "prettier": "^3.6.2", | |
| 53 | "prettier-config-holepunch": "^2.0.0" | |
| 54 | } | |
| 55 | } |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2018 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,68 @@ |
| 1 | # hypercore-crypto | |
| 2 | ||
| 3 | The crypto primitives used in hypercore, extracted into a separate module | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install hypercore-crypto | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const crypto = require('hypercore-crypto') | |
| 13 | ||
| 14 | const keyPair = crypto.keyPair() | |
| 15 | console.log(keyPair) // prints a ed25519 keypair | |
| 16 | ``` | |
| 17 | ||
| 18 | ## API | |
| 19 | ||
| 20 | #### `keyPair = crypto.keyPair()` | |
| 21 | ||
| 22 | Returns an `ED25519` keypair that can used for tree signing. | |
| 23 | ||
| 24 | #### `signature = crypto.sign(message, secretKey)` | |
| 25 | ||
| 26 | Signs a message (buffer). | |
| 27 | ||
| 28 | #### `verified = crypto.verify(message, signature, publicKey)` | |
| 29 | ||
| 30 | Verifies a signature for a message. | |
| 31 | ||
| 32 | #### `hash = crypto.data(data)` | |
| 33 | ||
| 34 | Hashes a leaf node in a merkle tree. | |
| 35 | ||
| 36 | #### `hash = crypto.parent(left, right)` | |
| 37 | ||
| 38 | Hash a parent node in a merkle tree. `left` and `right` should look like this: | |
| 39 | ||
| 40 | ```js | |
| 41 | { | |
| 42 | index: treeIndex, | |
| 43 | hash: hashOfThisNode, | |
| 44 | size: byteSizeOfThisTree | |
| 45 | } | |
| 46 | ``` | |
| 47 | ||
| 48 | #### `hash = crypto.tree(peaks)` | |
| 49 | ||
| 50 | Hashes the merkle root of the tree. `peaks` should be an array of the peaks of the tree and should look like above. | |
| 51 | ||
| 52 | #### `buffer = crypto.randomBytes(size)` | |
| 53 | ||
| 54 | Returns a buffer containing random bytes of size `size`. | |
| 55 | ||
| 56 | #### `hash = crypto.discoveryKey(publicKey)` | |
| 57 | ||
| 58 | Return a hash derived from a `publicKey` that can used for discovery | |
| 59 | without disclosing the public key. | |
| 60 | ||
| 61 | #### `list = crypto.namespace(name, count)` | |
| 62 | ||
| 63 | Make a list of namespaces from a specific publicly known name. | |
| 64 | Use this to namespace capabilities or hashes / signatures across algorithms. | |
| 65 | ||
| 66 | ## License | |
| 67 | ||
| 68 | MIT |
| … | … | @@ -0,0 +1,185 @@ |
| 1 | const sodium = require('sodium-universal') | |
| 2 | const c = require('compact-encoding') | |
| 3 | const b4a = require('b4a') | |
| 4 | ||
| 5 | // https://en.wikipedia.org/wiki/Merkle_tree#Second_preimage_attack | |
| 6 | const LEAF_TYPE = b4a.from([0]) | |
| 7 | const PARENT_TYPE = b4a.from([1]) | |
| 8 | const ROOT_TYPE = b4a.from([2]) | |
| 9 | ||
| 10 | const HYPERCORE = b4a.from('hypercore') | |
| 11 | ||
| 12 | exports.keyPair = function (seed) { | |
| 13 | // key pairs might stay around for a while, so better not to use a default slab to avoid retaining it completely | |
| 14 | const slab = b4a.allocUnsafeSlow(sodium.crypto_sign_PUBLICKEYBYTES + sodium.crypto_sign_SECRETKEYBYTES) | |
| 15 | const publicKey = slab.subarray(0, sodium.crypto_sign_PUBLICKEYBYTES) | |
| 16 | const secretKey = slab.subarray(sodium.crypto_sign_PUBLICKEYBYTES) | |
| 17 | ||
| 18 | if (seed) sodium.crypto_sign_seed_keypair(publicKey, secretKey, seed) | |
| 19 | else sodium.crypto_sign_keypair(publicKey, secretKey) | |
| 20 | ||
| 21 | return { | |
| 22 | publicKey, | |
| 23 | secretKey | |
| 24 | } | |
| 25 | } | |
| 26 | ||
| 27 | exports.validateKeyPair = function (keyPair) { | |
| 28 | const pk = b4a.allocUnsafe(sodium.crypto_sign_PUBLICKEYBYTES) | |
| 29 | sodium.crypto_sign_ed25519_sk_to_pk(pk, keyPair.secretKey) | |
| 30 | return b4a.equals(pk, keyPair.publicKey) | |
| 31 | } | |
| 32 | ||
| 33 | exports.sign = function (message, secretKey) { | |
| 34 | // Dedicated slab for the signature, to avoid retaining unneeded mem and for security | |
| 35 | const signature = b4a.allocUnsafeSlow(sodium.crypto_sign_BYTES) | |
| 36 | sodium.crypto_sign_detached(signature, message, secretKey) | |
| 37 | return signature | |
| 38 | } | |
| 39 | ||
| 40 | exports.verify = function (message, signature, publicKey) { | |
| 41 | if (signature.byteLength !== sodium.crypto_sign_BYTES) return false | |
| 42 | if (publicKey.byteLength !== sodium.crypto_sign_PUBLICKEYBYTES) return false | |
| 43 | return sodium.crypto_sign_verify_detached(signature, message, publicKey) | |
| 44 | } | |
| 45 | ||
| 46 | exports.encrypt = function (message, publicKey) { | |
| 47 | const ciphertext = b4a.alloc(message.byteLength + sodium.crypto_box_SEALBYTES) | |
| 48 | sodium.crypto_box_seal(ciphertext, message, publicKey) | |
| 49 | return ciphertext | |
| 50 | } | |
| 51 | ||
| 52 | exports.decrypt = function (ciphertext, keyPair) { | |
| 53 | if (ciphertext.byteLength < sodium.crypto_box_SEALBYTES) return null | |
| 54 | ||
| 55 | const plaintext = b4a.alloc(ciphertext.byteLength - sodium.crypto_box_SEALBYTES) | |
| 56 | ||
| 57 | if (!sodium.crypto_box_seal_open(plaintext, ciphertext, keyPair.publicKey, keyPair.secretKey)) { | |
| 58 | return null | |
| 59 | } | |
| 60 | ||
| 61 | return plaintext | |
| 62 | } | |
| 63 | ||
| 64 | exports.encryptionKeyPair = function (seed) { | |
| 65 | const publicKey = b4a.alloc(sodium.crypto_box_PUBLICKEYBYTES) | |
| 66 | const secretKey = b4a.alloc(sodium.crypto_box_SECRETKEYBYTES) | |
| 67 | ||
| 68 | if (seed) { | |
| 69 | sodium.crypto_box_seed_keypair(publicKey, secretKey, seed) | |
| 70 | } else { | |
| 71 | sodium.crypto_box_keypair(publicKey, secretKey) | |
| 72 | } | |
| 73 | ||
| 74 | return { | |
| 75 | publicKey, | |
| 76 | secretKey | |
| 77 | } | |
| 78 | } | |
| 79 | ||
| 80 | exports.data = function (data) { | |
| 81 | const out = b4a.allocUnsafe(32) | |
| 82 | ||
| 83 | sodium.crypto_generichash_batch(out, [ | |
| 84 | LEAF_TYPE, | |
| 85 | c.encode(c.uint64, data.byteLength), | |
| 86 | data | |
| 87 | ]) | |
| 88 | ||
| 89 | return out | |
| 90 | } | |
| 91 | ||
| 92 | exports.parent = function (a, b) { | |
| 93 | if (a.index > b.index) { | |
| 94 | const tmp = a | |
| 95 | a = b | |
| 96 | b = tmp | |
| 97 | } | |
| 98 | ||
| 99 | const out = b4a.allocUnsafe(32) | |
| 100 | ||
| 101 | sodium.crypto_generichash_batch(out, [ | |
| 102 | PARENT_TYPE, | |
| 103 | c.encode(c.uint64, a.size + b.size), | |
| 104 | a.hash, | |
| 105 | b.hash | |
| 106 | ]) | |
| 107 | ||
| 108 | return out | |
| 109 | } | |
| 110 | ||
| 111 | exports.tree = function (roots, out) { | |
| 112 | const buffers = new Array(3 * roots.length + 1) | |
| 113 | let j = 0 | |
| 114 | ||
| 115 | buffers[j++] = ROOT_TYPE | |
| 116 | ||
| 117 | for (let i = 0; i < roots.length; i++) { | |
| 118 | const r = roots[i] | |
| 119 | buffers[j++] = r.hash | |
| 120 | buffers[j++] = c.encode(c.uint64, r.index) | |
| 121 | buffers[j++] = c.encode(c.uint64, r.size) | |
| 122 | } | |
| 123 | ||
| 124 | if (!out) out = b4a.allocUnsafe(32) | |
| 125 | sodium.crypto_generichash_batch(out, buffers) | |
| 126 | return out | |
| 127 | } | |
| 128 | ||
| 129 | exports.hash = function (data, out) { | |
| 130 | if (!out) out = b4a.allocUnsafe(32) | |
| 131 | if (!Array.isArray(data)) data = [data] | |
| 132 | ||
| 133 | sodium.crypto_generichash_batch(out, data) | |
| 134 | ||
| 135 | return out | |
| 136 | } | |
| 137 | ||
| 138 | exports.randomBytes = function (n) { | |
| 139 | const buf = b4a.allocUnsafe(n) | |
| 140 | sodium.randombytes_buf(buf) | |
| 141 | return buf | |
| 142 | } | |
| 143 | ||
| 144 | exports.discoveryKey = function (key) { | |
| 145 | if (!key || key.byteLength !== 32) throw new Error('Must pass a 32 byte buffer') | |
| 146 | // Discovery keys might stay around for a while, so better not to use slab memory (for better gc) | |
| 147 | const digest = b4a.allocUnsafeSlow(32) | |
| 148 | sodium.crypto_generichash(digest, HYPERCORE, key) | |
| 149 | return digest | |
| 150 | } | |
| 151 | ||
| 152 | if (sodium.sodium_free) { | |
| 153 | exports.free = function (secureBuf) { | |
| 154 | if (secureBuf.secure) sodium.sodium_free(secureBuf) | |
| 155 | } | |
| 156 | } else { | |
| 157 | exports.free = function () {} | |
| 158 | } | |
| 159 | ||
| 160 | exports.namespace = function (name, count) { | |
| 161 | const ids = typeof count === 'number' ? range(count) : count | |
| 162 | ||
| 163 | // Namespaces are long-lived, so better to use a dedicated slab | |
| 164 | const buf = b4a.allocUnsafeSlow(32 * ids.length) | |
| 165 | ||
| 166 | const list = new Array(ids.length) | |
| 167 | ||
| 168 | // ns is emhemeral, so default slab | |
| 169 | const ns = b4a.allocUnsafe(33) | |
| 170 | sodium.crypto_generichash(ns.subarray(0, 32), typeof name === 'string' ? b4a.from(name) : name) | |
| 171 | ||
| 172 | for (let i = 0; i < list.length; i++) { | |
| 173 | list[i] = buf.subarray(32 * i, 32 * i + 32) | |
| 174 | ns[32] = ids[i] | |
| 175 | sodium.crypto_generichash(list[i], ns) | |
| 176 | } | |
| 177 | ||
| 178 | return list | |
| 179 | } | |
| 180 | ||
| 181 | function range (count) { | |
| 182 | const arr = new Array(count) | |
| 183 | for (let i = 0; i < count; i++) arr[i] = i | |
| 184 | return arr | |
| 185 | } |
| … | … | @@ -0,0 +1,279 @@ |
| 1 | cmake_minimum_required(VERSION 3.25) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) | |
| 5 | find_package(cmake-napi REQUIRED PATHS node_modules/cmake-napi) | |
| 6 | find_package(cmake-npm REQUIRED PATHS node_modules/cmake-npm) | |
| 7 | ||
| 8 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) | |
| 9 | ||
| 10 | project(sodium_native C ASM CXX) | |
| 11 | ||
| 12 | fetch_package("github:jedisct1/libsodium#e18eee6" SOURCE_DIR sodium) | |
| 13 | ||
| 14 | fetch_package("github:holepunchto/libjstl#098664c") | |
| 15 | ||
| 16 | bare_target(target) | |
| 17 | ||
| 18 | if(target MATCHES "win32") | |
| 19 | add_compile_options(/MT$<$<CONFIG:Debug>:d>) | |
| 20 | endif() | |
| 21 | ||
| 22 | file(COPY_FILE "${sodium}/builds/msvc/version.h" "${sodium}/src/libsodium/include/sodium/version.h") | |
| 23 | ||
| 24 | file(GLOB_RECURSE sodium_headers CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.h") | |
| 25 | file(GLOB_RECURSE sodium_sources CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.c") | |
| 26 | file(GLOB_RECURSE sodium_asm_sources CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.S") | |
| 27 | ||
| 28 | add_library(sodium OBJECT) | |
| 29 | ||
| 30 | target_sources( | |
| 31 | sodium | |
| 32 | INTERFACE | |
| 33 | ${sodium_headers} | |
| 34 | PRIVATE | |
| 35 | ${sodium_sources} | |
| 36 | ) | |
| 37 | ||
| 38 | target_include_directories( | |
| 39 | sodium | |
| 40 | INTERFACE | |
| 41 | "${sodium}/src/libsodium/include" | |
| 42 | PRIVATE | |
| 43 | "${sodium}/src/libsodium/include/sodium" | |
| 44 | ) | |
| 45 | ||
| 46 | if(NOT target MATCHES "win32") | |
| 47 | target_compile_options( | |
| 48 | sodium | |
| 49 | PRIVATE | |
| 50 | -fvisibility=hidden | |
| 51 | -fno-strict-aliasing | |
| 52 | -fwrapv | |
| 53 | -flax-vector-conversions | |
| 54 | ) | |
| 55 | endif() | |
| 56 | ||
| 57 | target_compile_definitions( | |
| 58 | sodium | |
| 59 | PUBLIC | |
| 60 | SODIUM_STATIC=1 | |
| 61 | PRIVATE | |
| 62 | _GNU_SOURCE=1 | |
| 63 | CONFIGURED=1 | |
| 64 | DEV_MODE=1 | |
| 65 | HAVE_ATOMIC_OPS=1 | |
| 66 | HAVE_C11_MEMORY_FENCES=1 | |
| 67 | HAVE_CET_H=1 | |
| 68 | HAVE_GCC_MEMORY_FENCES=1 | |
| 69 | HAVE_INLINE_ASM=1 | |
| 70 | HAVE_INTTYPES_H=1 | |
| 71 | HAVE_STDINT_H=1 | |
| 72 | HAVE_TI_MODE=1 | |
| 73 | ) | |
| 74 | ||
| 75 | if(target MATCHES "darwin|ios") | |
| 76 | target_compile_definitions( | |
| 77 | sodium | |
| 78 | PRIVATE | |
| 79 | ASM_HIDE_SYMBOL=.private_extern | |
| 80 | TLS=_Thread_local | |
| 81 | HAVE_ARC4RANDOM=1 | |
| 82 | HAVE_ARC4RANDOM_BUF=1 | |
| 83 | HAVE_CATCHABLE_ABRT=1 | |
| 84 | HAVE_CATCHABLE_SEGV=1 | |
| 85 | HAVE_CLOCK_GETTIME=1 | |
| 86 | HAVE_GETPID=1 | |
| 87 | HAVE_MADVISE=1 | |
| 88 | HAVE_MEMSET_S=1 | |
| 89 | HAVE_MLOCK=1 | |
| 90 | HAVE_MMAP=1 | |
| 91 | HAVE_MPROTECT=1 | |
| 92 | HAVE_NANOSLEEP=1 | |
| 93 | HAVE_POSIX_MEMALIGN=1 | |
| 94 | HAVE_PTHREAD=1 | |
| 95 | HAVE_PTHREAD_PRIO_INHERIT=1 | |
| 96 | HAVE_RAISE=1 | |
| 97 | HAVE_SYSCONF=1 | |
| 98 | HAVE_SYS_MMAN_H=1 | |
| 99 | HAVE_SYS_PARAM_H=1 | |
| 100 | HAVE_WEAK_SYMBOLS=1 | |
| 101 | ) | |
| 102 | ||
| 103 | if(NOT target MATCHES "ios") | |
| 104 | target_compile_definitions( | |
| 105 | sodium | |
| 106 | PRIVATE | |
| 107 | HAVE_GETENTROPY=1 | |
| 108 | HAVE_SYS_RANDOM_H=1 | |
| 109 | ) | |
| 110 | endif() | |
| 111 | endif() | |
| 112 | ||
| 113 | if(target MATCHES "linux") | |
| 114 | target_compile_definitions( | |
| 115 | sodium | |
| 116 | PRIVATE | |
| 117 | ASM_HIDE_SYMBOL=.hidden | |
| 118 | TLS=_Thread_local | |
| 119 | HAVE_CATCHABLE_ABRT=1 | |
| 120 | HAVE_CATCHABLE_SEGV=1 | |
| 121 | HAVE_CLOCK_GETTIME=1 | |
| 122 | HAVE_GETPID=1 | |
| 123 | HAVE_MADVISE=1 | |
| 124 | HAVE_MLOCK=1 | |
| 125 | HAVE_MMAP=1 | |
| 126 | HAVE_MPROTECT=1 | |
| 127 | HAVE_NANOSLEEP=1 | |
| 128 | HAVE_POSIX_MEMALIGN=1 | |
| 129 | HAVE_PTHREAD_PRIO_INHERIT=1 | |
| 130 | HAVE_PTHREAD=1 | |
| 131 | HAVE_RAISE=1 | |
| 132 | HAVE_SYSCONF=1 | |
| 133 | HAVE_SYS_AUXV_H=1 | |
| 134 | HAVE_SYS_MMAN_H=1 | |
| 135 | HAVE_SYS_PARAM_H=1 | |
| 136 | HAVE_SYS_RANDOM_H=1 | |
| 137 | HAVE_WEAK_SYMBOLS=1 | |
| 138 | ) | |
| 139 | endif() | |
| 140 | ||
| 141 | if(target MATCHES "win32") | |
| 142 | target_compile_definitions( | |
| 143 | sodium | |
| 144 | PRIVATE | |
| 145 | _CRT_SECURE_NO_WARNINGS=1 | |
| 146 | HAVE_RAISE=1 | |
| 147 | ) | |
| 148 | endif() | |
| 149 | ||
| 150 | if(target MATCHES "x64") | |
| 151 | target_compile_definitions( | |
| 152 | sodium | |
| 153 | PRIVATE | |
| 154 | HAVE_CPUID=1 | |
| 155 | HAVE_RDRAND=1 | |
| 156 | HAVE_EMMINTRIN_H=1 # SSE2 | |
| 157 | HAVE_PMMINTRIN_H=1 # SSE3 | |
| 158 | HAVE_TMMINTRIN_H=1 # SSSE3 | |
| 159 | HAVE_SMMINTRIN_H=1 # SSE4.1 | |
| 160 | HAVE_WMMINTRIN_H=1 # AES | |
| 161 | HAVE_AVXINTRIN_H=1 # AVX | |
| 162 | HAVE_AVX2INTRIN_H=1 # AVX2 | |
| 163 | HAVE_AVX512FINTRIN_H # AVX512F | |
| 164 | ) | |
| 165 | ||
| 166 | if(NOT target MATCHES "win32") | |
| 167 | target_compile_definitions( | |
| 168 | sodium | |
| 169 | PRIVATE | |
| 170 | HAVE_AMD64_ASM=1 | |
| 171 | HAVE_AVX_ASM=1 | |
| 172 | ) | |
| 173 | ||
| 174 | target_sources( | |
| 175 | sodium | |
| 176 | PRIVATE | |
| 177 | ${sodium_asm_sources} | |
| 178 | ) | |
| 179 | endif() | |
| 180 | endif() | |
| 181 | ||
| 182 | if(target MATCHES "arm64") | |
| 183 | target_compile_definitions( | |
| 184 | sodium | |
| 185 | PRIVATE | |
| 186 | HAVE_ARMCRYPTO=1 | |
| 187 | ) | |
| 188 | endif() | |
| 189 | ||
| 190 | if(CMAKE_C_BYTE_ORDER MATCHES "BIG_ENDIAN") | |
| 191 | target_compile_definitions( | |
| 192 | sodium | |
| 193 | PRIVATE | |
| 194 | NATIVE_BIG_ENDIAN=1 | |
| 195 | ) | |
| 196 | else() | |
| 197 | target_compile_definitions( | |
| 198 | sodium | |
| 199 | PRIVATE | |
| 200 | NATIVE_LITTLE_ENDIAN=1 | |
| 201 | ) | |
| 202 | endif() | |
| 203 | ||
| 204 | if(target MATCHES "linux|android") | |
| 205 | target_link_options( | |
| 206 | sodium | |
| 207 | PUBLIC | |
| 208 | -Wl,-z,noexecstack | |
| 209 | ) | |
| 210 | endif() | |
| 211 | ||
| 212 | add_library(sodium_extensions OBJECT) | |
| 213 | ||
| 214 | target_sources( | |
| 215 | sodium_extensions | |
| 216 | PRIVATE | |
| 217 | extensions/tweak/tweak.c | |
| 218 | extensions/tweak/tweak.h | |
| 219 | extensions/pbkdf2/pbkdf2.c | |
| 220 | extensions/pbkdf2/pbkdf2.h | |
| 221 | ) | |
| 222 | ||
| 223 | target_link_libraries( | |
| 224 | sodium_extensions | |
| 225 | PUBLIC | |
| 226 | sodium | |
| 227 | ) | |
| 228 | ||
| 229 | add_bare_module(sodium_native_bare) | |
| 230 | ||
| 231 | target_sources( | |
| 232 | ${sodium_native_bare} | |
| 233 | PRIVATE | |
| 234 | binding.cc | |
| 235 | ) | |
| 236 | ||
| 237 | target_link_libraries( | |
| 238 | ${sodium_native_bare} | |
| 239 | PRIVATE | |
| 240 | $<TARGET_OBJECTS:sodium> | |
| 241 | $<TARGET_OBJECTS:sodium_extensions> | |
| 242 | jstl | |
| 243 | PUBLIC | |
| 244 | sodium | |
| 245 | sodium_extensions | |
| 246 | ) | |
| 247 | ||
| 248 | add_napi_module(sodium_native_node) | |
| 249 | ||
| 250 | target_sources( | |
| 251 | ${sodium_native_node} | |
| 252 | PRIVATE | |
| 253 | binding.cc | |
| 254 | ) | |
| 255 | ||
| 256 | target_compile_definitions( | |
| 257 | ${sodium_native_node} | |
| 258 | PRIVATE | |
| 259 | NAPI_VERSION=9 | |
| 260 | ) | |
| 261 | ||
| 262 | target_link_libraries( | |
| 263 | ${sodium_native_node} | |
| 264 | PRIVATE | |
| 265 | $<TARGET_OBJECTS:sodium> | |
| 266 | $<TARGET_OBJECTS:sodium_extensions> | |
| 267 | jstl | |
| 268 | PUBLIC | |
| 269 | sodium | |
| 270 | sodium_extensions | |
| 271 | ) | |
| 272 | ||
| 273 | resolve_node_module(bare-compat-napi compat) | |
| 274 | ||
| 275 | target_include_directories( | |
| 276 | ${sodium_native_node} | |
| 277 | PRIVATE | |
| 278 | "${compat}/include" | |
| 279 | ) |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2016 Mathias Buus and Emil Bay | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,51 @@ |
| 1 | # sodium-native | |
| 2 | ||
| 3 | Low level bindings for [libsodium](https://github.com/jedisct1/libsodium). | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install sodium-native | |
| 7 | ``` | |
| 8 | ||
| 9 | The goal of this project is to be thin, stable, unopionated wrapper around libsodium. | |
| 10 | ||
| 11 | All methods exposed are more or less a direct translation of the libsodium c-api. | |
| 12 | This means that most data types are buffers and you have to manage allocating return values and passing them in as arguments instead of receiving them as return values. | |
| 13 | ||
| 14 | This makes this API harder to use than other libsodium wrappers out there, but also means that you'll be able to get a lot of perf / memory improvements as you can do stuff like inline encryption / decryption, reuse buffers etc. | |
| 15 | ||
| 16 | This also makes this library useful as a foundation for more high level crypto abstractions that you want to make. | |
| 17 | ||
| 18 | ## Usage | |
| 19 | ||
| 20 | ```js | |
| 21 | var sodium = require('sodium-native') | |
| 22 | ||
| 23 | var nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES) | |
| 24 | var key = sodium.sodium_malloc(sodium.crypto_secretbox_KEYBYTES) // secure buffer | |
| 25 | var message = Buffer.from('Hello, World!') | |
| 26 | var ciphertext = Buffer.alloc(message.length + sodium.crypto_secretbox_MACBYTES) | |
| 27 | ||
| 28 | sodium.randombytes_buf(nonce) // insert random data into nonce | |
| 29 | sodium.randombytes_buf(key) // insert random data into key | |
| 30 | ||
| 31 | // encrypted message is stored in ciphertext. | |
| 32 | sodium.crypto_secretbox_easy(ciphertext, message, nonce, key) | |
| 33 | ||
| 34 | console.log('Encrypted message:', ciphertext) | |
| 35 | ||
| 36 | var plainText = Buffer.alloc(ciphertext.length - sodium.crypto_secretbox_MACBYTES) | |
| 37 | ||
| 38 | if (!sodium.crypto_secretbox_open_easy(plainText, ciphertext, nonce, key)) { | |
| 39 | console.log('Decryption failed!') | |
| 40 | } else { | |
| 41 | console.log('Decrypted message:', plainText, '(' + plainText.toString() + ')') | |
| 42 | } | |
| 43 | ``` | |
| 44 | ||
| 45 | ## Documentation | |
| 46 | ||
| 47 | Complete documentation may be found on the [sodium-friends website](https://sodium-friends.github.io/docs/docs/getstarted) | |
| 48 | ||
| 49 | ## License | |
| 50 | ||
| 51 | MIT |
| … | … | @@ -0,0 +1,4094 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <bare.h> | |
| 3 | #include <js.h> | |
| 4 | #include <jstl.h> | |
| 5 | #include <sodium.h> | |
| 6 | #include <stdbool.h> | |
| 7 | #include <stdint.h> | |
| 8 | #include <string.h> | |
| 9 | #include <uv.h> | |
| 10 | ||
| 11 | #include "extensions/pbkdf2/pbkdf2.h" | |
| 12 | #include "extensions/tweak/tweak.h" | |
| 13 | ||
| 14 | #define assert_bounds(arraybuffer) \ | |
| 15 | assert(arraybuffer##_offset + arraybuffer##_len <= arraybuffer.size()) | |
| 16 | ||
| 17 | static inline void | |
| 18 | sn_sodium_memzero( | |
| 19 | js_env_t *, | |
| 20 | js_receiver_t, | |
| 21 | js_typedarray_span_t<> buf | |
| 22 | ) { | |
| 23 | sodium_memzero(buf.data(), buf.size_bytes()); | |
| 24 | } | |
| 25 | ||
| 26 | static inline int | |
| 27 | sn_sodium_mlock( | |
| 28 | js_env_t *, | |
| 29 | js_receiver_t, | |
| 30 | js_typedarray_span_t<> buf | |
| 31 | ) { | |
| 32 | return sodium_mlock(buf.data(), buf.size_bytes()); | |
| 33 | } | |
| 34 | ||
| 35 | static inline int | |
| 36 | sn_sodium_munlock( | |
| 37 | js_env_t *, | |
| 38 | js_receiver_t, | |
| 39 | js_typedarray_span_t<> buf | |
| 40 | ) { | |
| 41 | return sodium_munlock(buf.data(), buf.size_bytes()); | |
| 42 | } | |
| 43 | ||
| 44 | static void | |
| 45 | sn_external_arraybuffer_finalize(js_env_t *env, uint8_t *ptr) { | |
| 46 | sodium_free(ptr); | |
| 47 | } | |
| 48 | ||
| 49 | static js_arraybuffer_t | |
| 50 | sn_sodium_malloc(js_env_t *env, js_receiver_t, uint32_t len) { | |
| 51 | int err; | |
| 52 | ||
| 53 | auto ptr = reinterpret_cast<uint8_t *>(sodium_malloc(len)); | |
| 54 | ||
| 55 | js_arraybuffer_t buffer; | |
| 56 | ||
| 57 | err = js_create_external_arraybuffer<sn_external_arraybuffer_finalize>(env, ptr, len, buffer); | |
| 58 | assert(err == 0); | |
| 59 | ||
| 60 | return buffer; | |
| 61 | } | |
| 62 | ||
| 63 | static void | |
| 64 | sn_sodium_free(js_env_t *env, js_receiver_t, js_arraybuffer_t buf) { | |
| 65 | int err; | |
| 66 | ||
| 67 | std::span<uint8_t> view; | |
| 68 | err = js_get_arraybuffer_info(env, buf, view); | |
| 69 | assert(err == 0); | |
| 70 | ||
| 71 | if (view.empty()) return; | |
| 72 | ||
| 73 | err = js_detach_arraybuffer(env, buf); | |
| 74 | assert(err == 0); | |
| 75 | } | |
| 76 | ||
| 77 | static inline int | |
| 78 | sn_sodium_mprotect_noaccess(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 79 | return sodium_mprotect_noaccess(buf.data()); | |
| 80 | } | |
| 81 | ||
| 82 | static inline int | |
| 83 | sn_sodium_mprotect_readonly(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 84 | return sodium_mprotect_readonly(buf.data()); | |
| 85 | } | |
| 86 | ||
| 87 | static inline int | |
| 88 | sn_sodium_mprotect_readwrite(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 89 | return sodium_mprotect_readwrite(buf.data()); | |
| 90 | } | |
| 91 | ||
| 92 | static inline uint32_t // TODO: test envless | |
| 93 | sn_randombytes_random(js_env_t *env, js_receiver_t) { | |
| 94 | return randombytes_random(); | |
| 95 | } | |
| 96 | ||
| 97 | static inline uint32_t // TODO: test envless | |
| 98 | sn_randombytes_uniform(js_env_t *env, js_receiver_t, uint32_t upper_bound) { | |
| 99 | return randombytes_uniform(upper_bound); | |
| 100 | } | |
| 101 | ||
| 102 | static inline void | |
| 103 | sn_randombytes_buf( | |
| 104 | js_env_t *env, | |
| 105 | js_receiver_t, | |
| 106 | js_arraybuffer_span_t buf, | |
| 107 | uint32_t buf_offset, | |
| 108 | uint32_t buf_len | |
| 109 | ) { | |
| 110 | assert_bounds(buf); | |
| 111 | ||
| 112 | randombytes_buf(&buf[buf_offset], buf_len); | |
| 113 | } | |
| 114 | ||
| 115 | static inline void | |
| 116 | sn_randombytes_buf_deterministic( | |
| 117 | js_env_t *env, | |
| 118 | js_receiver_t, | |
| 119 | ||
| 120 | js_arraybuffer_span_t buf, | |
| 121 | uint32_t buf_offset, | |
| 122 | uint32_t buf_len, | |
| 123 | ||
| 124 | js_arraybuffer_span_t seed, | |
| 125 | uint32_t seed_offset, | |
| 126 | uint32_t seed_len | |
| 127 | ) { | |
| 128 | assert_bounds(buf); | |
| 129 | assert_bounds(seed); | |
| 130 | ||
| 131 | assert(seed_len == randombytes_SEEDBYTES); | |
| 132 | ||
| 133 | randombytes_buf_deterministic(&buf[buf_offset], buf_len, &seed[seed_offset]); | |
| 134 | } | |
| 135 | ||
| 136 | static inline bool | |
| 137 | sn_sodium_memcmp(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 138 | if (a.size_bytes() != b.size_bytes()) return false; | |
| 139 | ||
| 140 | return sodium_memcmp(a.data(), b.data(), a.size_bytes()) == 0; | |
| 141 | } | |
| 142 | ||
| 143 | static inline void | |
| 144 | sn_sodium_increment(js_env_t *, js_receiver_t, js_typedarray_span_t<> n) { | |
| 145 | sodium_increment(n.data(), n.size_bytes()); | |
| 146 | } | |
| 147 | ||
| 148 | static inline void | |
| 149 | sn_sodium_add(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 150 | sodium_add(a.data(), b.data(), a.size_bytes()); | |
| 151 | } | |
| 152 | ||
| 153 | static inline void | |
| 154 | sn_sodium_sub(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 155 | sodium_sub(a.data(), b.data(), a.size_bytes()); | |
| 156 | } | |
| 157 | ||
| 158 | static inline int32_t | |
| 159 | sn_sodium_compare(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 160 | return sodium_compare(a.data(), b.data(), a.size_bytes()); | |
| 161 | } | |
| 162 | ||
| 163 | static inline bool | |
| 164 | sn_sodium_is_zero(js_env_t *, js_receiver_t, js_typedarray_span_t<> buffer, uint32_t len) { | |
| 165 | assert(len <= buffer.size_bytes()); | |
| 166 | ||
| 167 | return sodium_is_zero(buffer.data(), len) != 0; | |
| 168 | } | |
| 169 | ||
| 170 | static inline uint32_t | |
| 171 | sn_sodium_pad(js_env_t *, js_receiver_t, js_typedarray_span_t<> buf, uint32_t unpadded_buflen, uint32_t blocksize) { | |
| 172 | size_t padded_buflen; | |
| 173 | ||
| 174 | sodium_pad(&padded_buflen, buf.data(), unpadded_buflen, blocksize, buf.size_bytes()); | |
| 175 | ||
| 176 | return padded_buflen; | |
| 177 | } | |
| 178 | ||
| 179 | static inline uint32_t | |
| 180 | sn_sodium_unpad(js_env_t *, js_receiver_t, js_typedarray_span_t<> buf, uint32_t padded_buflen, uint32_t blocksize) { | |
| 181 | size_t unpadded_buflen; | |
| 182 | ||
| 183 | sodium_unpad(&unpadded_buflen, buf.data(), padded_buflen, blocksize); | |
| 184 | ||
| 185 | return unpadded_buflen; | |
| 186 | } | |
| 187 | ||
| 188 | static inline int | |
| 189 | sn_crypto_sign_keypair(js_env_t *, js_receiver_t, js_typedarray_span_t<> pk, js_typedarray_span_t<> sk) { | |
| 190 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 191 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 192 | ||
| 193 | return crypto_sign_keypair(pk.data(), sk.data()); | |
| 194 | } | |
| 195 | ||
| 196 | static inline int | |
| 197 | sn_crypto_sign_seed_keypair( | |
| 198 | js_env_t *, | |
| 199 | js_receiver_t, | |
| 200 | js_typedarray_span_t<> pk, | |
| 201 | js_typedarray_span_t<> sk, | |
| 202 | js_typedarray_span_t<> seed | |
| 203 | ) { | |
| 204 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 205 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 206 | assert(seed.size_bytes() == crypto_sign_SEEDBYTES); | |
| 207 | ||
| 208 | return crypto_sign_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 209 | } | |
| 210 | ||
| 211 | static inline int | |
| 212 | sn_crypto_sign( | |
| 213 | js_env_t *, | |
| 214 | js_receiver_t, | |
| 215 | js_typedarray_span_t<> sm, | |
| 216 | js_typedarray_span_t<> m, | |
| 217 | js_typedarray_span_t<> sk | |
| 218 | ) { | |
| 219 | assert(sm.size_bytes() == crypto_sign_BYTES + m.size_bytes()); | |
| 220 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 221 | ||
| 222 | return crypto_sign(sm.data(), NULL, m.data(), m.size_bytes(), sk.data()); | |
| 223 | } | |
| 224 | ||
| 225 | static inline bool | |
| 226 | sn_crypto_sign_open( | |
| 227 | js_env_t *, | |
| 228 | js_receiver_t, | |
| 229 | js_typedarray_span_t<> m, | |
| 230 | js_typedarray_span_t<> sm, | |
| 231 | js_typedarray_span_t<> pk | |
| 232 | ) { | |
| 233 | assert(m.size_bytes() == sm.size_bytes() - crypto_sign_BYTES); | |
| 234 | assert(sm.size_bytes() >= crypto_sign_BYTES); | |
| 235 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 236 | ||
| 237 | return crypto_sign_open(m.data(), NULL, sm.data(), sm.size_bytes(), pk.data()) == 0; | |
| 238 | } | |
| 239 | ||
| 240 | static inline int | |
| 241 | sn_crypto_sign_detached( | |
| 242 | js_env_t *env, | |
| 243 | js_receiver_t, | |
| 244 | js_typedarray_span_t<> sig, | |
| 245 | js_typedarray_span_t<> m, | |
| 246 | js_typedarray_span_t<> sk | |
| 247 | ) { | |
| 248 | assert(sig.size_bytes() == crypto_sign_BYTES); | |
| 249 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 250 | ||
| 251 | return crypto_sign_detached(sig.data(), NULL, m.data(), m.size_bytes(), sk.data()); | |
| 252 | } | |
| 253 | ||
| 254 | static inline bool | |
| 255 | sn_crypto_sign_verify_detached( | |
| 256 | js_env_t *env, | |
| 257 | js_receiver_t, | |
| 258 | ||
| 259 | js_arraybuffer_span_t sig, | |
| 260 | uint32_t sig_offset, | |
| 261 | uint32_t sig_len, | |
| 262 | ||
| 263 | js_arraybuffer_span_t m, | |
| 264 | uint32_t m_offset, | |
| 265 | uint32_t m_len, | |
| 266 | ||
| 267 | js_arraybuffer_span_t pk, | |
| 268 | uint32_t pk_offset, | |
| 269 | uint32_t pk_len | |
| 270 | ) { | |
| 271 | assert_bounds(sig); | |
| 272 | assert_bounds(m); | |
| 273 | assert_bounds(pk); | |
| 274 | ||
| 275 | assert(sig_len >= crypto_sign_BYTES); | |
| 276 | assert(pk_len == crypto_sign_PUBLICKEYBYTES); | |
| 277 | ||
| 278 | return crypto_sign_verify_detached(&sig[sig_offset], &m[m_offset], m_len, &pk[pk_offset]) == 0; | |
| 279 | } | |
| 280 | ||
| 281 | static inline int | |
| 282 | sn_crypto_sign_ed25519_sk_to_pk( | |
| 283 | js_env_t *, | |
| 284 | js_receiver_t, | |
| 285 | js_typedarray_span_t<> pk, | |
| 286 | js_typedarray_span_t<> sk | |
| 287 | ) { | |
| 288 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 289 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 290 | ||
| 291 | return crypto_sign_ed25519_sk_to_pk(pk.data(), sk.data()); | |
| 292 | } | |
| 293 | ||
| 294 | static inline int | |
| 295 | sn_crypto_sign_ed25519_pk_to_curve25519( | |
| 296 | js_env_t *, | |
| 297 | js_receiver_t, | |
| 298 | js_typedarray_span_t<> x25519_pk, | |
| 299 | js_typedarray_span_t<> ed25519_pk | |
| 300 | ) { | |
| 301 | assert(x25519_pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 302 | assert(ed25519_pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 303 | ||
| 304 | return crypto_sign_ed25519_pk_to_curve25519(x25519_pk.data(), ed25519_pk.data()); | |
| 305 | } | |
| 306 | ||
| 307 | static inline int | |
| 308 | sn_crypto_sign_ed25519_sk_to_curve25519( | |
| 309 | js_env_t *, | |
| 310 | js_receiver_t, | |
| 311 | js_typedarray_span_t<> x25519_sk, | |
| 312 | js_typedarray_span_t<> ed25519_sk | |
| 313 | ) { | |
| 314 | assert(x25519_sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 315 | assert( | |
| 316 | ed25519_sk.size_bytes() == crypto_sign_SECRETKEYBYTES || | |
| 317 | ed25519_sk.size_bytes() == crypto_box_SECRETKEYBYTES | |
| 318 | ); | |
| 319 | ||
| 320 | return crypto_sign_ed25519_sk_to_curve25519(x25519_sk.data(), ed25519_sk.data()); | |
| 321 | } | |
| 322 | ||
| 323 | static inline int | |
| 324 | sn_crypto_generichash( | |
| 325 | js_env_t *env, | |
| 326 | js_receiver_t, | |
| 327 | ||
| 328 | js_arraybuffer_span_t out, | |
| 329 | uint32_t out_offset, | |
| 330 | uint32_t out_len, | |
| 331 | ||
| 332 | js_arraybuffer_span_t in, | |
| 333 | uint32_t in_offset, | |
| 334 | uint32_t in_len, | |
| 335 | ||
| 336 | js_arraybuffer_span_t key, | |
| 337 | uint32_t key_offset, | |
| 338 | uint32_t key_len | |
| 339 | ) { | |
| 340 | assert_bounds(out); | |
| 341 | assert( | |
| 342 | out_len >= crypto_generichash_BYTES_MIN && | |
| 343 | out_len <= crypto_generichash_BYTES_MAX | |
| 344 | ); | |
| 345 | ||
| 346 | assert_bounds(in); | |
| 347 | ||
| 348 | uint8_t *key_data = NULL; | |
| 349 | if (key_len) { | |
| 350 | assert_bounds(key); | |
| 351 | assert( | |
| 352 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 353 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 354 | ); | |
| 355 | key_data = &key[key_offset]; | |
| 356 | } | |
| 357 | ||
| 358 | return crypto_generichash(&out[out_offset], out_len, &in[in_offset], in_len, key_data, key_len); | |
| 359 | } | |
| 360 | ||
| 361 | static inline int | |
| 362 | sn_crypto_generichash_batch( | |
| 363 | js_env_t *env, | |
| 364 | js_receiver_t, | |
| 365 | js_typedarray_t<uint8_t> out, | |
| 366 | std::vector<js_typedarray_span_t<>> batch, | |
| 367 | bool use_key, | |
| 368 | js_typedarray_t<uint8_t> key | |
| 369 | ) { | |
| 370 | int err; | |
| 371 | ||
| 372 | uint8_t *out_data; | |
| 373 | size_t out_len; | |
| 374 | err = js_get_typedarray_info(env, out, out_data, out_len); | |
| 375 | assert(err == 0); | |
| 376 | assert( | |
| 377 | out_len >= crypto_generichash_BYTES_MIN && | |
| 378 | out_len <= crypto_generichash_BYTES_MAX | |
| 379 | ); | |
| 380 | ||
| 381 | uint8_t *key_data = NULL; | |
| 382 | size_t key_len = 0; | |
| 383 | if (use_key) { | |
| 384 | err = js_get_typedarray_info(env, key, key_data, key_len); | |
| 385 | assert(err == 0); | |
| 386 | assert( | |
| 387 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 388 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 389 | ); | |
| 390 | } | |
| 391 | ||
| 392 | crypto_generichash_state state; | |
| 393 | err = crypto_generichash_init(&state, key_data, key_len, out_len); | |
| 394 | if (err != 0) return err; | |
| 395 | ||
| 396 | for (auto &buf : batch) { | |
| 397 | err = crypto_generichash_update(&state, buf.data(), buf.size()); | |
| 398 | if (err != 0) return err; | |
| 399 | } | |
| 400 | ||
| 401 | return crypto_generichash_final(&state, out_data, out_len); | |
| 402 | } | |
| 403 | ||
| 404 | static inline void | |
| 405 | sn_crypto_generichash_keygen( | |
| 406 | js_env_t *env, | |
| 407 | js_receiver_t, | |
| 408 | ||
| 409 | js_arraybuffer_span_t key, | |
| 410 | uint32_t key_offset, | |
| 411 | uint32_t key_len | |
| 412 | ) { | |
| 413 | assert_bounds(key); | |
| 414 | assert(key_len == crypto_generichash_KEYBYTES); | |
| 415 | ||
| 416 | crypto_generichash_keygen(&key[key_offset]); | |
| 417 | } | |
| 418 | ||
| 419 | static inline int | |
| 420 | sn_crypto_generichash_init( | |
| 421 | js_env_t *env, | |
| 422 | js_receiver_t, | |
| 423 | ||
| 424 | js_arraybuffer_span_t state, | |
| 425 | uint32_t state_offset, | |
| 426 | uint32_t state_len, | |
| 427 | ||
| 428 | js_arraybuffer_span_t key, | |
| 429 | uint32_t key_offset, | |
| 430 | uint32_t key_len, | |
| 431 | ||
| 432 | uint32_t out_len | |
| 433 | ) { | |
| 434 | assert_bounds(state); | |
| 435 | ||
| 436 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 437 | ||
| 438 | uint8_t *key_data = NULL; | |
| 439 | if (key_len) { | |
| 440 | assert_bounds(key); | |
| 441 | assert( | |
| 442 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 443 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 444 | ); | |
| 445 | key_data = &key[key_offset]; | |
| 446 | } | |
| 447 | ||
| 448 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 449 | ||
| 450 | return crypto_generichash_init(state_data, key_data, key_len, out_len); | |
| 451 | } | |
| 452 | ||
| 453 | static inline int | |
| 454 | sn_crypto_generichash_update( | |
| 455 | js_env_t *env, | |
| 456 | js_receiver_t, | |
| 457 | ||
| 458 | js_arraybuffer_span_t state, | |
| 459 | uint32_t state_offset, | |
| 460 | uint32_t state_len, | |
| 461 | ||
| 462 | js_arraybuffer_span_t in, | |
| 463 | uint32_t in_offset, | |
| 464 | uint32_t in_len | |
| 465 | ) { | |
| 466 | assert_bounds(state); | |
| 467 | assert_bounds(in); | |
| 468 | ||
| 469 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 470 | ||
| 471 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 472 | ||
| 473 | return crypto_generichash_update(state_data, &in[in_offset], in_len); | |
| 474 | } | |
| 475 | ||
| 476 | static inline int | |
| 477 | sn_crypto_generichash_final( | |
| 478 | js_env_t *env, | |
| 479 | js_receiver_t, | |
| 480 | ||
| 481 | js_arraybuffer_span_t state, | |
| 482 | uint32_t state_offset, | |
| 483 | uint32_t state_len, | |
| 484 | ||
| 485 | js_arraybuffer_span_t out, | |
| 486 | uint32_t out_offset, | |
| 487 | uint32_t out_len | |
| 488 | ) { | |
| 489 | assert_bounds(state); | |
| 490 | assert_bounds(out); | |
| 491 | ||
| 492 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 493 | ||
| 494 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 495 | ||
| 496 | return crypto_generichash_final(state_data, &out[out_offset], out_len); | |
| 497 | } | |
| 498 | ||
| 499 | static inline int | |
| 500 | sn_crypto_box_keypair(js_env_t *, js_receiver_t, js_typedarray_span_t<> pk, js_typedarray_span_t<> sk) { | |
| 501 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 502 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 503 | ||
| 504 | return crypto_box_keypair(pk.data(), sk.data()); | |
| 505 | } | |
| 506 | ||
| 507 | static inline int | |
| 508 | sn_crypto_box_seed_keypair( | |
| 509 | js_env_t *, | |
| 510 | js_receiver_t, | |
| 511 | js_typedarray_span_t<> pk, | |
| 512 | js_typedarray_span_t<> sk, | |
| 513 | js_typedarray_span_t<> seed | |
| 514 | ) { | |
| 515 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 516 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 517 | assert(seed.size_bytes() == crypto_box_SEEDBYTES); | |
| 518 | ||
| 519 | return crypto_box_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 520 | } | |
| 521 | ||
| 522 | static inline int | |
| 523 | sn_crypto_box_easy( | |
| 524 | js_env_t *, | |
| 525 | js_receiver_t, | |
| 526 | js_typedarray_span_t<> c, | |
| 527 | js_typedarray_span_t<> m, | |
| 528 | js_typedarray_span_t<> n, | |
| 529 | js_typedarray_span_t<> pk, | |
| 530 | js_typedarray_span_t<> sk | |
| 531 | ) { | |
| 532 | assert(c.size_bytes() == m.size_bytes() + crypto_box_MACBYTES); | |
| 533 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 534 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 535 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 536 | ||
| 537 | return crypto_box_easy(c.data(), m.data(), m.size_bytes(), n.data(), pk.data(), sk.data()); | |
| 538 | } | |
| 539 | ||
| 540 | static inline bool | |
| 541 | sn_crypto_box_open_easy( | |
| 542 | js_env_t *, | |
| 543 | js_receiver_t, | |
| 544 | js_typedarray_span_t<> m, | |
| 545 | js_typedarray_span_t<> c, | |
| 546 | js_typedarray_span_t<> n, | |
| 547 | js_typedarray_span_t<> pk, | |
| 548 | js_typedarray_span_t<> sk | |
| 549 | ) { | |
| 550 | assert(c.size_bytes() >= crypto_box_MACBYTES); | |
| 551 | assert(m.size_bytes() == c.size_bytes() - crypto_box_MACBYTES); | |
| 552 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 553 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 554 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 555 | ||
| 556 | return crypto_box_open_easy(m.data(), c.data(), c.size_bytes(), n.data(), pk.data(), sk.data()) == 0; | |
| 557 | } | |
| 558 | ||
| 559 | static inline int | |
| 560 | sn_crypto_box_detached( | |
| 561 | js_env_t *, | |
| 562 | js_receiver_t, | |
| 563 | js_typedarray_span_t<> c, | |
| 564 | js_typedarray_span_t<> mac, | |
| 565 | js_typedarray_span_t<> m, | |
| 566 | js_typedarray_span_t<> n, | |
| 567 | js_typedarray_span_t<> pk, | |
| 568 | js_typedarray_span_t<> sk | |
| 569 | ) { | |
| 570 | assert(c.size_bytes() == m.size_bytes()); | |
| 571 | assert(mac.size_bytes() == crypto_box_MACBYTES); | |
| 572 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 573 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 574 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 575 | ||
| 576 | return crypto_box_detached(c.data(), mac.data(), m.data(), m.size_bytes(), n.data(), pk.data(), sk.data()); | |
| 577 | } | |
| 578 | ||
| 579 | static inline bool | |
| 580 | sn_crypto_box_open_detached( | |
| 581 | js_env_t *, | |
| 582 | js_receiver_t, | |
| 583 | js_typedarray_span_t<> m, | |
| 584 | js_typedarray_span_t<> c, | |
| 585 | js_typedarray_span_t<> mac, | |
| 586 | js_typedarray_span_t<> n, | |
| 587 | js_typedarray_span_t<> pk, | |
| 588 | js_typedarray_span_t<> sk | |
| 589 | ) { | |
| 590 | assert(m.size_bytes() == c.size_bytes()); | |
| 591 | assert(mac.size_bytes() == crypto_box_MACBYTES); | |
| 592 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 593 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 594 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 595 | ||
| 596 | return crypto_box_open_detached(m.data(), c.data(), mac.data(), c.size_bytes(), n.data(), pk.data(), sk.data()) == 0; | |
| 597 | } | |
| 598 | ||
| 599 | static inline int | |
| 600 | sn_crypto_box_seal( | |
| 601 | js_env_t *, | |
| 602 | js_receiver_t, | |
| 603 | js_typedarray_span_t<> c, | |
| 604 | js_typedarray_span_t<> m, | |
| 605 | js_typedarray_span_t<> pk | |
| 606 | ) { | |
| 607 | assert(c.size_bytes() == m.size_bytes() + crypto_box_SEALBYTES); | |
| 608 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 609 | ||
| 610 | return crypto_box_seal(c.data(), m.data(), m.size_bytes(), pk.data()); | |
| 611 | } | |
| 612 | ||
| 613 | static inline bool | |
| 614 | sn_crypto_box_seal_open( | |
| 615 | js_env_t *env, | |
| 616 | js_receiver_t, | |
| 617 | ||
| 618 | js_arraybuffer_span_t m, | |
| 619 | uint32_t m_offset, | |
| 620 | uint32_t m_len, | |
| 621 | ||
| 622 | js_arraybuffer_span_t c, | |
| 623 | uint32_t c_offset, | |
| 624 | uint32_t c_len, | |
| 625 | ||
| 626 | js_arraybuffer_span_t pk, | |
| 627 | uint32_t pk_offset, | |
| 628 | uint32_t pk_len, | |
| 629 | ||
| 630 | js_arraybuffer_span_t sk, | |
| 631 | uint32_t sk_offset, | |
| 632 | uint32_t sk_len | |
| 633 | ) { | |
| 634 | assert_bounds(m); | |
| 635 | assert_bounds(c); | |
| 636 | assert_bounds(pk); | |
| 637 | assert_bounds(sk); | |
| 638 | ||
| 639 | assert(m_len == c_len - crypto_box_SEALBYTES); | |
| 640 | assert(c_len >= crypto_box_SEALBYTES); | |
| 641 | assert(sk_len == crypto_box_SECRETKEYBYTES); | |
| 642 | assert(pk_len == crypto_box_PUBLICKEYBYTES); | |
| 643 | ||
| 644 | return crypto_box_seal_open(&m[m_offset], &c[c_offset], c_len, &pk[pk_offset], &sk[sk_offset]) == 0; | |
| 645 | } | |
| 646 | ||
| 647 | static inline int | |
| 648 | sn_crypto_secretbox_easy( | |
| 649 | js_env_t *, | |
| 650 | js_receiver_t, | |
| 651 | js_typedarray_span_t<> c, | |
| 652 | js_typedarray_span_t<> m, | |
| 653 | js_typedarray_span_t<> n, | |
| 654 | js_typedarray_span_t<> k | |
| 655 | ) { | |
| 656 | assert(c.size_bytes() == m.size_bytes() + crypto_secretbox_MACBYTES); | |
| 657 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 658 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 659 | ||
| 660 | return crypto_secretbox_easy(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 661 | } | |
| 662 | ||
| 663 | static inline bool | |
| 664 | sn_crypto_secretbox_open_easy( | |
| 665 | js_env_t *, | |
| 666 | js_receiver_t, | |
| 667 | js_typedarray_span_t<> m, | |
| 668 | js_typedarray_span_t<> c, | |
| 669 | js_typedarray_span_t<> n, | |
| 670 | js_typedarray_span_t<> k | |
| 671 | ) { | |
| 672 | assert(m.size_bytes() == c.size_bytes() - crypto_secretbox_MACBYTES); | |
| 673 | assert(c.size_bytes() >= crypto_secretbox_MACBYTES); | |
| 674 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 675 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 676 | ||
| 677 | return crypto_secretbox_open_easy(m.data(), c.data(), c.size_bytes(), n.data(), k.data()) == 0; | |
| 678 | } | |
| 679 | ||
| 680 | static inline int | |
| 681 | sn_crypto_secretbox_detached( | |
| 682 | js_env_t *, | |
| 683 | js_receiver_t, | |
| 684 | js_typedarray_span_t<> c, | |
| 685 | js_typedarray_span_t<> mac, | |
| 686 | js_typedarray_span_t<> m, | |
| 687 | js_typedarray_span_t<> n, | |
| 688 | js_typedarray_span_t<> k | |
| 689 | ) { | |
| 690 | assert(c.size_bytes() == m.size_bytes()); | |
| 691 | assert(mac.size_bytes() == crypto_secretbox_MACBYTES); | |
| 692 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 693 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 694 | ||
| 695 | return crypto_secretbox_detached(c.data(), mac.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 696 | } | |
| 697 | ||
| 698 | static inline bool | |
| 699 | sn_crypto_secretbox_open_detached( | |
| 700 | js_env_t *, | |
| 701 | js_receiver_t, | |
| 702 | js_typedarray_span_t<> m, | |
| 703 | js_typedarray_span_t<> c, | |
| 704 | js_typedarray_span_t<> mac, | |
| 705 | js_typedarray_span_t<> n, | |
| 706 | js_typedarray_span_t<> k | |
| 707 | ) { | |
| 708 | assert(m.size_bytes() == c.size_bytes()); | |
| 709 | assert(mac.size_bytes() == crypto_secretbox_MACBYTES); | |
| 710 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 711 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 712 | ||
| 713 | return crypto_secretbox_open_detached(m.data(), c.data(), mac.data(), c.size_bytes(), n.data(), k.data()) == 0; | |
| 714 | } | |
| 715 | ||
| 716 | static inline int | |
| 717 | sn_crypto_stream( | |
| 718 | js_env_t *, | |
| 719 | js_receiver_t, | |
| 720 | js_typedarray_span_t<> c, | |
| 721 | js_typedarray_span_t<> n, | |
| 722 | js_typedarray_span_t<> k | |
| 723 | ) { | |
| 724 | assert(n.size_bytes() == crypto_stream_NONCEBYTES); | |
| 725 | assert(k.size_bytes() == crypto_stream_KEYBYTES); | |
| 726 | ||
| 727 | return crypto_stream(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 728 | } | |
| 729 | ||
| 730 | static inline int | |
| 731 | sn_crypto_stream_xor( | |
| 732 | js_env_t *env, | |
| 733 | js_receiver_t, | |
| 734 | ||
| 735 | js_arraybuffer_span_t c, | |
| 736 | uint32_t c_offset, | |
| 737 | uint32_t c_len, | |
| 738 | ||
| 739 | js_arraybuffer_span_t m, | |
| 740 | uint32_t m_offset, | |
| 741 | uint32_t m_len, | |
| 742 | ||
| 743 | js_arraybuffer_span_t n, | |
| 744 | uint32_t n_offset, | |
| 745 | uint32_t n_len, | |
| 746 | ||
| 747 | js_arraybuffer_span_t k, | |
| 748 | uint32_t k_offset, | |
| 749 | uint32_t k_len | |
| 750 | ) { | |
| 751 | assert_bounds(c); | |
| 752 | assert_bounds(m); | |
| 753 | assert_bounds(n); | |
| 754 | assert_bounds(k); | |
| 755 | ||
| 756 | assert(c_len == m_len); | |
| 757 | assert(n_len == crypto_stream_NONCEBYTES); | |
| 758 | assert(k_len == crypto_stream_KEYBYTES); | |
| 759 | ||
| 760 | return crypto_stream_xor(&c[c_offset], &m[m_offset], m_len, &n[n_offset], &k[k_offset]); | |
| 761 | } | |
| 762 | ||
| 763 | static inline int | |
| 764 | sn_crypto_stream_chacha20( | |
| 765 | js_env_t *, | |
| 766 | js_receiver_t, | |
| 767 | js_typedarray_span_t<> c, | |
| 768 | js_typedarray_span_t<> n, | |
| 769 | js_typedarray_span_t<> k | |
| 770 | ) { | |
| 771 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 772 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 773 | ||
| 774 | return crypto_stream_chacha20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 775 | } | |
| 776 | ||
| 777 | static inline int | |
| 778 | sn_crypto_stream_chacha20_xor( | |
| 779 | js_env_t *, | |
| 780 | js_receiver_t, | |
| 781 | js_typedarray_span_t<> c, | |
| 782 | js_typedarray_span_t<> m, | |
| 783 | js_typedarray_span_t<> n, | |
| 784 | js_typedarray_span_t<> k | |
| 785 | ) { | |
| 786 | assert(c.size_bytes() == m.size_bytes()); | |
| 787 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 788 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 789 | ||
| 790 | return crypto_stream_chacha20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 791 | } | |
| 792 | ||
| 793 | static inline int | |
| 794 | sn_crypto_stream_chacha20_xor_ic( | |
| 795 | js_env_t *, | |
| 796 | js_receiver_t, | |
| 797 | js_typedarray_span_t<> c, | |
| 798 | js_typedarray_span_t<> m, | |
| 799 | js_typedarray_span_t<> n, | |
| 800 | uint32_t ic, | |
| 801 | js_typedarray_span_t<> k | |
| 802 | ) { | |
| 803 | assert(c.size_bytes() == m.size_bytes()); | |
| 804 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 805 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 806 | ||
| 807 | return crypto_stream_chacha20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 808 | } | |
| 809 | ||
| 810 | static inline int | |
| 811 | sn_crypto_stream_chacha20_ietf( | |
| 812 | js_env_t *, | |
| 813 | js_receiver_t, | |
| 814 | js_typedarray_span_t<> c, | |
| 815 | js_typedarray_span_t<> n, | |
| 816 | js_typedarray_span_t<> k | |
| 817 | ) { | |
| 818 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 819 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 820 | ||
| 821 | return crypto_stream_chacha20_ietf(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 822 | } | |
| 823 | ||
| 824 | static inline int | |
| 825 | sn_crypto_stream_chacha20_ietf_xor( | |
| 826 | js_env_t *, | |
| 827 | js_receiver_t, | |
| 828 | js_typedarray_span_t<> c, | |
| 829 | js_typedarray_span_t<> m, | |
| 830 | js_typedarray_span_t<> n, | |
| 831 | js_typedarray_span_t<> k | |
| 832 | ) { | |
| 833 | assert(c.size_bytes() == m.size_bytes()); | |
| 834 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 835 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 836 | ||
| 837 | return crypto_stream_chacha20_ietf_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 838 | } | |
| 839 | ||
| 840 | static inline int | |
| 841 | sn_crypto_stream_chacha20_ietf_xor_ic( | |
| 842 | js_env_t *, | |
| 843 | js_receiver_t, | |
| 844 | js_typedarray_span_t<> c, | |
| 845 | js_typedarray_span_t<> m, | |
| 846 | js_typedarray_span_t<> n, | |
| 847 | uint32_t ic, | |
| 848 | js_typedarray_span_t<> k | |
| 849 | ) { | |
| 850 | assert(c.size_bytes() == m.size_bytes()); | |
| 851 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 852 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 853 | ||
| 854 | return crypto_stream_chacha20_ietf_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 855 | } | |
| 856 | ||
| 857 | static inline int | |
| 858 | sn_crypto_stream_xchacha20( | |
| 859 | js_env_t *, | |
| 860 | js_receiver_t, | |
| 861 | js_typedarray_span_t<> c, | |
| 862 | js_typedarray_span_t<> n, | |
| 863 | js_typedarray_span_t<> k | |
| 864 | ) { | |
| 865 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 866 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 867 | ||
| 868 | return crypto_stream_xchacha20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 869 | } | |
| 870 | ||
| 871 | static inline int | |
| 872 | sn_crypto_stream_xchacha20_xor( | |
| 873 | js_env_t *, | |
| 874 | js_receiver_t, | |
| 875 | js_typedarray_span_t<> c, | |
| 876 | js_typedarray_span_t<> m, | |
| 877 | js_typedarray_span_t<> n, | |
| 878 | js_typedarray_span_t<> k | |
| 879 | ) { | |
| 880 | assert(c.size_bytes() == m.size_bytes()); | |
| 881 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 882 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 883 | ||
| 884 | return crypto_stream_xchacha20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 885 | } | |
| 886 | ||
| 887 | static inline int | |
| 888 | sn_crypto_stream_xchacha20_xor_ic( | |
| 889 | js_env_t *, | |
| 890 | js_receiver_t, | |
| 891 | js_typedarray_span_t<> c, | |
| 892 | js_typedarray_span_t<> m, | |
| 893 | js_typedarray_span_t<> n, | |
| 894 | uint32_t ic, | |
| 895 | js_typedarray_span_t<> k | |
| 896 | ) { | |
| 897 | assert(c.size_bytes() == m.size_bytes()); | |
| 898 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 899 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 900 | ||
| 901 | return crypto_stream_xchacha20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 902 | } | |
| 903 | ||
| 904 | static inline int | |
| 905 | sn_crypto_stream_salsa20( | |
| 906 | js_env_t *, | |
| 907 | js_receiver_t, | |
| 908 | js_typedarray_span_t<> c, | |
| 909 | js_typedarray_span_t<> n, | |
| 910 | js_typedarray_span_t<> k | |
| 911 | ) { | |
| 912 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 913 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 914 | ||
| 915 | return crypto_stream_salsa20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 916 | } | |
| 917 | ||
| 918 | static inline int | |
| 919 | sn_crypto_stream_salsa20_xor( | |
| 920 | js_env_t *, | |
| 921 | js_receiver_t, | |
| 922 | js_typedarray_span_t<> c, | |
| 923 | js_typedarray_span_t<> m, | |
| 924 | js_typedarray_span_t<> n, | |
| 925 | js_typedarray_span_t<> k | |
| 926 | ) { | |
| 927 | assert(c.size_bytes() == m.size_bytes()); | |
| 928 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 929 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 930 | ||
| 931 | return crypto_stream_salsa20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 932 | } | |
| 933 | ||
| 934 | static inline int | |
| 935 | sn_crypto_stream_salsa20_xor_ic( | |
| 936 | js_env_t *, | |
| 937 | js_receiver_t, | |
| 938 | js_typedarray_span_t<> c, | |
| 939 | js_typedarray_span_t<> m, | |
| 940 | js_typedarray_span_t<> n, | |
| 941 | uint32_t ic, | |
| 942 | js_typedarray_span_t<> k | |
| 943 | ) { | |
| 944 | assert(c.size_bytes() == m.size_bytes()); | |
| 945 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 946 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 947 | ||
| 948 | return crypto_stream_salsa20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 949 | } | |
| 950 | ||
| 951 | static inline int | |
| 952 | sn_crypto_auth( | |
| 953 | js_env_t *, | |
| 954 | js_receiver_t, | |
| 955 | js_typedarray_span_t<> out, | |
| 956 | js_typedarray_span_t<> in, | |
| 957 | js_typedarray_span_t<> k | |
| 958 | ) { | |
| 959 | assert(out.size_bytes() == crypto_auth_BYTES); | |
| 960 | assert(k.size_bytes() == crypto_auth_KEYBYTES); | |
| 961 | ||
| 962 | return crypto_auth(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 963 | } | |
| 964 | ||
| 965 | static inline bool | |
| 966 | sn_crypto_auth_verify( | |
| 967 | js_env_t *, | |
| 968 | js_receiver_t, | |
| 969 | js_typedarray_span_t<> h, | |
| 970 | js_typedarray_span_t<> in, | |
| 971 | js_typedarray_span_t<> k | |
| 972 | ) { | |
| 973 | assert(h.size_bytes() == crypto_auth_BYTES); | |
| 974 | assert(k.size_bytes() == crypto_auth_KEYBYTES); | |
| 975 | ||
| 976 | return crypto_auth_verify(h.data(), in.data(), in.size_bytes(), k.data()) == 0; | |
| 977 | } | |
| 978 | ||
| 979 | static inline int | |
| 980 | sn_crypto_onetimeauth( | |
| 981 | js_env_t *, | |
| 982 | js_receiver_t, | |
| 983 | js_typedarray_span_t<> out, | |
| 984 | js_typedarray_span_t<> in, | |
| 985 | js_typedarray_span_t<> k | |
| 986 | ) { | |
| 987 | assert(out.size_bytes() == crypto_onetimeauth_BYTES); | |
| 988 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 989 | ||
| 990 | return crypto_onetimeauth(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 991 | } | |
| 992 | ||
| 993 | static inline int | |
| 994 | sn_crypto_onetimeauth_init( | |
| 995 | js_env_t *, | |
| 996 | js_receiver_t, | |
| 997 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 998 | js_typedarray_span_t<> k | |
| 999 | ) { | |
| 1000 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 1001 | ||
| 1002 | return crypto_onetimeauth_init(&*state, k.data()); | |
| 1003 | } | |
| 1004 | ||
| 1005 | static inline int | |
| 1006 | sn_crypto_onetimeauth_update( | |
| 1007 | js_env_t *, | |
| 1008 | js_receiver_t, | |
| 1009 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 1010 | js_typedarray_span_t<> in | |
| 1011 | ) { | |
| 1012 | return crypto_onetimeauth_update(&*state, in.data(), in.size_bytes()); | |
| 1013 | } | |
| 1014 | ||
| 1015 | static inline int | |
| 1016 | sn_crypto_onetimeauth_final( | |
| 1017 | js_env_t *, | |
| 1018 | js_receiver_t, | |
| 1019 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 1020 | js_typedarray_span_t<> out | |
| 1021 | ) { | |
| 1022 | assert(out.size_bytes() == crypto_onetimeauth_BYTES); | |
| 1023 | ||
| 1024 | return crypto_onetimeauth_final(&*state, out.data()); | |
| 1025 | } | |
| 1026 | ||
| 1027 | static inline bool | |
| 1028 | sn_crypto_onetimeauth_verify( | |
| 1029 | js_env_t *, | |
| 1030 | js_receiver_t, | |
| 1031 | js_typedarray_span_t<> h, | |
| 1032 | js_typedarray_span_t<> in, | |
| 1033 | js_typedarray_span_t<> k | |
| 1034 | ) { | |
| 1035 | assert(h.size_bytes() == crypto_onetimeauth_BYTES); | |
| 1036 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 1037 | ||
| 1038 | return crypto_onetimeauth_verify(h.data(), in.data(), in.size_bytes(), k.data()) == 0; | |
| 1039 | } | |
| 1040 | ||
| 1041 | // CHECK: memlimit can be >32bit | |
| 1042 | static inline int | |
| 1043 | sn_crypto_pwhash( | |
| 1044 | js_env_t *, | |
| 1045 | js_receiver_t, | |
| 1046 | js_typedarray_span_t<> out, | |
| 1047 | js_typedarray_span_of_t<char> passwd, | |
| 1048 | js_typedarray_span_t<> salt, | |
| 1049 | uint64_t opslimit, | |
| 1050 | uint64_t memlimit, | |
| 1051 | int32_t alg | |
| 1052 | ) { | |
| 1053 | assert(out.size_bytes() >= crypto_pwhash_BYTES_MIN); | |
| 1054 | assert(out.size_bytes() <= crypto_pwhash_BYTES_MAX); | |
| 1055 | assert(salt.size_bytes() == crypto_pwhash_SALTBYTES); | |
| 1056 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1057 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1058 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1059 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1060 | assert(alg == 1 || alg == 2); // Argon2i or Argon2id | |
| 1061 | ||
| 1062 | return crypto_pwhash( | |
| 1063 | out.data(), | |
| 1064 | out.size_bytes(), | |
| 1065 | passwd.data(), | |
| 1066 | passwd.size_bytes(), | |
| 1067 | salt.data(), | |
| 1068 | opslimit, | |
| 1069 | memlimit, | |
| 1070 | alg | |
| 1071 | ); | |
| 1072 | } | |
| 1073 | ||
| 1074 | static inline int | |
| 1075 | sn_crypto_pwhash_str( | |
| 1076 | js_env_t *, | |
| 1077 | js_receiver_t, | |
| 1078 | js_typedarray_span_of_t<char> out, | |
| 1079 | js_typedarray_span_of_t<char> passwd, | |
| 1080 | uint64_t opslimit, | |
| 1081 | uint64_t memlimit | |
| 1082 | ) { | |
| 1083 | assert(out.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1084 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1085 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1086 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1087 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1088 | ||
| 1089 | return crypto_pwhash_str( | |
| 1090 | out.data(), | |
| 1091 | passwd.data(), | |
| 1092 | passwd.size_bytes(), | |
| 1093 | opslimit, | |
| 1094 | memlimit | |
| 1095 | ); | |
| 1096 | } | |
| 1097 | ||
| 1098 | static inline bool | |
| 1099 | sn_crypto_pwhash_str_verify( | |
| 1100 | js_env_t *, | |
| 1101 | js_receiver_t, | |
| 1102 | js_typedarray_span_of_t<char> str, | |
| 1103 | js_typedarray_span_of_t<char> passwd | |
| 1104 | ) { | |
| 1105 | assert(str.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1106 | ||
| 1107 | return crypto_pwhash_str_verify(str.data(), passwd.data(), passwd.size_bytes()) == 0; | |
| 1108 | } | |
| 1109 | ||
| 1110 | // CHECK: returns 1, 0, -1 | |
| 1111 | static inline bool | |
| 1112 | sn_crypto_pwhash_str_needs_rehash( | |
| 1113 | js_env_t *, | |
| 1114 | js_receiver_t, | |
| 1115 | js_typedarray_span_of_t<char> str, | |
| 1116 | uint64_t opslimit, | |
| 1117 | uint64_t memlimit | |
| 1118 | ) { | |
| 1119 | assert(str.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1120 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1121 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1122 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1123 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1124 | ||
| 1125 | return crypto_pwhash_str_needs_rehash(str.data(), opslimit, memlimit) != 0; | |
| 1126 | } | |
| 1127 | ||
| 1128 | // CHECK: memlimit can be >32bit | |
| 1129 | static inline int | |
| 1130 | sn_crypto_pwhash_scryptsalsa208sha256( | |
| 1131 | js_env_t *, | |
| 1132 | js_receiver_t, | |
| 1133 | js_typedarray_span_t<> out, | |
| 1134 | js_typedarray_span_of_t<char> passwd, | |
| 1135 | js_typedarray_span_t<> salt, | |
| 1136 | uint64_t opslimit, | |
| 1137 | uint64_t memlimit | |
| 1138 | ) { | |
| 1139 | assert(out.size_bytes() >= crypto_pwhash_scryptsalsa208sha256_BYTES_MIN); | |
| 1140 | assert(out.size_bytes() <= crypto_pwhash_scryptsalsa208sha256_BYTES_MAX); | |
| 1141 | assert(salt.size_bytes() == crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 1142 | assert(opslimit >= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 1143 | assert(opslimit <= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 1144 | assert(memlimit >= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 1145 | assert(memlimit <= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 1146 | ||
| 1147 | return crypto_pwhash_scryptsalsa208sha256( | |
| 1148 | out.data(), | |
| 1149 | out.size_bytes(), | |
| 1150 | passwd.data(), | |
| 1151 | passwd.size_bytes(), | |
| 1152 | salt.data(), | |
| 1153 | opslimit, | |
| 1154 | memlimit | |
| 1155 | ); | |
| 1156 | } | |
| 1157 | ||
| 1158 | static inline int | |
| 1159 | sn_crypto_pwhash_scryptsalsa208sha256_str( | |
| 1160 | js_env_t *, | |
| 1161 | js_receiver_t, | |
| 1162 | js_typedarray_span_of_t<char> out, | |
| 1163 | js_typedarray_span_of_t<char> passwd, | |
| 1164 | uint64_t opslimit, | |
| 1165 | uint64_t memlimit | |
| 1166 | ) { | |
| 1167 | assert(out.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1168 | assert(opslimit >= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 1169 | assert(opslimit <= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 1170 | assert(memlimit >= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 1171 | assert(memlimit <= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 1172 | ||
| 1173 | return crypto_pwhash_scryptsalsa208sha256_str( | |
| 1174 | out.data(), | |
| 1175 | passwd.data(), | |
| 1176 | passwd.size_bytes(), | |
| 1177 | opslimit, | |
| 1178 | memlimit | |
| 1179 | ); | |
| 1180 | } | |
| 1181 | ||
| 1182 | static inline bool | |
| 1183 | sn_crypto_pwhash_scryptsalsa208sha256_str_verify( | |
| 1184 | js_env_t *, | |
| 1185 | js_receiver_t, | |
| 1186 | js_typedarray_span_of_t<char> str, | |
| 1187 | js_typedarray_span_of_t<char> passwd | |
| 1188 | ) { | |
| 1189 | assert(str.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1190 | ||
| 1191 | return crypto_pwhash_scryptsalsa208sha256_str_verify(str.data(), passwd.data(), passwd.size_bytes()) == 0; | |
| 1192 | } | |
| 1193 | ||
| 1194 | static inline bool | |
| 1195 | sn_crypto_pwhash_scryptsalsa208sha256_str_needs_rehash( | |
| 1196 | js_env_t *, | |
| 1197 | js_receiver_t, | |
| 1198 | js_typedarray_span_of_t<char> str, | |
| 1199 | uint64_t opslimit, | |
| 1200 | uint64_t memlimit | |
| 1201 | ) { | |
| 1202 | assert(str.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1203 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1204 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1205 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1206 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1207 | ||
| 1208 | return crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(str.data(), opslimit, memlimit) != 0; | |
| 1209 | } | |
| 1210 | ||
| 1211 | static inline int | |
| 1212 | sn_crypto_kx_keypair( | |
| 1213 | js_env_t *, | |
| 1214 | js_receiver_t, | |
| 1215 | js_typedarray_span_t<> pk, | |
| 1216 | js_typedarray_span_t<> sk | |
| 1217 | ) { | |
| 1218 | assert(pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1219 | assert(sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1220 | ||
| 1221 | return crypto_kx_keypair(pk.data(), sk.data()); | |
| 1222 | } | |
| 1223 | ||
| 1224 | static inline int | |
| 1225 | sn_crypto_kx_seed_keypair( | |
| 1226 | js_env_t *, | |
| 1227 | js_receiver_t, | |
| 1228 | js_typedarray_span_t<> pk, | |
| 1229 | js_typedarray_span_t<> sk, | |
| 1230 | js_typedarray_span_t<> seed | |
| 1231 | ) { | |
| 1232 | assert(pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1233 | assert(sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1234 | assert(seed.size_bytes() == crypto_kx_SEEDBYTES); | |
| 1235 | ||
| 1236 | return crypto_kx_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 1237 | } | |
| 1238 | ||
| 1239 | static inline int | |
| 1240 | sn_crypto_kx_client_session_keys( | |
| 1241 | js_env_t *, | |
| 1242 | js_receiver_t, | |
| 1243 | std::optional<js_typedarray_span_t<>> rx, | |
| 1244 | std::optional<js_typedarray_span_t<>> tx, | |
| 1245 | js_typedarray_span_t<> client_pk, | |
| 1246 | js_typedarray_span_t<> client_sk, | |
| 1247 | js_typedarray_span_t<> server_pk | |
| 1248 | ) { | |
| 1249 | assert(rx.has_value() || tx.has_value()); | |
| 1250 | ||
| 1251 | if (rx) assert(rx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1252 | if (tx) assert(tx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1253 | ||
| 1254 | assert(client_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1255 | assert(client_sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1256 | assert(server_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1257 | ||
| 1258 | return crypto_kx_client_session_keys( | |
| 1259 | rx ? rx->data() : nullptr, | |
| 1260 | tx ? tx->data() : nullptr, | |
| 1261 | client_pk.data(), | |
| 1262 | client_sk.data(), | |
| 1263 | server_pk.data() | |
| 1264 | ); | |
| 1265 | } | |
| 1266 | ||
| 1267 | static inline int | |
| 1268 | sn_crypto_kx_server_session_keys( | |
| 1269 | js_env_t *, | |
| 1270 | js_receiver_t, | |
| 1271 | std::optional<js_typedarray_span_t<>> rx, | |
| 1272 | std::optional<js_typedarray_span_t<>> tx, | |
| 1273 | js_typedarray_span_t<> server_pk, | |
| 1274 | js_typedarray_span_t<> server_sk, | |
| 1275 | js_typedarray_span_t<> client_pk | |
| 1276 | ) { | |
| 1277 | assert(rx.has_value() || tx.has_value()); | |
| 1278 | ||
| 1279 | if (rx) assert(rx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1280 | if (tx) assert(tx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1281 | ||
| 1282 | assert(server_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1283 | assert(server_sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1284 | assert(client_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1285 | ||
| 1286 | return crypto_kx_server_session_keys( | |
| 1287 | rx ? rx->data() : nullptr, | |
| 1288 | tx ? tx->data() : nullptr, | |
| 1289 | server_pk.data(), | |
| 1290 | server_sk.data(), | |
| 1291 | client_pk.data() | |
| 1292 | ); | |
| 1293 | } | |
| 1294 | ||
| 1295 | static inline int | |
| 1296 | sn_crypto_scalarmult_base( | |
| 1297 | js_env_t *, | |
| 1298 | js_receiver_t, | |
| 1299 | js_typedarray_span_t<> q, | |
| 1300 | js_typedarray_span_t<> n | |
| 1301 | ) { | |
| 1302 | assert(q.size_bytes() == crypto_scalarmult_BYTES); | |
| 1303 | assert(n.size_bytes() == crypto_scalarmult_SCALARBYTES); | |
| 1304 | ||
| 1305 | return crypto_scalarmult_base(q.data(), n.data()); | |
| 1306 | } | |
| 1307 | ||
| 1308 | static inline int | |
| 1309 | sn_crypto_scalarmult( | |
| 1310 | js_env_t *, | |
| 1311 | js_receiver_t, | |
| 1312 | js_typedarray_span_t<> q, | |
| 1313 | js_typedarray_span_t<> n, | |
| 1314 | js_typedarray_span_t<> p | |
| 1315 | ) { | |
| 1316 | assert(q.size_bytes() == crypto_scalarmult_BYTES); | |
| 1317 | assert(n.size_bytes() == crypto_scalarmult_SCALARBYTES); | |
| 1318 | assert(p.size_bytes() == crypto_scalarmult_BYTES); | |
| 1319 | ||
| 1320 | return crypto_scalarmult(q.data(), n.data(), p.data()); | |
| 1321 | } | |
| 1322 | ||
| 1323 | static inline int | |
| 1324 | sn_crypto_scalarmult_ed25519_base( | |
| 1325 | js_env_t *, | |
| 1326 | js_receiver_t, | |
| 1327 | js_typedarray_span_t<> q, | |
| 1328 | js_typedarray_span_t<> n | |
| 1329 | ) { | |
| 1330 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1331 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1332 | ||
| 1333 | return crypto_scalarmult_ed25519_base(q.data(), n.data()); | |
| 1334 | } | |
| 1335 | ||
| 1336 | static inline int | |
| 1337 | sn_crypto_scalarmult_ed25519( | |
| 1338 | js_env_t *, | |
| 1339 | js_receiver_t, | |
| 1340 | js_typedarray_span_t<> q, | |
| 1341 | js_typedarray_span_t<> n, | |
| 1342 | js_typedarray_span_t<> p | |
| 1343 | ) { | |
| 1344 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1345 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1346 | assert(p.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1347 | ||
| 1348 | return crypto_scalarmult_ed25519(q.data(), n.data(), p.data()); | |
| 1349 | } | |
| 1350 | ||
| 1351 | static inline bool | |
| 1352 | sn_crypto_core_ed25519_is_valid_point( | |
| 1353 | js_env_t *, | |
| 1354 | js_receiver_t, | |
| 1355 | js_typedarray_span_t<> p | |
| 1356 | ) { | |
| 1357 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1358 | ||
| 1359 | return crypto_core_ed25519_is_valid_point(p.data()) != 0; | |
| 1360 | } | |
| 1361 | ||
| 1362 | static inline int | |
| 1363 | sn_crypto_core_ed25519_from_uniform( | |
| 1364 | js_env_t *, | |
| 1365 | js_receiver_t, | |
| 1366 | js_typedarray_span_t<> p, | |
| 1367 | js_typedarray_span_t<> r | |
| 1368 | ) { | |
| 1369 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1370 | assert(r.size_bytes() == crypto_core_ed25519_UNIFORMBYTES); | |
| 1371 | ||
| 1372 | return crypto_core_ed25519_from_uniform(p.data(), r.data()); | |
| 1373 | } | |
| 1374 | ||
| 1375 | static inline int | |
| 1376 | sn_crypto_scalarmult_ed25519_base_noclamp( | |
| 1377 | js_env_t *, | |
| 1378 | js_receiver_t, | |
| 1379 | js_typedarray_span_t<> q, | |
| 1380 | js_typedarray_span_t<> n | |
| 1381 | ) { | |
| 1382 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1383 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1384 | ||
| 1385 | return crypto_scalarmult_ed25519_base_noclamp(q.data(), n.data()); | |
| 1386 | } | |
| 1387 | ||
| 1388 | static inline int | |
| 1389 | sn_crypto_scalarmult_ed25519_noclamp( | |
| 1390 | js_env_t *, | |
| 1391 | js_receiver_t, | |
| 1392 | js_typedarray_span_t<> q, | |
| 1393 | js_typedarray_span_t<> n, | |
| 1394 | js_typedarray_span_t<> p | |
| 1395 | ) { | |
| 1396 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1397 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1398 | assert(p.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1399 | ||
| 1400 | return crypto_scalarmult_ed25519_noclamp(q.data(), n.data(), p.data()); | |
| 1401 | } | |
| 1402 | ||
| 1403 | static inline int | |
| 1404 | sn_crypto_core_ed25519_add( | |
| 1405 | js_env_t *, | |
| 1406 | js_receiver_t, | |
| 1407 | js_typedarray_span_t<> r, | |
| 1408 | js_typedarray_span_t<> p, | |
| 1409 | js_typedarray_span_t<> q | |
| 1410 | ) { | |
| 1411 | assert(r.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1412 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1413 | assert(q.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1414 | ||
| 1415 | return crypto_core_ed25519_add(r.data(), p.data(), q.data()); | |
| 1416 | } | |
| 1417 | ||
| 1418 | static inline int | |
| 1419 | sn_crypto_core_ed25519_sub( | |
| 1420 | js_env_t *, | |
| 1421 | js_receiver_t, | |
| 1422 | js_typedarray_span_t<> r, | |
| 1423 | js_typedarray_span_t<> p, | |
| 1424 | js_typedarray_span_t<> q | |
| 1425 | ) { | |
| 1426 | assert(r.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1427 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1428 | assert(q.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1429 | ||
| 1430 | return crypto_core_ed25519_sub(r.data(), p.data(), q.data()); | |
| 1431 | } | |
| 1432 | ||
| 1433 | static inline void | |
| 1434 | sn_crypto_core_ed25519_scalar_random( | |
| 1435 | js_env_t *, | |
| 1436 | js_receiver_t, | |
| 1437 | js_typedarray_span_t<> r | |
| 1438 | ) { | |
| 1439 | assert(r.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1440 | ||
| 1441 | crypto_core_ed25519_scalar_random(r.data()); | |
| 1442 | } | |
| 1443 | ||
| 1444 | static inline void | |
| 1445 | sn_crypto_core_ed25519_scalar_reduce( | |
| 1446 | js_env_t *, | |
| 1447 | js_receiver_t, | |
| 1448 | js_typedarray_span_t<> r, | |
| 1449 | js_typedarray_span_t<> s | |
| 1450 | ) { | |
| 1451 | assert(r.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1452 | assert(s.size_bytes() == crypto_core_ed25519_NONREDUCEDSCALARBYTES); | |
| 1453 | ||
| 1454 | crypto_core_ed25519_scalar_reduce(r.data(), s.data()); | |
| 1455 | } | |
| 1456 | ||
| 1457 | static inline void | |
| 1458 | sn_crypto_core_ed25519_scalar_invert( | |
| 1459 | js_env_t *, | |
| 1460 | js_receiver_t, | |
| 1461 | js_typedarray_span_t<> recip, | |
| 1462 | js_typedarray_span_t<> s | |
| 1463 | ) { | |
| 1464 | assert(recip.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1465 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1466 | ||
| 1467 | crypto_core_ed25519_scalar_invert(recip.data(), s.data()); | |
| 1468 | } | |
| 1469 | ||
| 1470 | static inline void | |
| 1471 | sn_crypto_core_ed25519_scalar_negate( | |
| 1472 | js_env_t *, | |
| 1473 | js_receiver_t, | |
| 1474 | js_typedarray_span_t<> neg, | |
| 1475 | js_typedarray_span_t<> s | |
| 1476 | ) { | |
| 1477 | assert(neg.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1478 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1479 | ||
| 1480 | crypto_core_ed25519_scalar_negate(neg.data(), s.data()); | |
| 1481 | } | |
| 1482 | ||
| 1483 | static inline void | |
| 1484 | sn_crypto_core_ed25519_scalar_complement( | |
| 1485 | js_env_t *, | |
| 1486 | js_receiver_t, | |
| 1487 | js_typedarray_span_t<> comp, | |
| 1488 | js_typedarray_span_t<> s | |
| 1489 | ) { | |
| 1490 | assert(comp.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1491 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1492 | ||
| 1493 | crypto_core_ed25519_scalar_complement(comp.data(), s.data()); | |
| 1494 | } | |
| 1495 | ||
| 1496 | static inline void | |
| 1497 | sn_crypto_core_ed25519_scalar_add( | |
| 1498 | js_env_t *, | |
| 1499 | js_receiver_t, | |
| 1500 | js_typedarray_span_t<> z, | |
| 1501 | js_typedarray_span_t<> x, | |
| 1502 | js_typedarray_span_t<> y | |
| 1503 | ) { | |
| 1504 | assert(z.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1505 | assert(x.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1506 | assert(y.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1507 | ||
| 1508 | crypto_core_ed25519_scalar_add(z.data(), x.data(), y.data()); | |
| 1509 | } | |
| 1510 | ||
| 1511 | static inline void | |
| 1512 | sn_crypto_core_ed25519_scalar_sub( | |
| 1513 | js_env_t *, | |
| 1514 | js_receiver_t, | |
| 1515 | js_typedarray_span_t<> z, | |
| 1516 | js_typedarray_span_t<> x, | |
| 1517 | js_typedarray_span_t<> y | |
| 1518 | ) { | |
| 1519 | assert(z.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1520 | assert(x.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1521 | assert(y.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1522 | ||
| 1523 | crypto_core_ed25519_scalar_sub(z.data(), x.data(), y.data()); | |
| 1524 | } | |
| 1525 | ||
| 1526 | static inline int | |
| 1527 | sn_crypto_shorthash( | |
| 1528 | js_env_t *, | |
| 1529 | js_receiver_t, | |
| 1530 | js_typedarray_span_t<> out, | |
| 1531 | js_typedarray_span_t<> in, | |
| 1532 | js_typedarray_span_t<> k | |
| 1533 | ) { | |
| 1534 | assert(out.size_bytes() == crypto_shorthash_BYTES); | |
| 1535 | assert(k.size_bytes() == crypto_shorthash_KEYBYTES); | |
| 1536 | ||
| 1537 | return crypto_shorthash(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 1538 | } | |
| 1539 | ||
| 1540 | static inline void | |
| 1541 | sn_crypto_kdf_keygen( | |
| 1542 | js_env_t *, | |
| 1543 | js_receiver_t, | |
| 1544 | js_typedarray_span_t<> key | |
| 1545 | ) { | |
| 1546 | assert(key.size_bytes() == crypto_kdf_KEYBYTES); | |
| 1547 | ||
| 1548 | crypto_kdf_keygen(key.data()); | |
| 1549 | } | |
| 1550 | ||
| 1551 | static inline int | |
| 1552 | sn_crypto_kdf_derive_from_key( | |
| 1553 | js_env_t *, | |
| 1554 | js_receiver_t, | |
| 1555 | js_typedarray_span_t<> subkey, | |
| 1556 | uint64_t subkey_id, | |
| 1557 | js_typedarray_span_of_t<char> ctx, | |
| 1558 | js_typedarray_span_t<> key | |
| 1559 | ) { | |
| 1560 | assert(subkey.size_bytes() >= crypto_kdf_BYTES_MIN); | |
| 1561 | assert(subkey.size_bytes() <= crypto_kdf_BYTES_MAX); | |
| 1562 | assert(ctx.size_bytes() == crypto_kdf_CONTEXTBYTES); | |
| 1563 | assert(key.size_bytes() == crypto_kdf_KEYBYTES); | |
| 1564 | ||
| 1565 | return crypto_kdf_derive_from_key( | |
| 1566 | subkey.data(), | |
| 1567 | subkey.size_bytes(), | |
| 1568 | subkey_id, | |
| 1569 | ctx.data(), | |
| 1570 | key.data() | |
| 1571 | ); | |
| 1572 | } | |
| 1573 | ||
| 1574 | static inline int | |
| 1575 | sn_crypto_hash( | |
| 1576 | js_env_t *, | |
| 1577 | js_receiver_t, | |
| 1578 | js_typedarray_span_t<> out, | |
| 1579 | js_typedarray_span_t<> in | |
| 1580 | ) { | |
| 1581 | assert(out.size_bytes() == crypto_hash_BYTES); | |
| 1582 | ||
| 1583 | return crypto_hash(out.data(), in.data(), in.size_bytes()); | |
| 1584 | } | |
| 1585 | ||
| 1586 | static inline int | |
| 1587 | sn_crypto_hash_sha256( | |
| 1588 | js_env_t *, | |
| 1589 | js_receiver_t, | |
| 1590 | js_typedarray_span_t<> out, | |
| 1591 | js_typedarray_span_t<> in | |
| 1592 | ) { | |
| 1593 | assert(out.size_bytes() == crypto_hash_sha256_BYTES); | |
| 1594 | ||
| 1595 | return crypto_hash_sha256(out.data(), in.data(), in.size_bytes()); | |
| 1596 | } | |
| 1597 | ||
| 1598 | static inline int | |
| 1599 | sn_crypto_hash_sha256_init( | |
| 1600 | js_env_t *, | |
| 1601 | js_receiver_t, | |
| 1602 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state | |
| 1603 | ) { | |
| 1604 | return crypto_hash_sha256_init(&*state); | |
| 1605 | } | |
| 1606 | ||
| 1607 | static inline int | |
| 1608 | sn_crypto_hash_sha256_update( | |
| 1609 | js_env_t *, | |
| 1610 | js_receiver_t, | |
| 1611 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state, | |
| 1612 | js_typedarray_span_t<> in | |
| 1613 | ) { | |
| 1614 | return crypto_hash_sha256_update(&*state, in.data(), in.size_bytes()); | |
| 1615 | } | |
| 1616 | ||
| 1617 | static inline int | |
| 1618 | sn_crypto_hash_sha256_final( | |
| 1619 | js_env_t *, | |
| 1620 | js_receiver_t, | |
| 1621 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state, | |
| 1622 | js_typedarray_span_t<> out | |
| 1623 | ) { | |
| 1624 | assert(out.size_bytes() == crypto_hash_sha256_BYTES); | |
| 1625 | ||
| 1626 | return crypto_hash_sha256_final(&*state, out.data()); | |
| 1627 | } | |
| 1628 | ||
| 1629 | static inline int | |
| 1630 | sn_crypto_hash_sha512( | |
| 1631 | js_env_t *, | |
| 1632 | js_receiver_t, | |
| 1633 | js_typedarray_span_t<> out, | |
| 1634 | js_typedarray_span_t<> in | |
| 1635 | ) { | |
| 1636 | assert(out.size_bytes() == crypto_hash_sha512_BYTES); | |
| 1637 | ||
| 1638 | return crypto_hash_sha512(out.data(), in.data(), in.size_bytes()); | |
| 1639 | } | |
| 1640 | ||
| 1641 | static inline int | |
| 1642 | sn_crypto_hash_sha512_init( | |
| 1643 | js_env_t *, | |
| 1644 | js_receiver_t, | |
| 1645 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state | |
| 1646 | ) { | |
| 1647 | return crypto_hash_sha512_init(&*state); | |
| 1648 | } | |
| 1649 | ||
| 1650 | static inline int | |
| 1651 | sn_crypto_hash_sha512_update( | |
| 1652 | js_env_t *, | |
| 1653 | js_receiver_t, | |
| 1654 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state, | |
| 1655 | js_typedarray_span_t<> in | |
| 1656 | ) { | |
| 1657 | return crypto_hash_sha512_update(&*state, in.data(), in.size_bytes()); | |
| 1658 | } | |
| 1659 | ||
| 1660 | static inline int | |
| 1661 | sn_crypto_hash_sha512_final( | |
| 1662 | js_env_t *, | |
| 1663 | js_receiver_t, | |
| 1664 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state, | |
| 1665 | js_typedarray_span_t<> out | |
| 1666 | ) { | |
| 1667 | assert(out.size_bytes() == crypto_hash_sha512_BYTES); | |
| 1668 | ||
| 1669 | return crypto_hash_sha512_final(&*state, out.data()); | |
| 1670 | } | |
| 1671 | ||
| 1672 | static inline void | |
| 1673 | sn_crypto_aead_xchacha20poly1305_ietf_keygen( | |
| 1674 | js_env_t *, | |
| 1675 | js_receiver_t, | |
| 1676 | js_typedarray_span_t<> k | |
| 1677 | ) { | |
| 1678 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1679 | ||
| 1680 | crypto_aead_xchacha20poly1305_ietf_keygen(k.data()); | |
| 1681 | } | |
| 1682 | ||
| 1683 | static inline uint64_t | |
| 1684 | sn_crypto_aead_xchacha20poly1305_ietf_encrypt( | |
| 1685 | js_env_t *, | |
| 1686 | js_receiver_t, | |
| 1687 | js_typedarray_span_t<> c, | |
| 1688 | js_typedarray_span_t<> m, | |
| 1689 | std::optional<js_typedarray_span_t<>> ad, | |
| 1690 | js_typedarray_span_t<> npub, | |
| 1691 | js_typedarray_span_t<> k | |
| 1692 | ) { | |
| 1693 | assert(c.size_bytes() == m.size_bytes() + crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1694 | assert(c.size_bytes() <= 0xffffffff); | |
| 1695 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1696 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1697 | ||
| 1698 | uint8_t *ad_data = nullptr; | |
| 1699 | size_t ad_size = 0; | |
| 1700 | if (ad) { | |
| 1701 | ad_data = ad->data(); | |
| 1702 | ad_size = ad->size_bytes(); | |
| 1703 | } | |
| 1704 | ||
| 1705 | unsigned long long clen = 0; | |
| 1706 | int status = crypto_aead_xchacha20poly1305_ietf_encrypt( | |
| 1707 | c.data(), | |
| 1708 | &clen, | |
| 1709 | m.data(), | |
| 1710 | m.size_bytes(), | |
| 1711 | ad_data, | |
| 1712 | ad_size, | |
| 1713 | nullptr, | |
| 1714 | npub.data(), | |
| 1715 | k.data() | |
| 1716 | ); | |
| 1717 | ||
| 1718 | if (status < 0) return status; | |
| 1719 | ||
| 1720 | return clen; | |
| 1721 | } | |
| 1722 | ||
| 1723 | static inline uint64_t | |
| 1724 | sn_crypto_aead_xchacha20poly1305_ietf_decrypt( | |
| 1725 | js_env_t *, | |
| 1726 | js_receiver_t, | |
| 1727 | js_typedarray_span_t<> m, | |
| 1728 | js_typedarray_span_t<> c, | |
| 1729 | std::optional<js_typedarray_span_t<>> ad, | |
| 1730 | js_typedarray_span_t<> npub, | |
| 1731 | js_typedarray_span_t<> k | |
| 1732 | ) { | |
| 1733 | assert(m.size_bytes() == c.size_bytes() - crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1734 | assert(m.size_bytes() <= 0xffffffff); | |
| 1735 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1736 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1737 | ||
| 1738 | uint8_t *ad_data = nullptr; | |
| 1739 | size_t ad_size = 0; | |
| 1740 | if (ad) { | |
| 1741 | ad_data = ad->data(); | |
| 1742 | ad_size = ad->size_bytes(); | |
| 1743 | } | |
| 1744 | ||
| 1745 | unsigned long long mlen = 0; | |
| 1746 | int status = crypto_aead_xchacha20poly1305_ietf_decrypt( | |
| 1747 | m.data(), | |
| 1748 | &mlen, | |
| 1749 | nullptr, | |
| 1750 | c.data(), | |
| 1751 | c.size_bytes(), | |
| 1752 | ad_data, | |
| 1753 | ad_size, | |
| 1754 | npub.data(), | |
| 1755 | k.data() | |
| 1756 | ); | |
| 1757 | ||
| 1758 | if (status < 0) return status; | |
| 1759 | ||
| 1760 | return mlen; | |
| 1761 | } | |
| 1762 | ||
| 1763 | static inline uint64_t | |
| 1764 | sn_crypto_aead_xchacha20poly1305_ietf_encrypt_detached( | |
| 1765 | js_env_t *, | |
| 1766 | js_receiver_t, | |
| 1767 | js_typedarray_span_t<> c, | |
| 1768 | js_typedarray_span_t<> mac, | |
| 1769 | js_typedarray_span_t<> m, | |
| 1770 | std::optional<js_typedarray_span_t<>> ad, | |
| 1771 | js_typedarray_span_t<> npub, | |
| 1772 | js_typedarray_span_t<> k | |
| 1773 | ) { | |
| 1774 | assert(c.size_bytes() == m.size_bytes()); | |
| 1775 | assert(mac.size_bytes() == crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1776 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1777 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1778 | ||
| 1779 | uint8_t *ad_data = nullptr; | |
| 1780 | size_t ad_size = 0; | |
| 1781 | if (ad) { | |
| 1782 | ad_data = ad->data(); | |
| 1783 | ad_size = ad->size_bytes(); | |
| 1784 | } | |
| 1785 | ||
| 1786 | unsigned long long maclen = 0; | |
| 1787 | int status = crypto_aead_xchacha20poly1305_ietf_encrypt_detached( | |
| 1788 | c.data(), | |
| 1789 | mac.data(), | |
| 1790 | &maclen, | |
| 1791 | m.data(), | |
| 1792 | m.size_bytes(), | |
| 1793 | ad_data, | |
| 1794 | ad_size, | |
| 1795 | nullptr, | |
| 1796 | npub.data(), | |
| 1797 | k.data() | |
| 1798 | ); | |
| 1799 | ||
| 1800 | if (status < 0) return status; | |
| 1801 | ||
| 1802 | return maclen; | |
| 1803 | } | |
| 1804 | ||
| 1805 | static inline int | |
| 1806 | sn_crypto_aead_xchacha20poly1305_ietf_decrypt_detached( | |
| 1807 | js_env_t *, | |
| 1808 | js_receiver_t, | |
| 1809 | js_typedarray_span_t<> m, | |
| 1810 | js_typedarray_span_t<> c, | |
| 1811 | js_typedarray_span_t<> mac, | |
| 1812 | std::optional<js_typedarray_span_t<>> ad, | |
| 1813 | js_typedarray_span_t<> npub, | |
| 1814 | js_typedarray_span_t<> k | |
| 1815 | ) { | |
| 1816 | assert(m.size_bytes() == c.size_bytes()); | |
| 1817 | assert(mac.size_bytes() == crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1818 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1819 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1820 | ||
| 1821 | uint8_t *ad_data = nullptr; | |
| 1822 | size_t ad_size = 0; | |
| 1823 | if (ad) { | |
| 1824 | ad_data = ad->data(); | |
| 1825 | ad_size = ad->size_bytes(); | |
| 1826 | } | |
| 1827 | ||
| 1828 | return crypto_aead_xchacha20poly1305_ietf_decrypt_detached( | |
| 1829 | m.data(), | |
| 1830 | nullptr, | |
| 1831 | c.data(), | |
| 1832 | c.size_bytes(), | |
| 1833 | mac.data(), | |
| 1834 | ad_data, | |
| 1835 | ad_size, | |
| 1836 | npub.data(), | |
| 1837 | k.data() | |
| 1838 | ); | |
| 1839 | } | |
| 1840 | ||
| 1841 | static inline void | |
| 1842 | sn_crypto_aead_chacha20poly1305_ietf_keygen( | |
| 1843 | js_env_t *, | |
| 1844 | js_receiver_t, | |
| 1845 | js_typedarray_span_t<> k | |
| 1846 | ) { | |
| 1847 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1848 | ||
| 1849 | crypto_aead_chacha20poly1305_ietf_keygen(k.data()); | |
| 1850 | } | |
| 1851 | ||
| 1852 | static inline uint64_t | |
| 1853 | sn_crypto_aead_chacha20poly1305_ietf_encrypt( | |
| 1854 | js_env_t *, | |
| 1855 | js_receiver_t, | |
| 1856 | js_typedarray_span_t<> c, | |
| 1857 | js_typedarray_span_t<> m, | |
| 1858 | std::optional<js_typedarray_span_t<>> ad, | |
| 1859 | js_typedarray_span_t<> npub, | |
| 1860 | js_typedarray_span_t<> k | |
| 1861 | ) { | |
| 1862 | assert(c.size_bytes() == m.size_bytes() + crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1863 | assert(c.size_bytes() <= 0xffffffff); | |
| 1864 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1865 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1866 | ||
| 1867 | uint8_t *ad_data = nullptr; | |
| 1868 | size_t ad_size = 0; | |
| 1869 | if (ad) { | |
| 1870 | ad_data = ad->data(); | |
| 1871 | ad_size = ad->size_bytes(); | |
| 1872 | } | |
| 1873 | ||
| 1874 | unsigned long long clen = 0; | |
| 1875 | int status = crypto_aead_chacha20poly1305_ietf_encrypt( | |
| 1876 | c.data(), | |
| 1877 | &clen, | |
| 1878 | m.data(), | |
| 1879 | m.size_bytes(), | |
| 1880 | ad_data, | |
| 1881 | ad_size, | |
| 1882 | nullptr, | |
| 1883 | npub.data(), | |
| 1884 | k.data() | |
| 1885 | ); | |
| 1886 | ||
| 1887 | if (status < 0) return status; | |
| 1888 | ||
| 1889 | return clen; | |
| 1890 | } | |
| 1891 | ||
| 1892 | static inline uint64_t | |
| 1893 | sn_crypto_aead_chacha20poly1305_ietf_decrypt( | |
| 1894 | js_env_t *, | |
| 1895 | js_receiver_t, | |
| 1896 | js_typedarray_span_t<> m, | |
| 1897 | js_typedarray_span_t<> c, | |
| 1898 | std::optional<js_typedarray_span_t<>> ad, | |
| 1899 | js_typedarray_span_t<> npub, | |
| 1900 | js_typedarray_span_t<> k | |
| 1901 | ) { | |
| 1902 | assert(m.size_bytes() == c.size_bytes() - crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1903 | assert(m.size_bytes() <= 0xffffffff); | |
| 1904 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1905 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1906 | ||
| 1907 | uint8_t *ad_data = nullptr; | |
| 1908 | size_t ad_size = 0; | |
| 1909 | if (ad) { | |
| 1910 | ad_data = ad->data(); | |
| 1911 | ad_size = ad->size_bytes(); | |
| 1912 | } | |
| 1913 | ||
| 1914 | unsigned long long mlen = 0; | |
| 1915 | int status = crypto_aead_chacha20poly1305_ietf_decrypt( | |
| 1916 | m.data(), | |
| 1917 | &mlen, | |
| 1918 | nullptr, | |
| 1919 | c.data(), | |
| 1920 | c.size_bytes(), | |
| 1921 | ad_data, | |
| 1922 | ad_size, | |
| 1923 | npub.data(), | |
| 1924 | k.data() | |
| 1925 | ); | |
| 1926 | ||
| 1927 | if (status < 0) return status; | |
| 1928 | ||
| 1929 | return mlen; | |
| 1930 | } | |
| 1931 | ||
| 1932 | static inline uint64_t | |
| 1933 | sn_crypto_aead_chacha20poly1305_ietf_encrypt_detached( | |
| 1934 | js_env_t *, | |
| 1935 | js_receiver_t, | |
| 1936 | js_typedarray_span_t<> c, | |
| 1937 | js_typedarray_span_t<> mac, | |
| 1938 | js_typedarray_span_t<> m, | |
| 1939 | std::optional<js_typedarray_span_t<>> ad, | |
| 1940 | js_typedarray_span_t<> npub, | |
| 1941 | js_typedarray_span_t<> k | |
| 1942 | ) { | |
| 1943 | assert(c.size_bytes() == m.size_bytes()); | |
| 1944 | assert(mac.size_bytes() == crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1945 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1946 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1947 | ||
| 1948 | uint8_t *ad_data = nullptr; | |
| 1949 | size_t ad_size = 0; | |
| 1950 | if (ad) { | |
| 1951 | ad_data = ad->data(); | |
| 1952 | ad_size = ad->size_bytes(); | |
| 1953 | } | |
| 1954 | ||
| 1955 | unsigned long long maclen = 0; | |
| 1956 | int status = crypto_aead_chacha20poly1305_ietf_encrypt_detached( | |
| 1957 | c.data(), | |
| 1958 | mac.data(), | |
| 1959 | &maclen, | |
| 1960 | m.data(), | |
| 1961 | m.size_bytes(), | |
| 1962 | ad_data, | |
| 1963 | ad_size, | |
| 1964 | nullptr, | |
| 1965 | npub.data(), | |
| 1966 | k.data() | |
| 1967 | ); | |
| 1968 | ||
| 1969 | if (status < 0) return status; | |
| 1970 | ||
| 1971 | return maclen; | |
| 1972 | } | |
| 1973 | ||
| 1974 | static inline int | |
| 1975 | sn_crypto_aead_chacha20poly1305_ietf_decrypt_detached( | |
| 1976 | js_env_t *, | |
| 1977 | js_receiver_t, | |
| 1978 | js_typedarray_span_t<> m, | |
| 1979 | js_typedarray_span_t<> c, | |
| 1980 | js_typedarray_span_t<> mac, | |
| 1981 | std::optional<js_typedarray_span_t<>> ad, | |
| 1982 | js_typedarray_span_t<> npub, | |
| 1983 | js_typedarray_span_t<> k | |
| 1984 | ) { | |
| 1985 | assert(m.size_bytes() == c.size_bytes()); | |
| 1986 | assert(mac.size_bytes() == crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1987 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1988 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1989 | ||
| 1990 | uint8_t *ad_data = nullptr; | |
| 1991 | size_t ad_size = 0; | |
| 1992 | if (ad) { | |
| 1993 | ad_data = ad->data(); | |
| 1994 | ad_size = ad->size_bytes(); | |
| 1995 | } | |
| 1996 | ||
| 1997 | return crypto_aead_chacha20poly1305_ietf_decrypt_detached( | |
| 1998 | m.data(), | |
| 1999 | nullptr, | |
| 2000 | c.data(), | |
| 2001 | c.size_bytes(), | |
| 2002 | mac.data(), | |
| 2003 | ad_data, | |
| 2004 | ad_size, | |
| 2005 | npub.data(), | |
| 2006 | k.data() | |
| 2007 | ); | |
| 2008 | } | |
| 2009 | ||
| 2010 | static inline void | |
| 2011 | sn_crypto_secretstream_xchacha20poly1305_keygen( | |
| 2012 | js_env_t *env, | |
| 2013 | js_receiver_t, | |
| 2014 | ||
| 2015 | js_arraybuffer_span_t k, | |
| 2016 | uint32_t k_offset, | |
| 2017 | uint32_t k_len | |
| 2018 | ) { | |
| 2019 | assert_bounds(k); | |
| 2020 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2021 | ||
| 2022 | crypto_secretstream_xchacha20poly1305_keygen(&k[k_offset]); | |
| 2023 | } | |
| 2024 | ||
| 2025 | static inline int | |
| 2026 | sn_crypto_secretstream_xchacha20poly1305_init_push( | |
| 2027 | js_env_t *env, | |
| 2028 | js_receiver_t, | |
| 2029 | ||
| 2030 | js_arraybuffer_span_t state, | |
| 2031 | uint32_t state_offset, | |
| 2032 | uint32_t state_len, | |
| 2033 | ||
| 2034 | js_arraybuffer_span_t header, | |
| 2035 | uint32_t header_offset, | |
| 2036 | uint32_t header_len, | |
| 2037 | ||
| 2038 | js_arraybuffer_span_t k, | |
| 2039 | uint32_t k_offset, | |
| 2040 | uint32_t k_len | |
| 2041 | ) { | |
| 2042 | assert_bounds(state); | |
| 2043 | assert_bounds(header); | |
| 2044 | assert_bounds(k); | |
| 2045 | ||
| 2046 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2047 | assert(header_len == crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 2048 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2049 | ||
| 2050 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2051 | ||
| 2052 | return crypto_secretstream_xchacha20poly1305_init_push(state_data, &header[header_offset], &k[k_offset]); | |
| 2053 | } | |
| 2054 | ||
| 2055 | static inline uint64_t | |
| 2056 | sn_crypto_secretstream_xchacha20poly1305_push( | |
| 2057 | js_env_t *env, | |
| 2058 | js_receiver_t, | |
| 2059 | ||
| 2060 | js_arraybuffer_span_t state, | |
| 2061 | uint32_t state_offset, | |
| 2062 | uint32_t state_len, | |
| 2063 | ||
| 2064 | js_arraybuffer_span_t c, | |
| 2065 | uint32_t c_offset, | |
| 2066 | uint32_t c_len, | |
| 2067 | ||
| 2068 | js_arraybuffer_span_t m, | |
| 2069 | uint32_t m_offset, | |
| 2070 | uint32_t m_len, | |
| 2071 | ||
| 2072 | js_arraybuffer_span_t ad, | |
| 2073 | uint32_t ad_offset, | |
| 2074 | uint32_t ad_len, | |
| 2075 | ||
| 2076 | uint32_t tag | |
| 2077 | ) { | |
| 2078 | assert_bounds(state); | |
| 2079 | assert_bounds(c); | |
| 2080 | assert_bounds(m); | |
| 2081 | ||
| 2082 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2083 | assert(c_len == m_len + crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2084 | assert(c_len <= 0xffffffff && "32bit integer"); | |
| 2085 | ||
| 2086 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2087 | ||
| 2088 | uint8_t *ad_data = NULL; | |
| 2089 | if (ad_len) { | |
| 2090 | assert_bounds(ad); | |
| 2091 | ad_data = &ad[ad_offset]; | |
| 2092 | } | |
| 2093 | ||
| 2094 | unsigned long long clen = 0; | |
| 2095 | int status = crypto_secretstream_xchacha20poly1305_push( | |
| 2096 | state_data, | |
| 2097 | &c[c_offset], | |
| 2098 | &clen, | |
| 2099 | &m[m_offset], | |
| 2100 | m_len, | |
| 2101 | ad_data, | |
| 2102 | ad_len, | |
| 2103 | tag | |
| 2104 | ); | |
| 2105 | ||
| 2106 | if (status < 0) return status; | |
| 2107 | ||
| 2108 | return clen; | |
| 2109 | } | |
| 2110 | ||
| 2111 | static inline int | |
| 2112 | sn_crypto_secretstream_xchacha20poly1305_init_pull( | |
| 2113 | js_env_t *, | |
| 2114 | js_receiver_t, | |
| 2115 | ||
| 2116 | js_arraybuffer_span_t state, | |
| 2117 | uint32_t state_offset, | |
| 2118 | uint32_t state_len, | |
| 2119 | ||
| 2120 | js_arraybuffer_span_t header, | |
| 2121 | uint32_t header_offset, | |
| 2122 | uint32_t header_len, | |
| 2123 | ||
| 2124 | js_arraybuffer_span_t k, | |
| 2125 | uint32_t k_offset, | |
| 2126 | uint32_t k_len | |
| 2127 | ) { | |
| 2128 | assert_bounds(state); | |
| 2129 | assert_bounds(header); | |
| 2130 | assert_bounds(k); | |
| 2131 | ||
| 2132 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2133 | assert(header_len == crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 2134 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2135 | ||
| 2136 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2137 | ||
| 2138 | return crypto_secretstream_xchacha20poly1305_init_pull(state_data, &header[header_offset], &k[k_offset]); | |
| 2139 | } | |
| 2140 | ||
| 2141 | static inline uint64_t | |
| 2142 | sn_crypto_secretstream_xchacha20poly1305_pull( | |
| 2143 | js_env_t *env, | |
| 2144 | js_receiver_t, | |
| 2145 | ||
| 2146 | js_arraybuffer_span_t state, | |
| 2147 | uint32_t state_offset, | |
| 2148 | uint32_t state_len, | |
| 2149 | ||
| 2150 | js_arraybuffer_span_t m, | |
| 2151 | uint32_t m_offset, | |
| 2152 | uint32_t m_len, | |
| 2153 | ||
| 2154 | js_arraybuffer_span_t tag, | |
| 2155 | uint32_t tag_offset, | |
| 2156 | uint32_t tag_len, | |
| 2157 | ||
| 2158 | js_arraybuffer_span_t c, | |
| 2159 | uint32_t c_offset, | |
| 2160 | uint32_t c_len, | |
| 2161 | ||
| 2162 | js_arraybuffer_span_t ad, | |
| 2163 | uint32_t ad_offset, | |
| 2164 | uint32_t ad_len | |
| 2165 | ) { | |
| 2166 | assert_bounds(state); | |
| 2167 | assert_bounds(m); | |
| 2168 | assert_bounds(tag); | |
| 2169 | assert_bounds(c); | |
| 2170 | ||
| 2171 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2172 | assert(c_len >= crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2173 | assert(tag_len == 1); | |
| 2174 | assert(m_len == c_len - crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2175 | assert(m_len <= 0xffffffff); | |
| 2176 | ||
| 2177 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2178 | ||
| 2179 | uint8_t *ad_data = NULL; | |
| 2180 | if (ad_len) { | |
| 2181 | assert_bounds(ad); | |
| 2182 | ad_data = &ad[ad_offset]; | |
| 2183 | } | |
| 2184 | ||
| 2185 | unsigned long long mlen = 0; | |
| 2186 | int status = crypto_secretstream_xchacha20poly1305_pull( | |
| 2187 | state_data, | |
| 2188 | &m[m_offset], | |
| 2189 | &mlen, | |
| 2190 | &tag[tag_offset], | |
| 2191 | &c[c_offset], | |
| 2192 | c_len, | |
| 2193 | ad_data, | |
| 2194 | ad_len | |
| 2195 | ); | |
| 2196 | ||
| 2197 | if (status < 0) return status; | |
| 2198 | ||
| 2199 | return mlen; | |
| 2200 | } | |
| 2201 | ||
| 2202 | static inline void | |
| 2203 | sn_crypto_secretstream_xchacha20poly1305_rekey( | |
| 2204 | js_env_t *, | |
| 2205 | js_receiver_t, | |
| 2206 | ||
| 2207 | js_arraybuffer_span_t state, | |
| 2208 | uint32_t state_offset, | |
| 2209 | uint32_t state_len | |
| 2210 | ) { | |
| 2211 | assert_bounds(state); | |
| 2212 | ||
| 2213 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2214 | ||
| 2215 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2216 | ||
| 2217 | crypto_secretstream_xchacha20poly1305_rekey(state_data); | |
| 2218 | } | |
| 2219 | ||
| 2220 | struct sn_async_task { | |
| 2221 | uv_work_t task; | |
| 2222 | js_env_t *env; | |
| 2223 | js_persistent_t<js_function_t<void, int>> cb; | |
| 2224 | js_deferred_teardown_t *teardown; | |
| 2225 | int code = 0; | |
| 2226 | bool exiting = false; | |
| 2227 | ||
| 2228 | sn_async_task(js_env_t *env) : env(env) { | |
| 2229 | int err; | |
| 2230 | err = js_add_teardown_callback<on_teardown, sn_async_task>(env, this, teardown); | |
| 2231 | assert(err == 0); | |
| 2232 | } | |
| 2233 | ||
| 2234 | ~sn_async_task() { | |
| 2235 | int err; | |
| 2236 | err = js_finish_teardown_callback(teardown); | |
| 2237 | assert(err == 0); | |
| 2238 | } | |
| 2239 | ||
| 2240 | private: | |
| 2241 | static void | |
| 2242 | on_teardown(js_deferred_teardown_t *teardown, sn_async_task *req) { | |
| 2243 | req->exiting = true; | |
| 2244 | } | |
| 2245 | }; | |
| 2246 | ||
| 2247 | struct sn_async_pwhash_request : sn_async_task { | |
| 2248 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2249 | std::span<uint8_t> out; | |
| 2250 | ||
| 2251 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2252 | std::span<char> pwd; | |
| 2253 | ||
| 2254 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 2255 | std::span<uint8_t> salt; | |
| 2256 | ||
| 2257 | uint64_t opslimit; | |
| 2258 | size_t memlimit; | |
| 2259 | int alg; | |
| 2260 | ||
| 2261 | sn_async_pwhash_request(js_env_t *env) : sn_async_task(env) {} | |
| 2262 | }; | |
| 2263 | ||
| 2264 | static void | |
| 2265 | async_pwhash_execute(uv_work_t *uv_req) { | |
| 2266 | auto req = reinterpret_cast<sn_async_pwhash_request *>(uv_req); | |
| 2267 | ||
| 2268 | req->code = crypto_pwhash( | |
| 2269 | req->out.data(), | |
| 2270 | req->out.size(), | |
| 2271 | req->pwd.data(), | |
| 2272 | req->pwd.size(), | |
| 2273 | req->salt.data(), | |
| 2274 | req->opslimit, | |
| 2275 | req->memlimit, | |
| 2276 | req->alg | |
| 2277 | ); | |
| 2278 | } | |
| 2279 | ||
| 2280 | static void | |
| 2281 | async_pwhash_complete(uv_work_t *uv_req, int status) { | |
| 2282 | int err; | |
| 2283 | ||
| 2284 | auto req = reinterpret_cast<sn_async_pwhash_request *>(uv_req); | |
| 2285 | ||
| 2286 | if (req->exiting) return delete req; | |
| 2287 | ||
| 2288 | js_handle_scope_t *scope; | |
| 2289 | err = js_open_handle_scope(req->env, &scope); | |
| 2290 | assert(err == 0); | |
| 2291 | ||
| 2292 | js_function_t<void, int> callback; | |
| 2293 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2294 | assert(err == 0); | |
| 2295 | ||
| 2296 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2297 | assert(err != js_pending_exception); | |
| 2298 | ||
| 2299 | err = js_close_handle_scope(req->env, scope); | |
| 2300 | assert(err == 0); | |
| 2301 | ||
| 2302 | delete req; | |
| 2303 | } | |
| 2304 | ||
| 2305 | static inline void | |
| 2306 | sn_crypto_pwhash_async( | |
| 2307 | js_env_t *env, | |
| 2308 | js_receiver_t, | |
| 2309 | ||
| 2310 | js_arraybuffer_t out, | |
| 2311 | uint32_t out_offset, | |
| 2312 | uint32_t out_len, | |
| 2313 | ||
| 2314 | js_arraybuffer_t pwd, | |
| 2315 | uint32_t pwd_offset, | |
| 2316 | uint32_t pwd_len, | |
| 2317 | ||
| 2318 | js_arraybuffer_t salt, | |
| 2319 | uint32_t salt_offset, | |
| 2320 | uint32_t salt_len, | |
| 2321 | ||
| 2322 | uint64_t opslimit, | |
| 2323 | uint64_t memlimit, | |
| 2324 | uint32_t alg, | |
| 2325 | ||
| 2326 | js_function_t<void, int> callback | |
| 2327 | ) { | |
| 2328 | int err; | |
| 2329 | ||
| 2330 | auto *req = new sn_async_pwhash_request(env); | |
| 2331 | ||
| 2332 | std::span<uint8_t> out_view; | |
| 2333 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2334 | assert(err == 0); | |
| 2335 | assert(out_offset + out_len <= out_view.size()); | |
| 2336 | ||
| 2337 | req->out = {&out_view[out_offset], out_len}; | |
| 2338 | ||
| 2339 | std::span<char> pwd_view; | |
| 2340 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2341 | assert(err == 0); | |
| 2342 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2343 | ||
| 2344 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2345 | ||
| 2346 | std::span<uint8_t> salt_view; | |
| 2347 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 2348 | assert(err == 0); | |
| 2349 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 2350 | ||
| 2351 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 2352 | ||
| 2353 | req->opslimit = opslimit; | |
| 2354 | req->memlimit = memlimit; | |
| 2355 | req->alg = alg; | |
| 2356 | ||
| 2357 | err = js_create_reference(env, out, req->out_ref); | |
| 2358 | assert(err == 0); | |
| 2359 | ||
| 2360 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2361 | assert(err == 0); | |
| 2362 | ||
| 2363 | err = js_create_reference(env, salt, req->salt_ref); | |
| 2364 | assert(err == 0); | |
| 2365 | ||
| 2366 | err = js_create_reference(env, callback, req->cb); | |
| 2367 | assert(err == 0); | |
| 2368 | ||
| 2369 | uv_loop_t *loop; | |
| 2370 | err = js_get_env_loop(env, &loop); | |
| 2371 | assert(err == 0); | |
| 2372 | ||
| 2373 | err = uv_queue_work(loop, &req->task, async_pwhash_execute, async_pwhash_complete); | |
| 2374 | assert(err == 0); | |
| 2375 | } | |
| 2376 | ||
| 2377 | struct sn_async_pwhash_str_request : sn_async_task { | |
| 2378 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2379 | std::span<char> out; | |
| 2380 | ||
| 2381 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2382 | std::span<char> pwd; | |
| 2383 | ||
| 2384 | uint64_t opslimit; | |
| 2385 | size_t memlimit; | |
| 2386 | ||
| 2387 | sn_async_pwhash_str_request(js_env_t *env) : sn_async_task(env) {} | |
| 2388 | }; | |
| 2389 | ||
| 2390 | static void | |
| 2391 | async_pwhash_str_execute(uv_work_t *uv_req) { | |
| 2392 | auto *req = reinterpret_cast<sn_async_pwhash_str_request *>(uv_req); | |
| 2393 | ||
| 2394 | req->code = crypto_pwhash_str( | |
| 2395 | req->out.data(), | |
| 2396 | req->pwd.data(), | |
| 2397 | req->pwd.size(), | |
| 2398 | req->opslimit, | |
| 2399 | req->memlimit | |
| 2400 | ); | |
| 2401 | } | |
| 2402 | ||
| 2403 | static void | |
| 2404 | async_pwhash_str_complete(uv_work_t *uv_req, int status) { | |
| 2405 | int err; | |
| 2406 | ||
| 2407 | auto *req = reinterpret_cast<sn_async_pwhash_str_request *>(uv_req); | |
| 2408 | ||
| 2409 | if (req->exiting) return delete req; | |
| 2410 | ||
| 2411 | js_handle_scope_t *scope; | |
| 2412 | err = js_open_handle_scope(req->env, &scope); | |
| 2413 | assert(err == 0); | |
| 2414 | ||
| 2415 | js_function_t<void, int> callback; | |
| 2416 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2417 | assert(err == 0); | |
| 2418 | ||
| 2419 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2420 | assert(err != js_pending_exception); | |
| 2421 | ||
| 2422 | err = js_close_handle_scope(req->env, scope); | |
| 2423 | assert(err == 0); | |
| 2424 | ||
| 2425 | delete req; | |
| 2426 | } | |
| 2427 | ||
| 2428 | static inline void | |
| 2429 | sn_crypto_pwhash_str_async( | |
| 2430 | js_env_t *env, | |
| 2431 | js_receiver_t, | |
| 2432 | ||
| 2433 | js_arraybuffer_t out, | |
| 2434 | uint32_t out_offset, | |
| 2435 | uint32_t out_len, | |
| 2436 | ||
| 2437 | js_arraybuffer_t pwd, | |
| 2438 | uint32_t pwd_offset, | |
| 2439 | uint32_t pwd_len, | |
| 2440 | ||
| 2441 | uint64_t opslimit, | |
| 2442 | uint64_t memlimit, | |
| 2443 | js_function_t<void, int> callback | |
| 2444 | ) { | |
| 2445 | int err; | |
| 2446 | ||
| 2447 | auto *req = new sn_async_pwhash_str_request(env); | |
| 2448 | ||
| 2449 | std::span<char> out_view; | |
| 2450 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2451 | assert(err == 0); | |
| 2452 | assert(out_offset + out_len <= out_view.size()); | |
| 2453 | ||
| 2454 | req->out = {&out_view[out_offset], out_len}; | |
| 2455 | ||
| 2456 | std::span<char> pwd_view; | |
| 2457 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2458 | assert(err == 0); | |
| 2459 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2460 | ||
| 2461 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2462 | ||
| 2463 | req->opslimit = opslimit; | |
| 2464 | req->memlimit = memlimit; | |
| 2465 | ||
| 2466 | err = js_create_reference(env, out, req->out_ref); | |
| 2467 | assert(err == 0); | |
| 2468 | ||
| 2469 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2470 | assert(err == 0); | |
| 2471 | ||
| 2472 | err = js_create_reference(env, callback, req->cb); | |
| 2473 | assert(err == 0); | |
| 2474 | ||
| 2475 | uv_loop_t *loop; | |
| 2476 | err = js_get_env_loop(env, &loop); | |
| 2477 | assert(err == 0); | |
| 2478 | ||
| 2479 | err = uv_queue_work(loop, &req->task, async_pwhash_str_execute, async_pwhash_str_complete); | |
| 2480 | assert(err == 0); | |
| 2481 | } | |
| 2482 | ||
| 2483 | struct sn_async_pwhash_str_verify_request : sn_async_task { | |
| 2484 | js_persistent_t<js_arraybuffer_t> str_ref; | |
| 2485 | std::span<char> str; | |
| 2486 | ||
| 2487 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2488 | std::span<char> pwd; | |
| 2489 | ||
| 2490 | sn_async_pwhash_str_verify_request(js_env_t *env) : sn_async_task(env) {} | |
| 2491 | }; | |
| 2492 | ||
| 2493 | static void | |
| 2494 | async_pwhash_str_verify_execute(uv_work_t *uv_req) { | |
| 2495 | auto *req = reinterpret_cast<sn_async_pwhash_str_verify_request *>(uv_req); | |
| 2496 | ||
| 2497 | req->code = crypto_pwhash_str_verify(req->str.data(), req->pwd.data(), req->pwd.size()); | |
| 2498 | } | |
| 2499 | ||
| 2500 | static void | |
| 2501 | async_pwhash_str_verify_complete(uv_work_t *uv_req, int status) { | |
| 2502 | int err; | |
| 2503 | ||
| 2504 | auto *req = reinterpret_cast<sn_async_pwhash_str_verify_request *>(uv_req); | |
| 2505 | ||
| 2506 | if (req->exiting) return delete req; | |
| 2507 | ||
| 2508 | js_handle_scope_t *scope; | |
| 2509 | err = js_open_handle_scope(req->env, &scope); | |
| 2510 | assert(err == 0); | |
| 2511 | ||
| 2512 | js_function_t<void, int> callback; | |
| 2513 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2514 | assert(err == 0); | |
| 2515 | ||
| 2516 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2517 | assert(err != js_pending_exception); | |
| 2518 | ||
| 2519 | err = js_close_handle_scope(req->env, scope); | |
| 2520 | assert(err == 0); | |
| 2521 | ||
| 2522 | delete req; | |
| 2523 | } | |
| 2524 | ||
| 2525 | static inline void | |
| 2526 | sn_crypto_pwhash_str_verify_async( | |
| 2527 | js_env_t *env, | |
| 2528 | js_receiver_t, | |
| 2529 | ||
| 2530 | js_arraybuffer_t str, | |
| 2531 | uint32_t str_offset, | |
| 2532 | uint32_t str_len, | |
| 2533 | ||
| 2534 | js_arraybuffer_t pwd, | |
| 2535 | uint32_t pwd_offset, | |
| 2536 | uint32_t pwd_len, | |
| 2537 | ||
| 2538 | js_function_t<void, int> callback | |
| 2539 | ) { | |
| 2540 | int err; | |
| 2541 | ||
| 2542 | auto *req = new sn_async_pwhash_str_verify_request(env); | |
| 2543 | ||
| 2544 | std::span<char> str_view; | |
| 2545 | err = js_get_arraybuffer_info(env, str, str_view); | |
| 2546 | assert(err == 0); | |
| 2547 | assert(str_offset + str_len <= str_view.size()); | |
| 2548 | ||
| 2549 | req->str = {&str_view[str_offset], str_len}; | |
| 2550 | ||
| 2551 | std::span<char> pwd_view; | |
| 2552 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2553 | assert(err == 0); | |
| 2554 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2555 | ||
| 2556 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2557 | ||
| 2558 | err = js_create_reference(env, str, req->str_ref); | |
| 2559 | assert(err == 0); | |
| 2560 | ||
| 2561 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2562 | assert(err == 0); | |
| 2563 | ||
| 2564 | err = js_create_reference(env, callback, req->cb); | |
| 2565 | assert(err == 0); | |
| 2566 | ||
| 2567 | uv_loop_t *loop; | |
| 2568 | err = js_get_env_loop(env, &loop); | |
| 2569 | assert(err == 0); | |
| 2570 | ||
| 2571 | err = uv_queue_work(loop, &req->task, async_pwhash_str_verify_execute, async_pwhash_str_verify_complete); | |
| 2572 | assert(err == 0); | |
| 2573 | } | |
| 2574 | ||
| 2575 | struct sn_async_pwhash_scryptsalsa208sha256_request : sn_async_task { | |
| 2576 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2577 | std::span<uint8_t> out; | |
| 2578 | ||
| 2579 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2580 | std::span<char> pwd; | |
| 2581 | ||
| 2582 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 2583 | std::span<uint8_t> salt; | |
| 2584 | ||
| 2585 | uint64_t opslimit; | |
| 2586 | size_t memlimit; | |
| 2587 | ||
| 2588 | sn_async_pwhash_scryptsalsa208sha256_request(js_env_t *env) : sn_async_task(env) {} | |
| 2589 | }; | |
| 2590 | ||
| 2591 | static void | |
| 2592 | async_pwhash_scryptsalsa208sha256_execute(uv_work_t *uv_req) { | |
| 2593 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_request *>(uv_req); | |
| 2594 | ||
| 2595 | req->code = crypto_pwhash_scryptsalsa208sha256( | |
| 2596 | req->out.data(), | |
| 2597 | req->out.size(), | |
| 2598 | req->pwd.data(), | |
| 2599 | req->pwd.size(), | |
| 2600 | req->salt.data(), | |
| 2601 | req->opslimit, | |
| 2602 | req->memlimit | |
| 2603 | ); | |
| 2604 | } | |
| 2605 | ||
| 2606 | static void | |
| 2607 | async_pwhash_scryptsalsa208sha256_complete(uv_work_t *uv_req, int status) { | |
| 2608 | int err; | |
| 2609 | ||
| 2610 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_request *>(uv_req); | |
| 2611 | ||
| 2612 | if (req->exiting) return delete req; | |
| 2613 | ||
| 2614 | js_handle_scope_t *scope; | |
| 2615 | err = js_open_handle_scope(req->env, &scope); | |
| 2616 | assert(err == 0); | |
| 2617 | ||
| 2618 | js_function_t<void, int> callback; | |
| 2619 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2620 | assert(err == 0); | |
| 2621 | ||
| 2622 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2623 | assert(err != js_pending_exception); | |
| 2624 | ||
| 2625 | err = js_close_handle_scope(req->env, scope); | |
| 2626 | assert(err == 0); | |
| 2627 | ||
| 2628 | delete req; | |
| 2629 | } | |
| 2630 | ||
| 2631 | static inline void | |
| 2632 | sn_crypto_pwhash_scryptsalsa208sha256_async( | |
| 2633 | js_env_t *env, | |
| 2634 | js_receiver_t, | |
| 2635 | ||
| 2636 | js_arraybuffer_t out, | |
| 2637 | uint32_t out_offset, | |
| 2638 | uint32_t out_len, | |
| 2639 | ||
| 2640 | js_arraybuffer_t pwd, | |
| 2641 | uint32_t pwd_offset, | |
| 2642 | uint32_t pwd_len, | |
| 2643 | ||
| 2644 | js_arraybuffer_t salt, | |
| 2645 | uint32_t salt_offset, | |
| 2646 | uint32_t salt_len, | |
| 2647 | ||
| 2648 | uint64_t opslimit, | |
| 2649 | uint64_t memlimit, | |
| 2650 | ||
| 2651 | js_function_t<void, int> callback | |
| 2652 | ) { | |
| 2653 | int err; | |
| 2654 | ||
| 2655 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_request(env); | |
| 2656 | ||
| 2657 | std::span<uint8_t> out_view; | |
| 2658 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2659 | assert(err == 0); | |
| 2660 | assert(out_offset + out_len <= out_view.size()); | |
| 2661 | ||
| 2662 | req->out = {&out_view[out_offset], out_len}; | |
| 2663 | ||
| 2664 | std::span<char> pwd_view; | |
| 2665 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2666 | assert(err == 0); | |
| 2667 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2668 | ||
| 2669 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2670 | ||
| 2671 | std::span<uint8_t> salt_view; | |
| 2672 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 2673 | assert(err == 0); | |
| 2674 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 2675 | assert(salt_len == crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 2676 | ||
| 2677 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 2678 | ||
| 2679 | req->opslimit = opslimit; | |
| 2680 | req->memlimit = memlimit; | |
| 2681 | ||
| 2682 | err = js_create_reference(env, out, req->out_ref); | |
| 2683 | assert(err == 0); | |
| 2684 | ||
| 2685 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2686 | assert(err == 0); | |
| 2687 | ||
| 2688 | err = js_create_reference(env, salt, req->salt_ref); | |
| 2689 | assert(err == 0); | |
| 2690 | ||
| 2691 | err = js_create_reference(env, callback, req->cb); | |
| 2692 | assert(err == 0); | |
| 2693 | ||
| 2694 | uv_loop_t *loop; | |
| 2695 | err = js_get_env_loop(env, &loop); | |
| 2696 | assert(err == 0); | |
| 2697 | ||
| 2698 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_execute, async_pwhash_scryptsalsa208sha256_complete); | |
| 2699 | assert(err == 0); | |
| 2700 | } | |
| 2701 | ||
| 2702 | struct sn_async_pwhash_scryptsalsa208sha256_str_request : sn_async_task { | |
| 2703 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2704 | std::span<char> out; | |
| 2705 | ||
| 2706 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2707 | std::span<char> pwd; | |
| 2708 | ||
| 2709 | uint64_t opslimit; | |
| 2710 | size_t memlimit; | |
| 2711 | ||
| 2712 | sn_async_pwhash_scryptsalsa208sha256_str_request(js_env_t *env) : sn_async_task(env) {} | |
| 2713 | }; | |
| 2714 | ||
| 2715 | static void | |
| 2716 | async_pwhash_scryptsalsa208sha256_str_execute(uv_work_t *uv_req) { | |
| 2717 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_request *>(uv_req); | |
| 2718 | ||
| 2719 | req->code = crypto_pwhash_scryptsalsa208sha256_str( | |
| 2720 | req->out.data(), | |
| 2721 | req->pwd.data(), | |
| 2722 | req->pwd.size(), | |
| 2723 | req->opslimit, | |
| 2724 | req->memlimit | |
| 2725 | ); | |
| 2726 | } | |
| 2727 | ||
| 2728 | static void | |
| 2729 | async_pwhash_scryptsalsa208sha256_str_complete(uv_work_t *uv_req, int status) { | |
| 2730 | int err; | |
| 2731 | ||
| 2732 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_request *>(uv_req); | |
| 2733 | ||
| 2734 | if (req->exiting) return delete req; | |
| 2735 | ||
| 2736 | js_handle_scope_t *scope; | |
| 2737 | err = js_open_handle_scope(req->env, &scope); | |
| 2738 | assert(err == 0); | |
| 2739 | ||
| 2740 | js_function_t<void, int> callback; | |
| 2741 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2742 | assert(err == 0); | |
| 2743 | ||
| 2744 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2745 | assert(err != js_pending_exception); | |
| 2746 | ||
| 2747 | err = js_close_handle_scope(req->env, scope); | |
| 2748 | assert(err == 0); | |
| 2749 | ||
| 2750 | delete req; | |
| 2751 | } | |
| 2752 | ||
| 2753 | static inline void | |
| 2754 | sn_crypto_pwhash_scryptsalsa208sha256_str_async( | |
| 2755 | js_env_t *env, | |
| 2756 | js_receiver_t, | |
| 2757 | ||
| 2758 | js_arraybuffer_t out, | |
| 2759 | uint32_t out_offset, | |
| 2760 | uint32_t out_len, | |
| 2761 | ||
| 2762 | js_arraybuffer_t pwd, | |
| 2763 | uint32_t pwd_offset, | |
| 2764 | uint32_t pwd_len, | |
| 2765 | ||
| 2766 | uint64_t opslimit, | |
| 2767 | uint64_t memlimit, | |
| 2768 | ||
| 2769 | js_function_t<void, int> callback | |
| 2770 | ) { | |
| 2771 | int err; | |
| 2772 | ||
| 2773 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_str_request(env); | |
| 2774 | ||
| 2775 | std::span<char> out_view; | |
| 2776 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2777 | assert(err == 0); | |
| 2778 | assert(out_offset + out_len <= out_view.size()); | |
| 2779 | ||
| 2780 | req->out = {&out_view[out_offset], out_len}; | |
| 2781 | ||
| 2782 | std::span<char> pwd_view; | |
| 2783 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2784 | assert(err == 0); | |
| 2785 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2786 | ||
| 2787 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2788 | ||
| 2789 | req->opslimit = opslimit; | |
| 2790 | req->memlimit = memlimit; | |
| 2791 | ||
| 2792 | err = js_create_reference(env, out, req->out_ref); | |
| 2793 | assert(err == 0); | |
| 2794 | ||
| 2795 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2796 | assert(err == 0); | |
| 2797 | ||
| 2798 | err = js_create_reference(env, callback, req->cb); | |
| 2799 | assert(err == 0); | |
| 2800 | ||
| 2801 | uv_loop_t *loop; | |
| 2802 | err = js_get_env_loop(env, &loop); | |
| 2803 | assert(err == 0); | |
| 2804 | ||
| 2805 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_str_execute, async_pwhash_scryptsalsa208sha256_str_complete); | |
| 2806 | assert(err == 0); | |
| 2807 | } | |
| 2808 | ||
| 2809 | struct sn_async_pwhash_scryptsalsa208sha256_str_verify_request : sn_async_task { | |
| 2810 | js_persistent_t<js_arraybuffer_t> str_ref; | |
| 2811 | std::span<char> str; | |
| 2812 | ||
| 2813 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2814 | std::span<char> pwd; | |
| 2815 | ||
| 2816 | sn_async_pwhash_scryptsalsa208sha256_str_verify_request(js_env_t *env) : sn_async_task(env) {} | |
| 2817 | }; | |
| 2818 | ||
| 2819 | static void | |
| 2820 | async_pwhash_scryptsalsa208sha256_str_verify_execute(uv_work_t *uv_req) { | |
| 2821 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_verify_request *>(uv_req); | |
| 2822 | ||
| 2823 | req->code = crypto_pwhash_scryptsalsa208sha256_str_verify(req->str.data(), req->pwd.data(), req->pwd.size()); | |
| 2824 | } | |
| 2825 | ||
| 2826 | static void | |
| 2827 | async_pwhash_scryptsalsa208sha256_str_verify_complete(uv_work_t *uv_req, int status) { | |
| 2828 | int err; | |
| 2829 | ||
| 2830 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_verify_request *>(uv_req); | |
| 2831 | ||
| 2832 | if (req->exiting) return delete req; | |
| 2833 | ||
| 2834 | js_handle_scope_t *scope; | |
| 2835 | err = js_open_handle_scope(req->env, &scope); | |
| 2836 | assert(err == 0); | |
| 2837 | ||
| 2838 | js_function_t<void, int> callback; | |
| 2839 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2840 | assert(err == 0); | |
| 2841 | ||
| 2842 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2843 | assert(err != js_pending_exception); | |
| 2844 | ||
| 2845 | err = js_close_handle_scope(req->env, scope); | |
| 2846 | assert(err == 0); | |
| 2847 | ||
| 2848 | delete req; | |
| 2849 | } | |
| 2850 | ||
| 2851 | static inline void | |
| 2852 | sn_crypto_pwhash_scryptsalsa208sha256_str_verify_async( | |
| 2853 | js_env_t *env, | |
| 2854 | js_receiver_t, | |
| 2855 | ||
| 2856 | js_arraybuffer_t str, | |
| 2857 | uint32_t str_offset, | |
| 2858 | uint32_t str_len, | |
| 2859 | ||
| 2860 | js_arraybuffer_t pwd, | |
| 2861 | uint32_t pwd_offset, | |
| 2862 | uint32_t pwd_len, | |
| 2863 | ||
| 2864 | js_function_t<void, int> callback | |
| 2865 | ) { | |
| 2866 | int err; | |
| 2867 | ||
| 2868 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_str_verify_request(env); | |
| 2869 | ||
| 2870 | std::span<char> str_view; | |
| 2871 | err = js_get_arraybuffer_info(env, str, str_view); | |
| 2872 | assert(err == 0); | |
| 2873 | assert(str_offset + str_len <= str_view.size()); | |
| 2874 | ||
| 2875 | req->str = {&str_view[str_offset], str_len}; | |
| 2876 | ||
| 2877 | std::span<char> pwd_view; | |
| 2878 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2879 | assert(err == 0); | |
| 2880 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2881 | ||
| 2882 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2883 | ||
| 2884 | err = js_create_reference(env, str, req->str_ref); | |
| 2885 | assert(err == 0); | |
| 2886 | ||
| 2887 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2888 | assert(err == 0); | |
| 2889 | ||
| 2890 | err = js_create_reference(env, callback, req->cb); | |
| 2891 | assert(err == 0); | |
| 2892 | ||
| 2893 | uv_loop_t *loop; | |
| 2894 | err = js_get_env_loop(env, &loop); | |
| 2895 | assert(err == 0); | |
| 2896 | ||
| 2897 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_str_verify_execute, async_pwhash_scryptsalsa208sha256_str_verify_complete); | |
| 2898 | assert(err == 0); | |
| 2899 | } | |
| 2900 | ||
| 2901 | struct sn_crypto_stream_xor_state { | |
| 2902 | unsigned char n[crypto_stream_NONCEBYTES]; | |
| 2903 | unsigned char k[crypto_stream_KEYBYTES]; | |
| 2904 | unsigned char next_block[64]; | |
| 2905 | int remainder; | |
| 2906 | uint64_t block_counter; | |
| 2907 | }; | |
| 2908 | ||
| 2909 | static inline void | |
| 2910 | sn_crypto_stream_xor_wrap_init( | |
| 2911 | js_env_t *, | |
| 2912 | js_receiver_t, | |
| 2913 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state, | |
| 2914 | js_typedarray_span_t<> n, | |
| 2915 | js_typedarray_span_t<> k | |
| 2916 | ) { | |
| 2917 | assert(n.size_bytes() == crypto_stream_NONCEBYTES); | |
| 2918 | assert(k.size_bytes() == crypto_stream_KEYBYTES); | |
| 2919 | ||
| 2920 | state->remainder = 0; | |
| 2921 | state->block_counter = 0; | |
| 2922 | memcpy(state->n, n.data(), crypto_stream_NONCEBYTES); | |
| 2923 | memcpy(state->k, k.data(), crypto_stream_KEYBYTES); | |
| 2924 | } | |
| 2925 | ||
| 2926 | static inline void | |
| 2927 | sn_crypto_stream_xor_wrap_update( | |
| 2928 | js_env_t *, | |
| 2929 | js_receiver_t, | |
| 2930 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state, | |
| 2931 | js_typedarray_span_t<> c, | |
| 2932 | js_typedarray_span_t<> m | |
| 2933 | ) { | |
| 2934 | assert(c.size_bytes() == m.size_bytes()); | |
| 2935 | ||
| 2936 | auto next_block = state->next_block; | |
| 2937 | ||
| 2938 | size_t m_size = m.size_bytes(); | |
| 2939 | auto *c_ptr = c.data(); | |
| 2940 | auto *m_ptr = m.data(); | |
| 2941 | ||
| 2942 | if (state->remainder) { | |
| 2943 | uint64_t offset = 0; | |
| 2944 | int rem = state->remainder; | |
| 2945 | ||
| 2946 | while (rem < 64 && offset < m_size) { | |
| 2947 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 2948 | ++offset; | |
| 2949 | ++rem; | |
| 2950 | } | |
| 2951 | ||
| 2952 | c_ptr += offset; | |
| 2953 | m_ptr += offset; | |
| 2954 | m_size -= offset; | |
| 2955 | state->remainder = (rem == 64) ? 0 : rem; | |
| 2956 | ||
| 2957 | if (m_size == 0) return; | |
| 2958 | } | |
| 2959 | ||
| 2960 | state->remainder = m_size & 63; | |
| 2961 | size_t main_len = m_size - state->remainder; | |
| 2962 | ||
| 2963 | crypto_stream_xsalsa20_xor_ic(c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k); | |
| 2964 | state->block_counter += main_len / 64; | |
| 2965 | ||
| 2966 | if (state->remainder) { | |
| 2967 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 2968 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 2969 | ||
| 2970 | crypto_stream_xsalsa20_xor_ic( | |
| 2971 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 2972 | ); | |
| 2973 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 2974 | ||
| 2975 | state->block_counter++; | |
| 2976 | } | |
| 2977 | } | |
| 2978 | ||
| 2979 | static inline void | |
| 2980 | sn_crypto_stream_xor_wrap_final( | |
| 2981 | js_env_t *, | |
| 2982 | js_receiver_t, | |
| 2983 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state | |
| 2984 | ) { | |
| 2985 | sodium_memzero(state->n, sizeof(state->n)); | |
| 2986 | sodium_memzero(state->k, sizeof(state->k)); | |
| 2987 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 2988 | state->remainder = 0; | |
| 2989 | } | |
| 2990 | ||
| 2991 | struct sn_crypto_stream_chacha20_xor_state { | |
| 2992 | unsigned char n[crypto_stream_chacha20_NONCEBYTES]; | |
| 2993 | unsigned char k[crypto_stream_chacha20_KEYBYTES]; | |
| 2994 | unsigned char next_block[64]; | |
| 2995 | int remainder; | |
| 2996 | uint64_t block_counter; | |
| 2997 | }; | |
| 2998 | ||
| 2999 | static inline void | |
| 3000 | sn_crypto_stream_chacha20_xor_wrap_init( | |
| 3001 | js_env_t *, | |
| 3002 | js_receiver_t, | |
| 3003 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state, | |
| 3004 | js_typedarray_span_t<> n, | |
| 3005 | js_typedarray_span_t<> k | |
| 3006 | ) { | |
| 3007 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 3008 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 3009 | ||
| 3010 | state->remainder = 0; | |
| 3011 | state->block_counter = 0; | |
| 3012 | memcpy(state->n, n.data(), crypto_stream_chacha20_NONCEBYTES); | |
| 3013 | memcpy(state->k, k.data(), crypto_stream_chacha20_KEYBYTES); | |
| 3014 | } | |
| 3015 | ||
| 3016 | static inline void | |
| 3017 | sn_crypto_stream_chacha20_xor_wrap_update( | |
| 3018 | js_env_t *, | |
| 3019 | js_receiver_t, | |
| 3020 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state, | |
| 3021 | js_typedarray_span_t<> c, | |
| 3022 | js_typedarray_span_t<> m | |
| 3023 | ) { | |
| 3024 | assert(c.size_bytes() == m.size_bytes()); | |
| 3025 | ||
| 3026 | auto *next_block = state->next_block; | |
| 3027 | ||
| 3028 | size_t m_size = m.size_bytes(); | |
| 3029 | auto *c_ptr = c.data(); | |
| 3030 | auto *m_ptr = m.data(); | |
| 3031 | ||
| 3032 | if (state->remainder) { | |
| 3033 | uint64_t offset = 0; | |
| 3034 | int rem = state->remainder; | |
| 3035 | ||
| 3036 | while (rem < 64 && offset < m_size) { | |
| 3037 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3038 | offset++; | |
| 3039 | rem++; | |
| 3040 | } | |
| 3041 | ||
| 3042 | c_ptr += offset; | |
| 3043 | m_ptr += offset; | |
| 3044 | m_size -= offset; | |
| 3045 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3046 | ||
| 3047 | if (m_size == 0) return; | |
| 3048 | } | |
| 3049 | ||
| 3050 | state->remainder = m_size & 63; | |
| 3051 | size_t main_len = m_size - state->remainder; | |
| 3052 | ||
| 3053 | crypto_stream_chacha20_xor_ic( | |
| 3054 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3055 | ); | |
| 3056 | ||
| 3057 | state->block_counter += main_len / 64; | |
| 3058 | ||
| 3059 | if (state->remainder) { | |
| 3060 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3061 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3062 | ||
| 3063 | crypto_stream_chacha20_xor_ic( | |
| 3064 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3065 | ); | |
| 3066 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3067 | ||
| 3068 | state->block_counter++; | |
| 3069 | } | |
| 3070 | } | |
| 3071 | ||
| 3072 | static inline void | |
| 3073 | sn_crypto_stream_chacha20_xor_wrap_final( | |
| 3074 | js_env_t *, | |
| 3075 | js_receiver_t, | |
| 3076 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state | |
| 3077 | ) { | |
| 3078 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3079 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3080 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3081 | state->remainder = 0; | |
| 3082 | } | |
| 3083 | ||
| 3084 | struct sn_crypto_stream_chacha20_ietf_xor_state { | |
| 3085 | unsigned char n[crypto_stream_chacha20_ietf_NONCEBYTES]; | |
| 3086 | unsigned char k[crypto_stream_chacha20_ietf_KEYBYTES]; | |
| 3087 | unsigned char next_block[64]; | |
| 3088 | int remainder; | |
| 3089 | uint64_t block_counter; | |
| 3090 | }; | |
| 3091 | ||
| 3092 | static inline void | |
| 3093 | sn_crypto_stream_chacha20_ietf_xor_wrap_init( | |
| 3094 | js_env_t *, | |
| 3095 | js_receiver_t, | |
| 3096 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state, | |
| 3097 | js_typedarray_span_t<> n, | |
| 3098 | js_typedarray_span_t<> k | |
| 3099 | ) { | |
| 3100 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 3101 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 3102 | ||
| 3103 | state->remainder = 0; | |
| 3104 | state->block_counter = 0; | |
| 3105 | memcpy(state->n, n.data(), crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 3106 | memcpy(state->k, k.data(), crypto_stream_chacha20_ietf_KEYBYTES); | |
| 3107 | } | |
| 3108 | ||
| 3109 | static inline void | |
| 3110 | sn_crypto_stream_chacha20_ietf_xor_wrap_update( | |
| 3111 | js_env_t *, | |
| 3112 | js_receiver_t, | |
| 3113 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state, | |
| 3114 | js_typedarray_span_t<> c, | |
| 3115 | js_typedarray_span_t<> m | |
| 3116 | ) { | |
| 3117 | assert(c.size_bytes() == m.size_bytes()); | |
| 3118 | ||
| 3119 | auto *next_block = state->next_block; | |
| 3120 | ||
| 3121 | size_t m_size = m.size_bytes(); | |
| 3122 | auto *c_ptr = c.data(); | |
| 3123 | auto *m_ptr = m.data(); | |
| 3124 | ||
| 3125 | if (state->remainder) { | |
| 3126 | uint64_t offset = 0; | |
| 3127 | int rem = state->remainder; | |
| 3128 | ||
| 3129 | while (rem < 64 && offset < m_size) { | |
| 3130 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3131 | offset++; | |
| 3132 | rem++; | |
| 3133 | } | |
| 3134 | ||
| 3135 | c_ptr += offset; | |
| 3136 | m_ptr += offset; | |
| 3137 | m_size -= offset; | |
| 3138 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3139 | ||
| 3140 | if (m_size == 0) return; | |
| 3141 | } | |
| 3142 | ||
| 3143 | state->remainder = m_size & 63; | |
| 3144 | size_t main_len = m_size - state->remainder; | |
| 3145 | ||
| 3146 | crypto_stream_chacha20_ietf_xor_ic( | |
| 3147 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3148 | ); | |
| 3149 | ||
| 3150 | state->block_counter += main_len / 64; | |
| 3151 | ||
| 3152 | if (state->remainder) { | |
| 3153 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3154 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3155 | ||
| 3156 | crypto_stream_chacha20_ietf_xor_ic( | |
| 3157 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3158 | ); | |
| 3159 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3160 | ||
| 3161 | state->block_counter++; | |
| 3162 | } | |
| 3163 | } | |
| 3164 | ||
| 3165 | static inline void | |
| 3166 | sn_crypto_stream_chacha20_ietf_xor_wrap_final( | |
| 3167 | js_env_t *, | |
| 3168 | js_receiver_t, | |
| 3169 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state | |
| 3170 | ) { | |
| 3171 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3172 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3173 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3174 | state->remainder = 0; | |
| 3175 | } | |
| 3176 | ||
| 3177 | struct sn_crypto_stream_xchacha20_xor_state { | |
| 3178 | unsigned char n[crypto_stream_xchacha20_NONCEBYTES]; | |
| 3179 | unsigned char k[crypto_stream_xchacha20_KEYBYTES]; | |
| 3180 | unsigned char next_block[64]; | |
| 3181 | int remainder; | |
| 3182 | uint64_t block_counter; | |
| 3183 | }; | |
| 3184 | ||
| 3185 | static inline void | |
| 3186 | sn_crypto_stream_xchacha20_xor_wrap_init( | |
| 3187 | js_env_t *, | |
| 3188 | js_receiver_t, | |
| 3189 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state, | |
| 3190 | js_typedarray_span_t<> n, | |
| 3191 | js_typedarray_span_t<> k | |
| 3192 | ) { | |
| 3193 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 3194 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 3195 | ||
| 3196 | state->remainder = 0; | |
| 3197 | state->block_counter = 0; | |
| 3198 | memcpy(state->n, n.data(), crypto_stream_xchacha20_NONCEBYTES); | |
| 3199 | memcpy(state->k, k.data(), crypto_stream_xchacha20_KEYBYTES); | |
| 3200 | } | |
| 3201 | ||
| 3202 | static inline void | |
| 3203 | sn_crypto_stream_xchacha20_xor_wrap_update( | |
| 3204 | js_env_t *, | |
| 3205 | js_receiver_t, | |
| 3206 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state, | |
| 3207 | js_typedarray_span_t<> c, | |
| 3208 | js_typedarray_span_t<> m | |
| 3209 | ) { | |
| 3210 | assert(c.size_bytes() == m.size_bytes()); | |
| 3211 | ||
| 3212 | auto *next_block = state->next_block; | |
| 3213 | ||
| 3214 | size_t m_size = m.size_bytes(); | |
| 3215 | auto *c_ptr = c.data(); | |
| 3216 | auto *m_ptr = m.data(); | |
| 3217 | ||
| 3218 | if (state->remainder) { | |
| 3219 | uint64_t offset = 0; | |
| 3220 | int rem = state->remainder; | |
| 3221 | ||
| 3222 | while (rem < 64 && offset < m_size) { | |
| 3223 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3224 | ++offset; | |
| 3225 | ++rem; | |
| 3226 | } | |
| 3227 | ||
| 3228 | c_ptr += offset; | |
| 3229 | m_ptr += offset; | |
| 3230 | m_size -= offset; | |
| 3231 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3232 | ||
| 3233 | if (m_size == 0) return; | |
| 3234 | } | |
| 3235 | ||
| 3236 | state->remainder = m_size & 63; | |
| 3237 | size_t main_len = m_size - state->remainder; | |
| 3238 | ||
| 3239 | crypto_stream_xchacha20_xor_ic( | |
| 3240 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3241 | ); | |
| 3242 | ||
| 3243 | state->block_counter += main_len / 64; | |
| 3244 | ||
| 3245 | if (state->remainder) { | |
| 3246 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3247 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3248 | ||
| 3249 | crypto_stream_xchacha20_xor_ic( | |
| 3250 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3251 | ); | |
| 3252 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3253 | ||
| 3254 | state->block_counter++; | |
| 3255 | } | |
| 3256 | } | |
| 3257 | ||
| 3258 | static inline void | |
| 3259 | sn_crypto_stream_xchacha20_xor_wrap_final( | |
| 3260 | js_env_t *, | |
| 3261 | js_receiver_t, | |
| 3262 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state | |
| 3263 | ) { | |
| 3264 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3265 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3266 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3267 | state->remainder = 0; | |
| 3268 | } | |
| 3269 | ||
| 3270 | struct sn_crypto_stream_salsa20_xor_state { | |
| 3271 | unsigned char n[crypto_stream_salsa20_NONCEBYTES]; | |
| 3272 | unsigned char k[crypto_stream_salsa20_KEYBYTES]; | |
| 3273 | unsigned char next_block[64]; | |
| 3274 | int remainder; | |
| 3275 | uint64_t block_counter; | |
| 3276 | }; | |
| 3277 | ||
| 3278 | static inline void | |
| 3279 | sn_crypto_stream_salsa20_xor_wrap_init( | |
| 3280 | js_env_t *, | |
| 3281 | js_receiver_t, | |
| 3282 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state, | |
| 3283 | js_typedarray_span_t<> n, | |
| 3284 | js_typedarray_span_t<> k | |
| 3285 | ) { | |
| 3286 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 3287 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 3288 | ||
| 3289 | state->remainder = 0; | |
| 3290 | state->block_counter = 0; | |
| 3291 | memcpy(state->n, n.data(), crypto_stream_salsa20_NONCEBYTES); | |
| 3292 | memcpy(state->k, k.data(), crypto_stream_salsa20_KEYBYTES); | |
| 3293 | } | |
| 3294 | ||
| 3295 | static inline void | |
| 3296 | sn_crypto_stream_salsa20_xor_wrap_update( | |
| 3297 | js_env_t *, | |
| 3298 | js_receiver_t, | |
| 3299 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state, | |
| 3300 | js_typedarray_span_t<> c, | |
| 3301 | js_typedarray_span_t<> m | |
| 3302 | ) { | |
| 3303 | assert(c.size_bytes() == m.size_bytes()); | |
| 3304 | ||
| 3305 | auto *next_block = state->next_block; | |
| 3306 | ||
| 3307 | size_t m_size = m.size_bytes(); | |
| 3308 | auto *c_ptr = c.data(); | |
| 3309 | auto *m_ptr = m.data(); | |
| 3310 | ||
| 3311 | if (state->remainder) { | |
| 3312 | uint64_t offset = 0; | |
| 3313 | int rem = state->remainder; | |
| 3314 | ||
| 3315 | while (rem < 64 && offset < m_size) { | |
| 3316 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3317 | ++offset; | |
| 3318 | ++rem; | |
| 3319 | } | |
| 3320 | ||
| 3321 | c_ptr += offset; | |
| 3322 | m_ptr += offset; | |
| 3323 | m_size -= offset; | |
| 3324 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3325 | ||
| 3326 | if (m_size == 0) return; | |
| 3327 | } | |
| 3328 | ||
| 3329 | state->remainder = m_size & 63; | |
| 3330 | size_t main_len = m_size - state->remainder; | |
| 3331 | ||
| 3332 | crypto_stream_salsa20_xor_ic( | |
| 3333 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3334 | ); | |
| 3335 | ||
| 3336 | state->block_counter += main_len / 64; | |
| 3337 | ||
| 3338 | if (state->remainder) { | |
| 3339 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3340 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3341 | ||
| 3342 | crypto_stream_salsa20_xor_ic( | |
| 3343 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3344 | ); | |
| 3345 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3346 | ||
| 3347 | state->block_counter++; | |
| 3348 | } | |
| 3349 | } | |
| 3350 | ||
| 3351 | static inline void | |
| 3352 | sn_crypto_stream_salsa20_xor_wrap_final( | |
| 3353 | js_env_t *, | |
| 3354 | js_receiver_t, | |
| 3355 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state | |
| 3356 | ) { | |
| 3357 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3358 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3359 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3360 | state->remainder = 0; | |
| 3361 | } | |
| 3362 | ||
| 3363 | // Experimental API | |
| 3364 | ||
| 3365 | static inline void | |
| 3366 | sn_extension_tweak_ed25519_base( | |
| 3367 | js_env_t *, | |
| 3368 | js_receiver_t, | |
| 3369 | js_typedarray_span_t<> n, | |
| 3370 | js_typedarray_span_t<> p, | |
| 3371 | js_typedarray_span_t<> ns | |
| 3372 | ) { | |
| 3373 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3374 | assert(p.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3375 | ||
| 3376 | sn__extension_tweak_ed25519_base(p.data(), n.data(), ns.data(), ns.size_bytes()); | |
| 3377 | } | |
| 3378 | ||
| 3379 | static inline int | |
| 3380 | sn_extension_tweak_ed25519_sign_detached( | |
| 3381 | js_env_t *, | |
| 3382 | js_receiver_t, | |
| 3383 | js_typedarray_span_t<> sig, | |
| 3384 | js_typedarray_span_t<> m, | |
| 3385 | js_typedarray_span_t<> scalar, | |
| 3386 | std::optional<js_typedarray_span_t<>> pk | |
| 3387 | ) { | |
| 3388 | assert(sig.size_bytes() == crypto_sign_BYTES); | |
| 3389 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3390 | ||
| 3391 | uint8_t *pk_data = nullptr; | |
| 3392 | if (pk) { | |
| 3393 | assert(pk->size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3394 | pk_data = pk->data(); | |
| 3395 | } | |
| 3396 | ||
| 3397 | return sn__extension_tweak_ed25519_sign_detached( | |
| 3398 | sig.data(), | |
| 3399 | nullptr, | |
| 3400 | m.data(), | |
| 3401 | m.size_bytes(), | |
| 3402 | scalar.data(), | |
| 3403 | pk_data | |
| 3404 | ); | |
| 3405 | } | |
| 3406 | ||
| 3407 | static inline void | |
| 3408 | sn_extension_tweak_ed25519_sk_to_scalar( | |
| 3409 | js_env_t *, | |
| 3410 | js_receiver_t, | |
| 3411 | js_typedarray_span_t<> n, | |
| 3412 | js_typedarray_span_t<> sk | |
| 3413 | ) { | |
| 3414 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3415 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 3416 | ||
| 3417 | sn__extension_tweak_ed25519_sk_to_scalar(n.data(), sk.data()); | |
| 3418 | } | |
| 3419 | ||
| 3420 | static inline void | |
| 3421 | sn_extension_tweak_ed25519_scalar( | |
| 3422 | js_env_t *, | |
| 3423 | js_receiver_t, | |
| 3424 | js_typedarray_span_t<> scalar_out, | |
| 3425 | js_typedarray_span_t<> scalar, | |
| 3426 | js_typedarray_span_t<> ns | |
| 3427 | ) { | |
| 3428 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3429 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3430 | ||
| 3431 | sn__extension_tweak_ed25519_scalar(scalar_out.data(), scalar.data(), ns.data(), ns.size_bytes()); | |
| 3432 | } | |
| 3433 | ||
| 3434 | static inline int | |
| 3435 | sn_extension_tweak_ed25519_pk( | |
| 3436 | js_env_t *, | |
| 3437 | js_receiver_t, | |
| 3438 | js_typedarray_span_t<> tpk, | |
| 3439 | js_typedarray_span_t<> pk, | |
| 3440 | js_typedarray_span_t<> ns | |
| 3441 | ) { | |
| 3442 | assert(tpk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3443 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3444 | ||
| 3445 | return sn__extension_tweak_ed25519_pk(tpk.data(), pk.data(), ns.data(), ns.size_bytes()); | |
| 3446 | } | |
| 3447 | ||
| 3448 | static inline void | |
| 3449 | sn_extension_tweak_ed25519_keypair( | |
| 3450 | js_env_t *, | |
| 3451 | js_receiver_t, | |
| 3452 | js_typedarray_span_t<> pk, | |
| 3453 | js_typedarray_span_t<> scalar_out, | |
| 3454 | js_typedarray_span_t<> scalar_in, | |
| 3455 | js_typedarray_span_t<> ns | |
| 3456 | ) { | |
| 3457 | assert(pk.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3458 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3459 | assert(scalar_in.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3460 | ||
| 3461 | sn__extension_tweak_ed25519_keypair( | |
| 3462 | pk.data(), | |
| 3463 | scalar_out.data(), | |
| 3464 | scalar_in.data(), | |
| 3465 | ns.data(), | |
| 3466 | ns.size_bytes() | |
| 3467 | ); | |
| 3468 | } | |
| 3469 | ||
| 3470 | static inline void | |
| 3471 | sn_extension_tweak_ed25519_scalar_add( | |
| 3472 | js_env_t *, | |
| 3473 | js_receiver_t, | |
| 3474 | js_typedarray_span_t<> scalar_out, | |
| 3475 | js_typedarray_span_t<> scalar, | |
| 3476 | js_typedarray_span_t<> n | |
| 3477 | ) { | |
| 3478 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3479 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3480 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3481 | ||
| 3482 | sn__extension_tweak_ed25519_scalar_add(scalar_out.data(), scalar.data(), n.data()); | |
| 3483 | } | |
| 3484 | ||
| 3485 | static inline int | |
| 3486 | sn_extension_tweak_ed25519_pk_add( | |
| 3487 | js_env_t *, | |
| 3488 | js_receiver_t, | |
| 3489 | js_typedarray_span_t<> tpk, | |
| 3490 | js_typedarray_span_t<> pk, | |
| 3491 | js_typedarray_span_t<> p | |
| 3492 | ) { | |
| 3493 | assert(tpk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3494 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3495 | assert(p.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3496 | ||
| 3497 | return sn__extension_tweak_ed25519_pk_add(tpk.data(), pk.data(), p.data()); | |
| 3498 | } | |
| 3499 | ||
| 3500 | static inline int | |
| 3501 | sn_extension_tweak_ed25519_keypair_add( | |
| 3502 | js_env_t *, | |
| 3503 | js_receiver_t, | |
| 3504 | js_typedarray_span_t<> pk, | |
| 3505 | js_typedarray_span_t<> scalar_out, | |
| 3506 | js_typedarray_span_t<> scalar_in, | |
| 3507 | js_typedarray_span_t<> tweak | |
| 3508 | ) { | |
| 3509 | assert(pk.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3510 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3511 | assert(scalar_in.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3512 | assert(tweak.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3513 | ||
| 3514 | return sn__extension_tweak_ed25519_keypair_add( | |
| 3515 | pk.data(), | |
| 3516 | scalar_out.data(), | |
| 3517 | scalar_in.data(), | |
| 3518 | tweak.data() | |
| 3519 | ); | |
| 3520 | } | |
| 3521 | ||
| 3522 | static inline int | |
| 3523 | sn_extension_pbkdf2_sha512( | |
| 3524 | js_env_t *, | |
| 3525 | js_receiver_t, | |
| 3526 | js_typedarray_span_t<> out, | |
| 3527 | js_typedarray_span_t<> passwd, | |
| 3528 | js_typedarray_span_t<> salt, | |
| 3529 | uint64_t iter, | |
| 3530 | uint64_t outlen | |
| 3531 | ) { | |
| 3532 | ||
| 3533 | assert(iter >= sn__extension_pbkdf2_sha512_ITERATIONS_MIN); | |
| 3534 | assert(outlen <= sn__extension_pbkdf2_sha512_BYTES_MAX); | |
| 3535 | assert(out.size_bytes() >= static_cast<size_t>(outlen)); | |
| 3536 | ||
| 3537 | return sn__extension_pbkdf2_sha512( | |
| 3538 | passwd.data(), | |
| 3539 | passwd.size_bytes(), | |
| 3540 | salt.data(), | |
| 3541 | salt.size_bytes(), | |
| 3542 | iter, | |
| 3543 | out.data(), | |
| 3544 | outlen | |
| 3545 | ); | |
| 3546 | } | |
| 3547 | ||
| 3548 | struct sn_async_pbkdf2_sha512_request : sn_async_task { | |
| 3549 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 3550 | std::span<uint8_t> out; | |
| 3551 | ||
| 3552 | size_t outlen; | |
| 3553 | ||
| 3554 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 3555 | std::span<uint8_t> pwd; | |
| 3556 | ||
| 3557 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 3558 | std::span<uint8_t> salt; | |
| 3559 | ||
| 3560 | uint64_t iter; | |
| 3561 | ||
| 3562 | sn_async_pbkdf2_sha512_request(js_env_t *env) : sn_async_task(env) {} | |
| 3563 | }; | |
| 3564 | ||
| 3565 | static void | |
| 3566 | async_pbkdf2_sha512_execute(uv_work_t *uv_req) { | |
| 3567 | auto *req = reinterpret_cast<sn_async_pbkdf2_sha512_request *>(uv_req); | |
| 3568 | ||
| 3569 | req->code = sn__extension_pbkdf2_sha512( | |
| 3570 | req->pwd.data(), | |
| 3571 | req->pwd.size(), | |
| 3572 | req->salt.data(), | |
| 3573 | req->salt.size(), | |
| 3574 | req->iter, | |
| 3575 | req->out.data(), | |
| 3576 | req->outlen | |
| 3577 | ); | |
| 3578 | } | |
| 3579 | ||
| 3580 | static void | |
| 3581 | async_pbkdf2_sha512_complete(uv_work_t *uv_req, int status) { | |
| 3582 | int err; | |
| 3583 | ||
| 3584 | auto *req = reinterpret_cast<sn_async_pbkdf2_sha512_request *>(uv_req); | |
| 3585 | ||
| 3586 | if (req->exiting) return delete req; | |
| 3587 | ||
| 3588 | js_handle_scope_t *scope; | |
| 3589 | err = js_open_handle_scope(req->env, &scope); | |
| 3590 | assert(err == 0); | |
| 3591 | ||
| 3592 | js_function_t<void, int> callback; | |
| 3593 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 3594 | assert(err == 0); | |
| 3595 | ||
| 3596 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 3597 | assert(err != js_pending_exception); | |
| 3598 | ||
| 3599 | err = js_close_handle_scope(req->env, scope); | |
| 3600 | assert(err == 0); | |
| 3601 | ||
| 3602 | delete req; | |
| 3603 | } | |
| 3604 | ||
| 3605 | static inline void | |
| 3606 | sn_extension_pbkdf2_sha512_async( | |
| 3607 | js_env_t *env, | |
| 3608 | js_receiver_t, | |
| 3609 | ||
| 3610 | js_arraybuffer_t out, | |
| 3611 | uint32_t out_offset, | |
| 3612 | uint32_t out_len, | |
| 3613 | ||
| 3614 | js_arraybuffer_t pwd, | |
| 3615 | uint32_t pwd_offset, | |
| 3616 | uint32_t pwd_len, | |
| 3617 | ||
| 3618 | js_arraybuffer_t salt, | |
| 3619 | uint32_t salt_offset, | |
| 3620 | uint32_t salt_len, | |
| 3621 | ||
| 3622 | uint64_t iter, | |
| 3623 | uint64_t outlen, | |
| 3624 | ||
| 3625 | js_function_t<void, int> callback | |
| 3626 | ) { | |
| 3627 | int err; | |
| 3628 | ||
| 3629 | auto *req = new sn_async_pbkdf2_sha512_request(env); | |
| 3630 | ||
| 3631 | std::span<uint8_t> out_view; | |
| 3632 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 3633 | assert(err == 0); | |
| 3634 | assert(out_offset + out_len <= out_view.size()); | |
| 3635 | ||
| 3636 | req->out = {&out_view[out_offset], out_len}; | |
| 3637 | ||
| 3638 | std::span<uint8_t> pwd_view; | |
| 3639 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 3640 | assert(err == 0); | |
| 3641 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 3642 | ||
| 3643 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 3644 | ||
| 3645 | std::span<uint8_t> salt_view; | |
| 3646 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 3647 | assert(err == 0); | |
| 3648 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 3649 | ||
| 3650 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 3651 | ||
| 3652 | req->iter = iter; | |
| 3653 | req->outlen = outlen; | |
| 3654 | ||
| 3655 | err = js_create_reference(env, out, req->out_ref); | |
| 3656 | assert(err == 0); | |
| 3657 | ||
| 3658 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 3659 | assert(err == 0); | |
| 3660 | ||
| 3661 | err = js_create_reference(env, salt, req->salt_ref); | |
| 3662 | assert(err == 0); | |
| 3663 | ||
| 3664 | err = js_create_reference(env, callback, req->cb); | |
| 3665 | assert(err == 0); | |
| 3666 | ||
| 3667 | uv_loop_t *loop; | |
| 3668 | err = js_get_env_loop(env, &loop); | |
| 3669 | assert(err == 0); | |
| 3670 | ||
| 3671 | err = uv_queue_work(loop, &req->task, async_pbkdf2_sha512_execute, async_pbkdf2_sha512_complete); | |
| 3672 | assert(err == 0); | |
| 3673 | } | |
| 3674 | ||
| 3675 | static js_value_t * | |
| 3676 | sodium_native_exports(js_env_t *env, js_value_t *exports) { | |
| 3677 | int err; | |
| 3678 | ||
| 3679 | err = sodium_init(); | |
| 3680 | assert(err >= 0); | |
| 3681 | ||
| 3682 | #define V_FUNCTION(name, fn) \ | |
| 3683 | err = js_set_property<fn>(env, exports, name); \ | |
| 3684 | assert(err == 0); | |
| 3685 | ||
| 3686 | #define V_FUNCTION_NOSCOPE(name, fn) \ | |
| 3687 | err = js_set_property<fn, js_function_options_t{.scoped = false}>(env, exports, name); \ | |
| 3688 | assert(err == 0); | |
| 3689 | ||
| 3690 | #define V_UINT32(name, constant) \ | |
| 3691 | err = js_set_property(env, exports, name, static_cast<uint32_t>(constant)); \ | |
| 3692 | assert(err == 0); | |
| 3693 | ||
| 3694 | #define V_UINT64(name, constant) \ | |
| 3695 | err = js_set_property(env, exports, name, static_cast<int64_t>(std::min<uint64_t>(constant, js_max_safe_integer))); \ | |
| 3696 | assert(err == 0); | |
| 3697 | ||
| 3698 | #define V_STRING(name, str) \ | |
| 3699 | err = js_set_property(env, exports, name, str); \ | |
| 3700 | assert(err == 0); | |
| 3701 | ||
| 3702 | // memory | |
| 3703 | ||
| 3704 | V_FUNCTION("sodium_memzero", sn_sodium_memzero); | |
| 3705 | V_FUNCTION("sodium_mlock", sn_sodium_mlock); | |
| 3706 | V_FUNCTION("sodium_munlock", sn_sodium_munlock); | |
| 3707 | V_FUNCTION("sodium_malloc", sn_sodium_malloc); | |
| 3708 | V_FUNCTION("sodium_free", sn_sodium_free); | |
| 3709 | V_FUNCTION("sodium_mprotect_noaccess", sn_sodium_mprotect_noaccess); | |
| 3710 | V_FUNCTION("sodium_mprotect_readonly", sn_sodium_mprotect_readonly); | |
| 3711 | V_FUNCTION("sodium_mprotect_readwrite", sn_sodium_mprotect_readwrite); | |
| 3712 | ||
| 3713 | // randombytes | |
| 3714 | ||
| 3715 | V_FUNCTION_NOSCOPE("randombytes_buf", sn_randombytes_buf); | |
| 3716 | V_FUNCTION_NOSCOPE("randombytes_buf_deterministic", sn_randombytes_buf_deterministic); | |
| 3717 | V_FUNCTION_NOSCOPE("randombytes_random", sn_randombytes_random); | |
| 3718 | V_FUNCTION_NOSCOPE("randombytes_uniform", sn_randombytes_uniform); | |
| 3719 | ||
| 3720 | V_UINT32("randombytes_SEEDBYTES", randombytes_SEEDBYTES); | |
| 3721 | ||
| 3722 | // sodium helpers | |
| 3723 | ||
| 3724 | V_FUNCTION("sodium_memcmp", sn_sodium_memcmp); | |
| 3725 | V_FUNCTION("sodium_increment", sn_sodium_increment); | |
| 3726 | V_FUNCTION("sodium_add", sn_sodium_add); | |
| 3727 | V_FUNCTION("sodium_sub", sn_sodium_sub); | |
| 3728 | V_FUNCTION("sodium_compare", sn_sodium_compare); | |
| 3729 | V_FUNCTION("sodium_is_zero", sn_sodium_is_zero); | |
| 3730 | V_FUNCTION("sodium_pad", sn_sodium_pad); | |
| 3731 | V_FUNCTION("sodium_unpad", sn_sodium_unpad); | |
| 3732 | ||
| 3733 | // crypto_aead | |
| 3734 | ||
| 3735 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_keygen", sn_crypto_aead_xchacha20poly1305_ietf_keygen); | |
| 3736 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_encrypt", sn_crypto_aead_xchacha20poly1305_ietf_encrypt); | |
| 3737 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_decrypt", sn_crypto_aead_xchacha20poly1305_ietf_decrypt); | |
| 3738 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_encrypt_detached", sn_crypto_aead_xchacha20poly1305_ietf_encrypt_detached); | |
| 3739 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_decrypt_detached", sn_crypto_aead_xchacha20poly1305_ietf_decrypt_detached); | |
| 3740 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_ABYTES", crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 3741 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_KEYBYTES", crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 3742 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_NPUBBYTES", crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 3743 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_NSECBYTES", crypto_aead_xchacha20poly1305_ietf_NSECBYTES); | |
| 3744 | V_UINT64("crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX", crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX); | |
| 3745 | ||
| 3746 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_keygen", sn_crypto_aead_chacha20poly1305_ietf_keygen); | |
| 3747 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_encrypt", sn_crypto_aead_chacha20poly1305_ietf_encrypt); | |
| 3748 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_decrypt", sn_crypto_aead_chacha20poly1305_ietf_decrypt); | |
| 3749 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_encrypt_detached", sn_crypto_aead_chacha20poly1305_ietf_encrypt_detached); | |
| 3750 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_decrypt_detached", sn_crypto_aead_chacha20poly1305_ietf_decrypt_detached); | |
| 3751 | V_UINT32("crypto_aead_chacha20poly1305_ietf_ABYTES", crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 3752 | V_UINT32("crypto_aead_chacha20poly1305_ietf_KEYBYTES", crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 3753 | V_UINT32("crypto_aead_chacha20poly1305_ietf_NPUBBYTES", crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 3754 | V_UINT32("crypto_aead_chacha20poly1305_ietf_NSECBYTES", crypto_aead_chacha20poly1305_ietf_NSECBYTES); | |
| 3755 | V_UINT64("crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX", crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX); | |
| 3756 | ||
| 3757 | // crypto_auth | |
| 3758 | ||
| 3759 | V_FUNCTION("crypto_auth", sn_crypto_auth); | |
| 3760 | V_FUNCTION("crypto_auth_verify", sn_crypto_auth_verify); | |
| 3761 | V_UINT32("crypto_auth_BYTES", crypto_auth_BYTES); | |
| 3762 | V_UINT32("crypto_auth_KEYBYTES", crypto_auth_KEYBYTES); | |
| 3763 | V_STRING("crypto_auth_PRIMITIVE", crypto_auth_PRIMITIVE); | |
| 3764 | ||
| 3765 | // crypto_box | |
| 3766 | ||
| 3767 | V_FUNCTION("crypto_box_keypair", sn_crypto_box_keypair); | |
| 3768 | V_FUNCTION("crypto_box_seed_keypair", sn_crypto_box_seed_keypair); | |
| 3769 | V_FUNCTION("crypto_box_easy", sn_crypto_box_easy); | |
| 3770 | V_FUNCTION("crypto_box_open_easy", sn_crypto_box_open_easy); | |
| 3771 | V_FUNCTION("crypto_box_detached", sn_crypto_box_detached); | |
| 3772 | V_FUNCTION("crypto_box_open_detached", sn_crypto_box_open_detached); | |
| 3773 | V_FUNCTION("crypto_box_seal", sn_crypto_box_seal); | |
| 3774 | V_FUNCTION_NOSCOPE("crypto_box_seal_open", sn_crypto_box_seal_open); | |
| 3775 | ||
| 3776 | V_UINT32("crypto_box_SEEDBYTES", crypto_box_SEEDBYTES); | |
| 3777 | V_UINT32("crypto_box_PUBLICKEYBYTES", crypto_box_PUBLICKEYBYTES); | |
| 3778 | V_UINT32("crypto_box_SECRETKEYBYTES", crypto_box_SECRETKEYBYTES); | |
| 3779 | V_UINT32("crypto_box_NONCEBYTES", crypto_box_NONCEBYTES); | |
| 3780 | V_UINT32("crypto_box_MACBYTES", crypto_box_MACBYTES); | |
| 3781 | V_UINT32("crypto_box_SEALBYTES", crypto_box_SEALBYTES); | |
| 3782 | V_STRING("crypto_box_PRIMITIVE", crypto_box_PRIMITIVE); | |
| 3783 | ||
| 3784 | // crypto_core | |
| 3785 | ||
| 3786 | V_FUNCTION("crypto_core_ed25519_is_valid_point", sn_crypto_core_ed25519_is_valid_point); | |
| 3787 | V_FUNCTION("crypto_core_ed25519_from_uniform", sn_crypto_core_ed25519_from_uniform); | |
| 3788 | V_FUNCTION("crypto_core_ed25519_add", sn_crypto_core_ed25519_add); | |
| 3789 | V_FUNCTION("crypto_core_ed25519_sub", sn_crypto_core_ed25519_sub); | |
| 3790 | V_FUNCTION("crypto_core_ed25519_scalar_random", sn_crypto_core_ed25519_scalar_random); | |
| 3791 | V_FUNCTION("crypto_core_ed25519_scalar_reduce", sn_crypto_core_ed25519_scalar_reduce); | |
| 3792 | V_FUNCTION("crypto_core_ed25519_scalar_invert", sn_crypto_core_ed25519_scalar_invert); | |
| 3793 | V_FUNCTION("crypto_core_ed25519_scalar_negate", sn_crypto_core_ed25519_scalar_negate); | |
| 3794 | V_FUNCTION("crypto_core_ed25519_scalar_complement", sn_crypto_core_ed25519_scalar_complement); | |
| 3795 | V_FUNCTION("crypto_core_ed25519_scalar_add", sn_crypto_core_ed25519_scalar_add); | |
| 3796 | V_FUNCTION("crypto_core_ed25519_scalar_sub", sn_crypto_core_ed25519_scalar_sub); | |
| 3797 | V_UINT32("crypto_core_ed25519_BYTES", crypto_core_ed25519_BYTES); | |
| 3798 | V_UINT32("crypto_core_ed25519_UNIFORMBYTES", crypto_core_ed25519_UNIFORMBYTES); | |
| 3799 | V_UINT32("crypto_core_ed25519_SCALARBYTES", crypto_core_ed25519_SCALARBYTES); | |
| 3800 | V_UINT32("crypto_core_ed25519_NONREDUCEDSCALARBYTES", crypto_core_ed25519_NONREDUCEDSCALARBYTES); | |
| 3801 | ||
| 3802 | // crypto_kdf | |
| 3803 | ||
| 3804 | V_FUNCTION("crypto_kdf_keygen", sn_crypto_kdf_keygen); | |
| 3805 | V_FUNCTION("crypto_kdf_derive_from_key", sn_crypto_kdf_derive_from_key); | |
| 3806 | V_UINT32("crypto_kdf_BYTES_MIN", crypto_kdf_BYTES_MIN); | |
| 3807 | V_UINT32("crypto_kdf_BYTES_MAX", crypto_kdf_BYTES_MAX); | |
| 3808 | V_UINT32("crypto_kdf_CONTEXTBYTES", crypto_kdf_CONTEXTBYTES); | |
| 3809 | V_UINT32("crypto_kdf_KEYBYTES", crypto_kdf_KEYBYTES); | |
| 3810 | V_STRING("crypto_kdf_PRIMITIVE", crypto_kdf_PRIMITIVE); | |
| 3811 | ||
| 3812 | // crypto_kx | |
| 3813 | ||
| 3814 | V_FUNCTION("crypto_kx_keypair", sn_crypto_kx_keypair); | |
| 3815 | V_FUNCTION("crypto_kx_seed_keypair", sn_crypto_kx_seed_keypair); | |
| 3816 | V_FUNCTION("crypto_kx_client_session_keys", sn_crypto_kx_client_session_keys); | |
| 3817 | V_FUNCTION("crypto_kx_server_session_keys", sn_crypto_kx_server_session_keys); | |
| 3818 | V_UINT32("crypto_kx_PUBLICKEYBYTES", crypto_kx_PUBLICKEYBYTES); | |
| 3819 | V_UINT32("crypto_kx_SECRETKEYBYTES", crypto_kx_SECRETKEYBYTES); | |
| 3820 | V_UINT32("crypto_kx_SEEDBYTES", crypto_kx_SEEDBYTES); | |
| 3821 | V_UINT32("crypto_kx_SESSIONKEYBYTES", crypto_kx_SESSIONKEYBYTES); | |
| 3822 | V_STRING("crypto_kx_PRIMITIVE", crypto_kx_PRIMITIVE); | |
| 3823 | ||
| 3824 | // crypto_generichash | |
| 3825 | ||
| 3826 | V_FUNCTION_NOSCOPE("crypto_generichash", sn_crypto_generichash); | |
| 3827 | V_FUNCTION("crypto_generichash_batch", sn_crypto_generichash_batch); | |
| 3828 | V_FUNCTION_NOSCOPE("crypto_generichash_keygen", sn_crypto_generichash_keygen); | |
| 3829 | V_FUNCTION_NOSCOPE("crypto_generichash_init", sn_crypto_generichash_init); | |
| 3830 | V_FUNCTION_NOSCOPE("crypto_generichash_update", sn_crypto_generichash_update); | |
| 3831 | V_FUNCTION_NOSCOPE("crypto_generichash_final", sn_crypto_generichash_final); | |
| 3832 | ||
| 3833 | V_UINT32("crypto_generichash_STATEBYTES", sizeof(crypto_generichash_state)); | |
| 3834 | V_STRING("crypto_generichash_PRIMITIVE", crypto_generichash_PRIMITIVE); | |
| 3835 | V_UINT32("crypto_generichash_BYTES_MIN", crypto_generichash_BYTES_MIN); | |
| 3836 | V_UINT32("crypto_generichash_BYTES_MAX", crypto_generichash_BYTES_MAX); | |
| 3837 | V_UINT32("crypto_generichash_BYTES", crypto_generichash_BYTES); | |
| 3838 | V_UINT32("crypto_generichash_KEYBYTES_MIN", crypto_generichash_KEYBYTES_MIN); | |
| 3839 | V_UINT32("crypto_generichash_KEYBYTES_MAX", crypto_generichash_KEYBYTES_MAX); | |
| 3840 | V_UINT32("crypto_generichash_KEYBYTES", crypto_generichash_KEYBYTES); | |
| 3841 | ||
| 3842 | // crypto_hash | |
| 3843 | ||
| 3844 | V_FUNCTION("crypto_hash", sn_crypto_hash); | |
| 3845 | V_UINT32("crypto_hash_BYTES", crypto_hash_BYTES); | |
| 3846 | V_STRING("crypto_hash_PRIMITIVE", crypto_hash_PRIMITIVE); | |
| 3847 | ||
| 3848 | V_FUNCTION("crypto_hash_sha256", sn_crypto_hash_sha256); | |
| 3849 | V_FUNCTION("crypto_hash_sha256_init", sn_crypto_hash_sha256_init); | |
| 3850 | V_FUNCTION("crypto_hash_sha256_update", sn_crypto_hash_sha256_update); | |
| 3851 | V_FUNCTION("crypto_hash_sha256_final", sn_crypto_hash_sha256_final); | |
| 3852 | V_UINT32("crypto_hash_sha256_STATEBYTES", sizeof(crypto_hash_sha256_state)); | |
| 3853 | V_UINT32("crypto_hash_sha256_BYTES", crypto_hash_sha256_BYTES); | |
| 3854 | ||
| 3855 | V_FUNCTION("crypto_hash_sha512", sn_crypto_hash_sha512); | |
| 3856 | V_FUNCTION("crypto_hash_sha512_init", sn_crypto_hash_sha512_init); | |
| 3857 | V_FUNCTION("crypto_hash_sha512_update", sn_crypto_hash_sha512_update); | |
| 3858 | V_FUNCTION("crypto_hash_sha512_final", sn_crypto_hash_sha512_final); | |
| 3859 | V_UINT32("crypto_hash_sha512_STATEBYTES", sizeof(crypto_hash_sha512_state)); | |
| 3860 | V_UINT32("crypto_hash_sha512_BYTES", crypto_hash_sha512_BYTES); | |
| 3861 | ||
| 3862 | // crypto_onetimeauth | |
| 3863 | ||
| 3864 | V_FUNCTION("crypto_onetimeauth", sn_crypto_onetimeauth); | |
| 3865 | V_FUNCTION("crypto_onetimeauth_verify", sn_crypto_onetimeauth_verify); | |
| 3866 | V_FUNCTION("crypto_onetimeauth_init", sn_crypto_onetimeauth_init); | |
| 3867 | V_FUNCTION("crypto_onetimeauth_update", sn_crypto_onetimeauth_update); | |
| 3868 | V_FUNCTION("crypto_onetimeauth_final", sn_crypto_onetimeauth_final); | |
| 3869 | V_UINT32("crypto_onetimeauth_STATEBYTES", sizeof(crypto_onetimeauth_state)); | |
| 3870 | V_UINT32("crypto_onetimeauth_BYTES", crypto_onetimeauth_BYTES); | |
| 3871 | V_UINT32("crypto_onetimeauth_KEYBYTES", crypto_onetimeauth_KEYBYTES); | |
| 3872 | V_STRING("crypto_onetimeauth_PRIMITIVE", crypto_onetimeauth_PRIMITIVE); | |
| 3873 | ||
| 3874 | // crypto_pwhash | |
| 3875 | ||
| 3876 | V_FUNCTION("crypto_pwhash", sn_crypto_pwhash); | |
| 3877 | V_FUNCTION("crypto_pwhash_str", sn_crypto_pwhash_str); | |
| 3878 | V_FUNCTION("crypto_pwhash_str_verify", sn_crypto_pwhash_str_verify); | |
| 3879 | V_FUNCTION("crypto_pwhash_str_needs_rehash", sn_crypto_pwhash_str_needs_rehash); | |
| 3880 | V_FUNCTION("crypto_pwhash_async", sn_crypto_pwhash_async); | |
| 3881 | V_FUNCTION("crypto_pwhash_str_async", sn_crypto_pwhash_str_async); | |
| 3882 | V_FUNCTION("crypto_pwhash_str_verify_async", sn_crypto_pwhash_str_verify_async); | |
| 3883 | V_UINT32("crypto_pwhash_ALG_ARGON2I13", crypto_pwhash_ALG_ARGON2I13); | |
| 3884 | V_UINT32("crypto_pwhash_ALG_ARGON2ID13", crypto_pwhash_ALG_ARGON2ID13); | |
| 3885 | V_UINT32("crypto_pwhash_ALG_DEFAULT", crypto_pwhash_ALG_DEFAULT); | |
| 3886 | V_UINT32("crypto_pwhash_BYTES_MIN", crypto_pwhash_BYTES_MIN); | |
| 3887 | V_UINT32("crypto_pwhash_BYTES_MAX", crypto_pwhash_BYTES_MAX); | |
| 3888 | V_UINT32("crypto_pwhash_PASSWD_MIN", crypto_pwhash_PASSWD_MIN); | |
| 3889 | V_UINT32("crypto_pwhash_PASSWD_MAX", crypto_pwhash_PASSWD_MAX); | |
| 3890 | V_UINT32("crypto_pwhash_SALTBYTES", crypto_pwhash_SALTBYTES); | |
| 3891 | V_UINT32("crypto_pwhash_STRBYTES", crypto_pwhash_STRBYTES); | |
| 3892 | V_STRING("crypto_pwhash_STRPREFIX", crypto_pwhash_STRPREFIX); | |
| 3893 | V_UINT32("crypto_pwhash_OPSLIMIT_MIN", crypto_pwhash_OPSLIMIT_MIN); | |
| 3894 | V_UINT32("crypto_pwhash_OPSLIMIT_MAX", crypto_pwhash_OPSLIMIT_MAX); | |
| 3895 | V_UINT64("crypto_pwhash_MEMLIMIT_MIN", crypto_pwhash_MEMLIMIT_MIN); | |
| 3896 | V_UINT64("crypto_pwhash_MEMLIMIT_MAX", crypto_pwhash_MEMLIMIT_MAX); | |
| 3897 | V_UINT32("crypto_pwhash_OPSLIMIT_INTERACTIVE", crypto_pwhash_OPSLIMIT_INTERACTIVE); | |
| 3898 | V_UINT64("crypto_pwhash_MEMLIMIT_INTERACTIVE", crypto_pwhash_MEMLIMIT_INTERACTIVE); | |
| 3899 | V_UINT32("crypto_pwhash_OPSLIMIT_MODERATE", crypto_pwhash_OPSLIMIT_MODERATE); | |
| 3900 | V_UINT64("crypto_pwhash_MEMLIMIT_MODERATE", crypto_pwhash_MEMLIMIT_MODERATE); | |
| 3901 | V_UINT32("crypto_pwhash_OPSLIMIT_SENSITIVE", crypto_pwhash_OPSLIMIT_SENSITIVE); | |
| 3902 | V_UINT64("crypto_pwhash_MEMLIMIT_SENSITIVE", crypto_pwhash_MEMLIMIT_SENSITIVE); | |
| 3903 | V_STRING("crypto_pwhash_PRIMITIVE", crypto_pwhash_PRIMITIVE); | |
| 3904 | ||
| 3905 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256", sn_crypto_pwhash_scryptsalsa208sha256); | |
| 3906 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str", sn_crypto_pwhash_scryptsalsa208sha256_str); | |
| 3907 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_verify", sn_crypto_pwhash_scryptsalsa208sha256_str_verify); | |
| 3908 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_needs_rehash", sn_crypto_pwhash_scryptsalsa208sha256_str_needs_rehash); | |
| 3909 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_async", sn_crypto_pwhash_scryptsalsa208sha256_async); | |
| 3910 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_async", sn_crypto_pwhash_scryptsalsa208sha256_str_async) | |
| 3911 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_verify_async", sn_crypto_pwhash_scryptsalsa208sha256_str_verify_async); | |
| 3912 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_BYTES_MIN", crypto_pwhash_scryptsalsa208sha256_BYTES_MIN); | |
| 3913 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_BYTES_MAX", crypto_pwhash_scryptsalsa208sha256_BYTES_MAX); | |
| 3914 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN", crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN); | |
| 3915 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX", crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX); | |
| 3916 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_SALTBYTES", crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 3917 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_STRBYTES", crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 3918 | V_STRING("crypto_pwhash_scryptsalsa208sha256_STRPREFIX", crypto_pwhash_scryptsalsa208sha256_STRPREFIX); | |
| 3919 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 3920 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 3921 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 3922 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 3923 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); | |
| 3924 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); | |
| 3925 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE); | |
| 3926 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE); | |
| 3927 | ||
| 3928 | // crypto_scalarmult | |
| 3929 | ||
| 3930 | V_FUNCTION("crypto_scalarmult_base", sn_crypto_scalarmult_base); | |
| 3931 | V_FUNCTION("crypto_scalarmult", sn_crypto_scalarmult); | |
| 3932 | V_STRING("crypto_scalarmult_PRIMITIVE", crypto_scalarmult_PRIMITIVE); | |
| 3933 | V_UINT32("crypto_scalarmult_BYTES", crypto_scalarmult_BYTES); | |
| 3934 | V_UINT32("crypto_scalarmult_SCALARBYTES", crypto_scalarmult_SCALARBYTES); | |
| 3935 | ||
| 3936 | V_FUNCTION("crypto_scalarmult_ed25519_base", sn_crypto_scalarmult_ed25519_base); | |
| 3937 | V_FUNCTION("crypto_scalarmult_ed25519", sn_crypto_scalarmult_ed25519); | |
| 3938 | V_FUNCTION("crypto_scalarmult_ed25519_base_noclamp", sn_crypto_scalarmult_ed25519_base_noclamp); | |
| 3939 | V_FUNCTION("crypto_scalarmult_ed25519_noclamp", sn_crypto_scalarmult_ed25519_noclamp); | |
| 3940 | V_UINT32("crypto_scalarmult_ed25519_BYTES", crypto_scalarmult_ed25519_BYTES); | |
| 3941 | V_UINT32("crypto_scalarmult_ed25519_SCALARBYTES", crypto_scalarmult_ed25519_SCALARBYTES); | |
| 3942 | ||
| 3943 | // crypto_secretbox | |
| 3944 | ||
| 3945 | V_FUNCTION("crypto_secretbox_easy", sn_crypto_secretbox_easy); | |
| 3946 | V_FUNCTION("crypto_secretbox_open_easy", sn_crypto_secretbox_open_easy); | |
| 3947 | V_FUNCTION("crypto_secretbox_detached", sn_crypto_secretbox_detached); | |
| 3948 | V_FUNCTION("crypto_secretbox_open_detached", sn_crypto_secretbox_open_detached); | |
| 3949 | V_UINT32("crypto_secretbox_KEYBYTES", crypto_secretbox_KEYBYTES); | |
| 3950 | V_UINT32("crypto_secretbox_NONCEBYTES", crypto_secretbox_NONCEBYTES); | |
| 3951 | V_UINT32("crypto_secretbox_MACBYTES", crypto_secretbox_MACBYTES); | |
| 3952 | V_STRING("crypto_secretbox_PRIMITIVE", crypto_secretbox_PRIMITIVE); | |
| 3953 | ||
| 3954 | // crypto_secretstream | |
| 3955 | ||
| 3956 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_keygen", sn_crypto_secretstream_xchacha20poly1305_keygen); | |
| 3957 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_init_push", sn_crypto_secretstream_xchacha20poly1305_init_push); | |
| 3958 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_init_pull", sn_crypto_secretstream_xchacha20poly1305_init_pull); | |
| 3959 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_push", sn_crypto_secretstream_xchacha20poly1305_push); | |
| 3960 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_pull", sn_crypto_secretstream_xchacha20poly1305_pull); | |
| 3961 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_rekey", sn_crypto_secretstream_xchacha20poly1305_rekey); | |
| 3962 | ||
| 3963 | V_UINT32("crypto_secretstream_xchacha20poly1305_STATEBYTES", sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 3964 | V_UINT32("crypto_secretstream_xchacha20poly1305_ABYTES", crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 3965 | V_UINT32("crypto_secretstream_xchacha20poly1305_HEADERBYTES", crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 3966 | V_UINT32("crypto_secretstream_xchacha20poly1305_KEYBYTES", crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 3967 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAGBYTES", 1); | |
| 3968 | V_UINT64("crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX", crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX); | |
| 3969 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_MESSAGE", crypto_secretstream_xchacha20poly1305_TAG_MESSAGE); | |
| 3970 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_PUSH", crypto_secretstream_xchacha20poly1305_TAG_PUSH); | |
| 3971 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_REKEY", crypto_secretstream_xchacha20poly1305_TAG_REKEY); | |
| 3972 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_FINAL", crypto_secretstream_xchacha20poly1305_TAG_FINAL); | |
| 3973 | ||
| 3974 | // crypto_shorthash | |
| 3975 | ||
| 3976 | V_FUNCTION("crypto_shorthash", sn_crypto_shorthash); | |
| 3977 | V_UINT32("crypto_shorthash_BYTES", crypto_shorthash_BYTES); | |
| 3978 | V_UINT32("crypto_shorthash_KEYBYTES", crypto_shorthash_KEYBYTES); | |
| 3979 | V_STRING("crypto_shorthash_PRIMITIVE", crypto_shorthash_PRIMITIVE); | |
| 3980 | ||
| 3981 | // crypto_sign | |
| 3982 | ||
| 3983 | V_FUNCTION("crypto_sign_keypair", sn_crypto_sign_keypair); | |
| 3984 | V_FUNCTION("crypto_sign_seed_keypair", sn_crypto_sign_seed_keypair); | |
| 3985 | V_FUNCTION("crypto_sign", sn_crypto_sign); | |
| 3986 | V_FUNCTION("crypto_sign_open", sn_crypto_sign_open); | |
| 3987 | V_FUNCTION("crypto_sign_detached", sn_crypto_sign_detached); | |
| 3988 | V_FUNCTION_NOSCOPE("crypto_sign_verify_detached", sn_crypto_sign_verify_detached); | |
| 3989 | V_FUNCTION("crypto_sign_ed25519_sk_to_pk", sn_crypto_sign_ed25519_sk_to_pk); | |
| 3990 | V_FUNCTION("crypto_sign_ed25519_pk_to_curve25519", sn_crypto_sign_ed25519_pk_to_curve25519); | |
| 3991 | V_FUNCTION("crypto_sign_ed25519_sk_to_curve25519", sn_crypto_sign_ed25519_sk_to_curve25519); | |
| 3992 | ||
| 3993 | V_UINT32("crypto_sign_SEEDBYTES", crypto_sign_SEEDBYTES); | |
| 3994 | V_UINT32("crypto_sign_PUBLICKEYBYTES", crypto_sign_PUBLICKEYBYTES); | |
| 3995 | V_UINT32("crypto_sign_SECRETKEYBYTES", crypto_sign_SECRETKEYBYTES); | |
| 3996 | V_UINT32("crypto_sign_BYTES", crypto_sign_BYTES); | |
| 3997 | ||
| 3998 | // crypto_stream | |
| 3999 | ||
| 4000 | V_FUNCTION("crypto_stream", sn_crypto_stream); | |
| 4001 | V_UINT32("crypto_stream_KEYBYTES", crypto_stream_KEYBYTES); | |
| 4002 | V_UINT32("crypto_stream_NONCEBYTES", crypto_stream_NONCEBYTES); | |
| 4003 | V_STRING("crypto_stream_PRIMITIVE", crypto_stream_PRIMITIVE); | |
| 4004 | ||
| 4005 | V_FUNCTION_NOSCOPE("crypto_stream_xor", sn_crypto_stream_xor); | |
| 4006 | V_FUNCTION("crypto_stream_xor_init", sn_crypto_stream_xor_wrap_init); | |
| 4007 | V_FUNCTION("crypto_stream_xor_update", sn_crypto_stream_xor_wrap_update); | |
| 4008 | V_FUNCTION("crypto_stream_xor_final", sn_crypto_stream_xor_wrap_final); | |
| 4009 | V_UINT32("crypto_stream_xor_STATEBYTES", sizeof(sn_crypto_stream_xor_state)); | |
| 4010 | ||
| 4011 | V_FUNCTION("crypto_stream_chacha20", sn_crypto_stream_chacha20); | |
| 4012 | V_UINT32("crypto_stream_chacha20_KEYBYTES", crypto_stream_chacha20_KEYBYTES); | |
| 4013 | V_UINT32("crypto_stream_chacha20_NONCEBYTES", crypto_stream_chacha20_NONCEBYTES); | |
| 4014 | V_UINT64("crypto_stream_chacha20_MESSAGEBYTES_MAX", crypto_stream_chacha20_MESSAGEBYTES_MAX); | |
| 4015 | ||
| 4016 | V_FUNCTION("crypto_stream_chacha20_xor", sn_crypto_stream_chacha20_xor); | |
| 4017 | V_FUNCTION("crypto_stream_chacha20_xor_ic", sn_crypto_stream_chacha20_xor_ic); | |
| 4018 | V_FUNCTION("crypto_stream_chacha20_xor_init", sn_crypto_stream_chacha20_xor_wrap_init); | |
| 4019 | V_FUNCTION("crypto_stream_chacha20_xor_update", sn_crypto_stream_chacha20_xor_wrap_update); | |
| 4020 | V_FUNCTION("crypto_stream_chacha20_xor_final", sn_crypto_stream_chacha20_xor_wrap_final); | |
| 4021 | V_UINT32("crypto_stream_chacha20_xor_STATEBYTES", sizeof(sn_crypto_stream_chacha20_xor_state)); | |
| 4022 | ||
| 4023 | V_FUNCTION("crypto_stream_chacha20_ietf", sn_crypto_stream_chacha20_ietf); | |
| 4024 | V_UINT32("crypto_stream_chacha20_ietf_KEYBYTES", crypto_stream_chacha20_ietf_KEYBYTES); | |
| 4025 | V_UINT32("crypto_stream_chacha20_ietf_NONCEBYTES", crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 4026 | V_UINT64("crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX", crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX); | |
| 4027 | V_UINT32("crypto_stream_chacha20_ietf_xor_STATEBYTES", sizeof(sn_crypto_stream_chacha20_ietf_xor_state)); | |
| 4028 | ||
| 4029 | V_FUNCTION("crypto_stream_chacha20_ietf_xor", sn_crypto_stream_chacha20_ietf_xor); | |
| 4030 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_ic", sn_crypto_stream_chacha20_ietf_xor_ic); | |
| 4031 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_init", sn_crypto_stream_chacha20_ietf_xor_wrap_init); | |
| 4032 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_update", sn_crypto_stream_chacha20_ietf_xor_wrap_update); | |
| 4033 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_final", sn_crypto_stream_chacha20_ietf_xor_wrap_final); | |
| 4034 | ||
| 4035 | V_FUNCTION("crypto_stream_xchacha20", sn_crypto_stream_xchacha20); | |
| 4036 | V_UINT32("crypto_stream_xchacha20_KEYBYTES", crypto_stream_xchacha20_KEYBYTES); | |
| 4037 | V_UINT32("crypto_stream_xchacha20_NONCEBYTES", crypto_stream_xchacha20_NONCEBYTES); | |
| 4038 | V_UINT64("crypto_stream_xchacha20_MESSAGEBYTES_MAX", crypto_stream_xchacha20_MESSAGEBYTES_MAX); | |
| 4039 | ||
| 4040 | V_FUNCTION("crypto_stream_xchacha20_xor", sn_crypto_stream_xchacha20_xor); | |
| 4041 | V_FUNCTION("crypto_stream_xchacha20_xor_ic", sn_crypto_stream_xchacha20_xor_ic); | |
| 4042 | V_FUNCTION("crypto_stream_xchacha20_xor_init", sn_crypto_stream_xchacha20_xor_wrap_init); | |
| 4043 | V_FUNCTION("crypto_stream_xchacha20_xor_update", sn_crypto_stream_xchacha20_xor_wrap_update); | |
| 4044 | V_FUNCTION("crypto_stream_xchacha20_xor_final", sn_crypto_stream_xchacha20_xor_wrap_final); | |
| 4045 | V_FUNCTION("crypto_stream_xchacha20", sn_crypto_stream_xchacha20); | |
| 4046 | V_UINT32("crypto_stream_xchacha20_xor_STATEBYTES", sizeof(sn_crypto_stream_xchacha20_xor_state)); | |
| 4047 | ||
| 4048 | V_FUNCTION("crypto_stream_salsa20", sn_crypto_stream_salsa20); | |
| 4049 | V_UINT32("crypto_stream_salsa20_KEYBYTES", crypto_stream_salsa20_KEYBYTES); | |
| 4050 | V_UINT32("crypto_stream_salsa20_NONCEBYTES", crypto_stream_salsa20_NONCEBYTES); | |
| 4051 | V_UINT64("crypto_stream_salsa20_MESSAGEBYTES_MAX", crypto_stream_salsa20_MESSAGEBYTES_MAX); | |
| 4052 | ||
| 4053 | V_FUNCTION("crypto_stream_salsa20_xor", sn_crypto_stream_salsa20_xor); | |
| 4054 | V_FUNCTION("crypto_stream_salsa20_xor_ic", sn_crypto_stream_salsa20_xor_ic); | |
| 4055 | V_FUNCTION("crypto_stream_salsa20_xor_init", sn_crypto_stream_salsa20_xor_wrap_init); | |
| 4056 | V_FUNCTION("crypto_stream_salsa20_xor_update", sn_crypto_stream_salsa20_xor_wrap_update); | |
| 4057 | V_FUNCTION("crypto_stream_salsa20_xor_final", sn_crypto_stream_salsa20_xor_wrap_final); | |
| 4058 | V_UINT32("crypto_stream_salsa20_xor_STATEBYTES", sizeof(sn_crypto_stream_salsa20_xor_state)); | |
| 4059 | ||
| 4060 | // extensions | |
| 4061 | ||
| 4062 | // tweak | |
| 4063 | ||
| 4064 | V_FUNCTION("extension_tweak_ed25519_base", sn_extension_tweak_ed25519_base); | |
| 4065 | V_FUNCTION("extension_tweak_ed25519_sign_detached", sn_extension_tweak_ed25519_sign_detached); | |
| 4066 | V_FUNCTION("extension_tweak_ed25519_sk_to_scalar", sn_extension_tweak_ed25519_sk_to_scalar); | |
| 4067 | V_FUNCTION("extension_tweak_ed25519_scalar", sn_extension_tweak_ed25519_scalar); | |
| 4068 | V_FUNCTION("extension_tweak_ed25519_pk", sn_extension_tweak_ed25519_pk); | |
| 4069 | V_FUNCTION("extension_tweak_ed25519_keypair", sn_extension_tweak_ed25519_keypair); | |
| 4070 | V_FUNCTION("extension_tweak_ed25519_scalar_add", sn_extension_tweak_ed25519_scalar_add); | |
| 4071 | V_FUNCTION("extension_tweak_ed25519_pk_add", sn_extension_tweak_ed25519_pk_add); | |
| 4072 | V_FUNCTION("extension_tweak_ed25519_keypair_add", sn_extension_tweak_ed25519_keypair_add); | |
| 4073 | V_UINT32("extension_tweak_ed25519_BYTES", sn__extension_tweak_ed25519_BYTES); | |
| 4074 | V_UINT32("extension_tweak_ed25519_SCALARBYTES", sn__extension_tweak_ed25519_SCALARBYTES); | |
| 4075 | ||
| 4076 | // pbkdf2 | |
| 4077 | ||
| 4078 | V_FUNCTION("extension_pbkdf2_sha512", sn_extension_pbkdf2_sha512); | |
| 4079 | V_FUNCTION("extension_pbkdf2_sha512_async", sn_extension_pbkdf2_sha512_async); | |
| 4080 | V_UINT32("extension_pbkdf2_sha512_SALTBYTES", sn__extension_pbkdf2_sha512_SALTBYTES); | |
| 4081 | V_UINT32("extension_pbkdf2_sha512_HASHBYTES", sn__extension_pbkdf2_sha512_HASHBYTES); | |
| 4082 | V_UINT32("extension_pbkdf2_sha512_ITERATIONS_MIN", sn__extension_pbkdf2_sha512_ITERATIONS_MIN); | |
| 4083 | V_UINT64("extension_pbkdf2_sha512_BYTES_MAX", sn__extension_pbkdf2_sha512_BYTES_MAX); | |
| 4084 | ||
| 4085 | #undef V_FUNCTION | |
| 4086 | #undef V_FUNCTION_NOSCOPE | |
| 4087 | #undef V_UINT32 | |
| 4088 | #undef V_UINT64 | |
| 4089 | #undef V_STRING | |
| 4090 | ||
| 4091 | return exports; | |
| 4092 | } | |
| 4093 | ||
| 4094 | BARE_MODULE(sodium_native, sodium_native_exports) |
| … | … | @@ -0,0 +1,2 @@ |
| 1 | require.addon = require('require-addon') | |
| 2 | module.exports = require.addon('.', __filename) |
| … | … | @@ -0,0 +1,90 @@ |
| 1 | /*- | |
| 2 | * Copyright 2005,2007,2009 Colin Percival | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 24 | * SUCH DAMAGE. | |
| 25 | */ | |
| 26 | ||
| 27 | /* | |
| 28 | * Adapted from libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf-sha256.c | |
| 29 | */ | |
| 30 | ||
| 31 | #include <string.h> | |
| 32 | #include <sodium.h> | |
| 33 | ||
| 34 | #include "pbkdf2.h" | |
| 35 | ||
| 36 | /** | |
| 37 | * pbkdf2_sha512(passwd, passwdlen, salt, saltlen, c, buf, dkLen): | |
| 38 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and | |
| 39 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). | |
| 40 | */ | |
| 41 | int | |
| 42 | sn__extension_pbkdf2_sha512(const unsigned char *passwd, size_t passwdlen, | |
| 43 | const unsigned char *salt, size_t saltlen, uint64_t c, | |
| 44 | unsigned char *buf, size_t dkLen) | |
| 45 | { | |
| 46 | crypto_auth_hmacsha512_state PShctx, hctx; | |
| 47 | size_t i; | |
| 48 | unsigned char ivec[4]; | |
| 49 | unsigned char U[64]; | |
| 50 | unsigned char T[64]; | |
| 51 | uint64_t j; | |
| 52 | unsigned int k; | |
| 53 | size_t clen; | |
| 54 | ||
| 55 | if (dkLen > sn__extension_pbkdf2_sha512_BYTES_MAX) { | |
| 56 | return -1; | |
| 57 | } | |
| 58 | ||
| 59 | crypto_auth_hmacsha512_init(&PShctx, passwd, passwdlen); | |
| 60 | crypto_auth_hmacsha512_update(&PShctx, salt, saltlen); | |
| 61 | ||
| 62 | for (i = 0; i * crypto_auth_hmacsha512_BYTES < dkLen; i++) { | |
| 63 | SN_PBKDF2_STORE32_BE(ivec, (uint32_t)(i + 1)); | |
| 64 | memcpy(&hctx, &PShctx, sizeof(crypto_auth_hmacsha512_state)); | |
| 65 | crypto_auth_hmacsha512_update(&hctx, ivec, 4); | |
| 66 | crypto_auth_hmacsha512_final(&hctx, U); | |
| 67 | ||
| 68 | memcpy(T, U, crypto_auth_hmacsha512_BYTES); | |
| 69 | /* LCOV_EXCL_START */ | |
| 70 | for (j = 2; j <= c; j++) { | |
| 71 | crypto_auth_hmacsha512_init(&hctx, passwd, passwdlen); | |
| 72 | crypto_auth_hmacsha512_update(&hctx, U, crypto_auth_hmacsha512_BYTES); | |
| 73 | crypto_auth_hmacsha512_final(&hctx, U); | |
| 74 | ||
| 75 | for (k = 0; k < crypto_auth_hmacsha512_BYTES; k++) { | |
| 76 | T[k] ^= U[k]; | |
| 77 | } | |
| 78 | } | |
| 79 | /* LCOV_EXCL_STOP */ | |
| 80 | ||
| 81 | clen = dkLen - i * crypto_auth_hmacsha512_BYTES; | |
| 82 | if (clen > crypto_auth_hmacsha512_BYTES) { | |
| 83 | clen = crypto_auth_hmacsha512_BYTES; | |
| 84 | } | |
| 85 | memcpy(&buf[i * crypto_auth_hmacsha512_BYTES], T, clen); | |
| 86 | } | |
| 87 | sodium_memzero((void *) &PShctx, sizeof PShctx); | |
| 88 | ||
| 89 | return 0; | |
| 90 | } |
| … | … | @@ -0,0 +1,60 @@ |
| 1 | /*- | |
| 2 | * Copyright 2005,2007,2009 Colin Percival | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 24 | * SUCH DAMAGE. | |
| 25 | * | |
| 26 | */ | |
| 27 | ||
| 28 | /* | |
| 29 | * Adapted from libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf-sha256.c | |
| 30 | */ | |
| 31 | #ifdef __cplusplus | |
| 32 | extern "C" { | |
| 33 | #endif | |
| 34 | ||
| 35 | #include <sodium.h> | |
| 36 | ||
| 37 | #define SN_PBKDF2_STORE32_BE(buf, n32) \ | |
| 38 | buf[0] = n32 >> 24 & 0xff; \ | |
| 39 | buf[1] = n32 >> 16 & 0xff; \ | |
| 40 | buf[2] = n32 >> 8 & 0xff; \ | |
| 41 | buf[3] = n32 >> 0 & 0xff; | |
| 42 | ||
| 43 | #define sn__extension_pbkdf2_sha512_SALTBYTES 16U | |
| 44 | ||
| 45 | #define sn__extension_pbkdf2_sha512_HASHBYTES crypto_hash_sha512_BYTES | |
| 46 | ||
| 47 | #define sn__extension_pbkdf2_sha512_ITERATIONS_MIN 1U | |
| 48 | ||
| 49 | #define sn__extension_pbkdf2_sha512_BYTES_MAX 0x3fffffffc0ULL | |
| 50 | ||
| 51 | /** | |
| 52 | * extension_pbkdf2_sha512(passwd, passwdlen, salt, saltlen, c, buf, dkLen): | |
| 53 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and | |
| 54 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). | |
| 55 | */ | |
| 56 | int sn__extension_pbkdf2_sha512(const unsigned char *, size_t, const unsigned char *, size_t, | |
| 57 | uint64_t, unsigned char *, size_t); | |
| 58 | #ifdef __cplusplus | |
| 59 | }; | |
| 60 | #endif |
| … | … | @@ -0,0 +1,206 @@ |
| 1 | #include "tweak.h" | |
| 2 | ||
| 3 | /* | |
| 4 | *EXPERIMENTAL API* | |
| 5 | ||
| 6 | This module is an experimental implementation of a key tweaking protocol | |
| 7 | over ed25519 keys. The signature algorithm has been reimplemented from | |
| 8 | libsodium, but the nonce generation algorithm is *non-standard*. | |
| 9 | ||
| 10 | Use at your own risk | |
| 11 | */ | |
| 12 | ||
| 13 | static void _extension_tweak_nonce (unsigned char *nonce, const unsigned char *n, | |
| 14 | const unsigned char *m, unsigned long long mlen) | |
| 15 | { | |
| 16 | // dom2(x, y) with x = 0 (not prehashed) and y = "crypto_tweak_ed25519" | |
| 17 | static const unsigned char TWEAK_PREFIX[32 + 2 + 20] = { | |
| 18 | 'S', 'i', 'g', 'E', 'd', '2', '5', '5', '1', '9', ' ', | |
| 19 | 'n', 'o', ' ', 'E', 'd', '2', '5', '5', '1', '9', ' ', | |
| 20 | 'c', 'o', 'l', 'l', 'i', 's', 'i', 'o', 'n', 's', 0, | |
| 21 | 20, 'c', 'r', 'y', 'p', 't', 'o', '_', 't', 'w', 'e', | |
| 22 | 'a', 'k', '_', 'e', 'd', '2', '5', '5', '1', '9' | |
| 23 | }; | |
| 24 | ||
| 25 | crypto_hash_sha512_state hs; | |
| 26 | ||
| 27 | crypto_hash_sha512_init(&hs); | |
| 28 | crypto_hash_sha512_update(&hs, TWEAK_PREFIX, sizeof TWEAK_PREFIX); | |
| 29 | crypto_hash_sha512_update(&hs, n, 32); | |
| 30 | crypto_hash_sha512_update(&hs, m, mlen); | |
| 31 | crypto_hash_sha512_final(&hs, nonce); | |
| 32 | } | |
| 33 | ||
| 34 | static inline void | |
| 35 | _crypto_sign_ed25519_clamp(unsigned char k[32]) | |
| 36 | { | |
| 37 | k[0] &= 248; | |
| 38 | k[31] &= 127; | |
| 39 | k[31] |= 64; | |
| 40 | } | |
| 41 | ||
| 42 | static void _extension_tweak_ed25519(unsigned char *q, unsigned char *n, | |
| 43 | const unsigned char *ns, unsigned long long nslen) | |
| 44 | { | |
| 45 | sodium_memzero(q, sizeof q); | |
| 46 | ||
| 47 | crypto_hash(n, ns, nslen); | |
| 48 | n[31] &= 127; // clear highest bit | |
| 49 | ||
| 50 | crypto_scalarmult_ed25519_base_noclamp(q, n); | |
| 51 | ||
| 52 | // hash tweak until we get a valid tweaked q | |
| 53 | while (crypto_core_ed25519_is_valid_point(q) != 1) { | |
| 54 | crypto_hash(n, n, 32); | |
| 55 | n[31] &= 127; // clear highest bit | |
| 56 | ||
| 57 | crypto_scalarmult_ed25519_base_noclamp(q, n); | |
| 58 | } | |
| 59 | } | |
| 60 | ||
| 61 | void sn__extension_tweak_ed25519_base(unsigned char *pk, unsigned char *scalar, | |
| 62 | const unsigned char *ns, unsigned long long nslen) | |
| 63 | { | |
| 64 | unsigned char n64[64]; | |
| 65 | ||
| 66 | _extension_tweak_ed25519(pk, n64, ns, nslen); | |
| 67 | ||
| 68 | SN_TWEAK_COPY_32(scalar, n64) | |
| 69 | } | |
| 70 | ||
| 71 | int sn__extension_tweak_ed25519_sign_detached(unsigned char *sig, unsigned long long *siglen_p, | |
| 72 | const unsigned char *m, unsigned long long mlen, | |
| 73 | const unsigned char *n, unsigned char *pk) | |
| 74 | { | |
| 75 | crypto_hash_sha512_state hs; | |
| 76 | ||
| 77 | unsigned char nonce[64]; | |
| 78 | unsigned char R[32]; | |
| 79 | unsigned char hram[64]; | |
| 80 | unsigned char _pk[32]; | |
| 81 | ||
| 82 | // check if pk was passed | |
| 83 | if (pk == NULL) { | |
| 84 | pk = _pk; | |
| 85 | ||
| 86 | // derive pk from scalar | |
| 87 | if (crypto_scalarmult_ed25519_base_noclamp(pk, n) != 0) { | |
| 88 | return -1; | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | _extension_tweak_nonce(nonce, n, m, mlen); | |
| 93 | crypto_core_ed25519_scalar_reduce(nonce, nonce); | |
| 94 | ||
| 95 | // R = G ^ nonce : curve point from nonce | |
| 96 | if (crypto_scalarmult_ed25519_base_noclamp(R, nonce) != 0) { | |
| 97 | return -1; | |
| 98 | } | |
| 99 | ||
| 100 | // generate challenge as h(ram) = hash(R, pk, message) | |
| 101 | crypto_hash_sha512_init(&hs); | |
| 102 | crypto_hash_sha512_update(&hs, R, 32); | |
| 103 | crypto_hash_sha512_update(&hs, pk, 32); | |
| 104 | crypto_hash_sha512_update(&hs, m, mlen); | |
| 105 | ||
| 106 | crypto_hash_sha512_final(&hs, hram); | |
| 107 | ||
| 108 | crypto_core_ed25519_scalar_reduce(hram, hram); | |
| 109 | ||
| 110 | // sig = nonce + n * h(ram) | |
| 111 | crypto_core_ed25519_scalar_mul(sig, hram, n); | |
| 112 | crypto_core_ed25519_scalar_add(sig + 32, nonce, sig); | |
| 113 | ||
| 114 | SN_TWEAK_COPY_32(sig, R) | |
| 115 | ||
| 116 | if (siglen_p != NULL) { | |
| 117 | *siglen_p = 64U; | |
| 118 | } | |
| 119 | ||
| 120 | return 0; | |
| 121 | } | |
| 122 | ||
| 123 | // tweak a secret key | |
| 124 | void sn__extension_tweak_ed25519_sk_to_scalar(unsigned char *n, const unsigned char *sk) | |
| 125 | { | |
| 126 | unsigned char n64[64]; | |
| 127 | ||
| 128 | // get sk scalar from seed, cf. crypto_sign_keypair_seed | |
| 129 | crypto_hash(n64, sk, 32); | |
| 130 | _crypto_sign_ed25519_clamp(n64); | |
| 131 | ||
| 132 | SN_TWEAK_COPY_32(n, n64) | |
| 133 | } | |
| 134 | ||
| 135 | // tweak a secret key | |
| 136 | void sn__extension_tweak_ed25519_scalar(unsigned char *scalar_out, | |
| 137 | const unsigned char *scalar, | |
| 138 | const unsigned char *ns, | |
| 139 | unsigned long long nslen) | |
| 140 | { | |
| 141 | unsigned char n[64]; | |
| 142 | unsigned char q[32]; | |
| 143 | ||
| 144 | _extension_tweak_ed25519(q, n, ns, nslen); | |
| 145 | crypto_core_ed25519_scalar_add(scalar_out, scalar, n); | |
| 146 | } | |
| 147 | ||
| 148 | // tweak a public key | |
| 149 | int sn__extension_tweak_ed25519_pk(unsigned char *tpk, | |
| 150 | const unsigned char *pk, | |
| 151 | const unsigned char *ns, | |
| 152 | unsigned long long nslen) | |
| 153 | { | |
| 154 | unsigned char n[64]; | |
| 155 | unsigned char q[32]; | |
| 156 | ||
| 157 | _extension_tweak_ed25519(q, n, ns, nslen); | |
| 158 | return crypto_core_ed25519_add(tpk, q, pk); | |
| 159 | } | |
| 160 | ||
| 161 | ||
| 162 | void sn__extension_tweak_ed25519_keypair(unsigned char *pk, unsigned char *scalar_out, | |
| 163 | unsigned char *scalar, const unsigned char *ns, | |
| 164 | unsigned long long nslen) | |
| 165 | { | |
| 166 | unsigned char n64[64]; | |
| 167 | ||
| 168 | crypto_hash(n64, ns, nslen); | |
| 169 | n64[31] &= 127; // clear highest bit | |
| 170 | ||
| 171 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, n64); | |
| 172 | crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 173 | ||
| 174 | // hash tweak until we get a valid tweaked point | |
| 175 | while (crypto_core_ed25519_is_valid_point(pk) != 1) { | |
| 176 | crypto_hash(n64, n64, 32); | |
| 177 | n64[31] &= 127; // clear highest bit | |
| 178 | ||
| 179 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, n64); | |
| 180 | crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 181 | } | |
| 182 | } | |
| 183 | ||
| 184 | // add tweak to scalar | |
| 185 | void sn__extension_tweak_ed25519_scalar_add(unsigned char *scalar_out, | |
| 186 | const unsigned char *scalar, | |
| 187 | const unsigned char *n) | |
| 188 | { | |
| 189 | crypto_core_ed25519_scalar_add(scalar_out, scalar, n); | |
| 190 | } | |
| 191 | ||
| 192 | // add tweak point to public key | |
| 193 | int sn__extension_tweak_ed25519_pk_add(unsigned char *tpk, | |
| 194 | const unsigned char *pk, | |
| 195 | const unsigned char *q) | |
| 196 | { | |
| 197 | return crypto_core_ed25519_add(tpk, pk, q); | |
| 198 | } | |
| 199 | ||
| 200 | ||
| 201 | int sn__extension_tweak_ed25519_keypair_add(unsigned char *pk, unsigned char *scalar_out, | |
| 202 | unsigned char *scalar, const unsigned char *tweak) | |
| 203 | { | |
| 204 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, tweak); | |
| 205 | return crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 206 | } |
| … | … | @@ -0,0 +1,62 @@ |
| 1 | #ifdef __cplusplus | |
| 2 | extern "C" { | |
| 3 | #endif | |
| 4 | ||
| 5 | #include <sodium.h> | |
| 6 | ||
| 7 | // copy 32 bytes using int64_t pointers | |
| 8 | #define SN_TWEAK_COPY_32(a, b) \ | |
| 9 | { \ | |
| 10 | long long *dst = (long long *) a; \ | |
| 11 | long long *src = (long long *) b; \ | |
| 12 | dst[0] = src[0]; \ | |
| 13 | dst[1] = src[1]; \ | |
| 14 | dst[2] = src[2]; \ | |
| 15 | dst[3] = src[3]; \ | |
| 16 | } | |
| 17 | ||
| 18 | #define sn__extension_tweak_ed25519_BYTES crypto_sign_ed25519_PUBLICKEYBYTES | |
| 19 | ||
| 20 | #define sn__extension_tweak_ed25519_SCALARBYTES crypto_scalarmult_ed25519_SCALARBYTES | |
| 21 | ||
| 22 | int sn__extension_tweak_ed25519_sign_detached(unsigned char *sig, unsigned long long *siglen_p, | |
| 23 | const unsigned char *m, unsigned long long mlen, | |
| 24 | const unsigned char *n, unsigned char *pk); | |
| 25 | ||
| 26 | void sn__extension_tweak_ed25519_base(unsigned char *pk, unsigned char *scalar, | |
| 27 | const unsigned char *ns, unsigned long long nslen); | |
| 28 | ||
| 29 | void sn__extension_tweak_ed25519_sk_to_scalar(unsigned char *scalar, const unsigned char *sk); | |
| 30 | ||
| 31 | // tweak a secret key | |
| 32 | void sn__extension_tweak_ed25519_scalar(unsigned char *scalar_out, | |
| 33 | const unsigned char *scalar, | |
| 34 | const unsigned char *ns, | |
| 35 | unsigned long long nslen); | |
| 36 | ||
| 37 | // tweak a public key | |
| 38 | int sn__extension_tweak_ed25519_pk(unsigned char *tpk, | |
| 39 | const unsigned char *pk, | |
| 40 | const unsigned char *ns, | |
| 41 | unsigned long long nslen); | |
| 42 | ||
| 43 | void sn__extension_tweak_ed25519_keypair(unsigned char *pk, unsigned char *scalar_out, | |
| 44 | unsigned char *scalar, const unsigned char *ns, | |
| 45 | unsigned long long nslen); | |
| 46 | ||
| 47 | // add tweak scalar to private key | |
| 48 | void sn__extension_tweak_ed25519_scalar_add(unsigned char *scalar_out, | |
| 49 | const unsigned char *scalar, | |
| 50 | const unsigned char *n); | |
| 51 | ||
| 52 | // add tweak point to public key | |
| 53 | int sn__extension_tweak_ed25519_pk_add(unsigned char *tpk, | |
| 54 | const unsigned char *pk, | |
| 55 | const unsigned char *q); | |
| 56 | ||
| 57 | int sn__extension_tweak_ed25519_keypair_add(unsigned char *pk, unsigned char *scalar_out, | |
| 58 | unsigned char *scalar, const unsigned char *tweak); | |
| 59 | ||
| 60 | #ifdef __cplusplus | |
| 61 | }; | |
| 62 | #endif |
| … | … | @@ -0,0 +1,3231 @@ |
| 1 | const assert = require('assert') | |
| 2 | const binding = require('./binding') | |
| 3 | const { isNode } = require('which-runtime') | |
| 4 | ||
| 5 | const OPTIONAL = Buffer.from(new ArrayBuffer(0)) | |
| 6 | ||
| 7 | module.exports = exports = { ...binding } | |
| 8 | ||
| 9 | // memory | |
| 10 | ||
| 11 | exports.sodium_memzero = function (buf) { | |
| 12 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 13 | ||
| 14 | binding.sodium_memzero(buf) | |
| 15 | } | |
| 16 | ||
| 17 | exports.sodium_mlock = function (buf) { | |
| 18 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 19 | ||
| 20 | const res = binding.sodium_mlock(buf) | |
| 21 | ||
| 22 | if (res !== 0) throw new Error('memory lock failed') | |
| 23 | } | |
| 24 | ||
| 25 | exports.sodium_munlock = function (buf) { | |
| 26 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 27 | ||
| 28 | const res = binding.sodium_munlock(buf) | |
| 29 | ||
| 30 | if (res !== 0) throw new Error('memory unlock failed') | |
| 31 | } | |
| 32 | ||
| 33 | exports.sodium_malloc = function (size) { | |
| 34 | assert(size >= 0, 'invalid size') | |
| 35 | ||
| 36 | const buf = Buffer.from(binding.sodium_malloc(size)) | |
| 37 | buf.secure = true | |
| 38 | return buf | |
| 39 | } | |
| 40 | ||
| 41 | exports.sodium_free = function (buf) { | |
| 42 | if (!buf || !buf.secure) return | |
| 43 | ||
| 44 | binding.sodium_free(buf.buffer) | |
| 45 | } | |
| 46 | ||
| 47 | exports.sodium_mprotect_noaccess = function (buf) { | |
| 48 | const res = binding.sodium_mprotect_noaccess(buf.buffer) | |
| 49 | ||
| 50 | if (res !== 0) throw new Error('failed to lock buffer') | |
| 51 | } | |
| 52 | ||
| 53 | exports.sodium_mprotect_readonly = function (buf) { | |
| 54 | const res = binding.sodium_mprotect_readonly(buf.buffer) | |
| 55 | ||
| 56 | if (res !== 0) throw new Error('failed to unlock buffer') | |
| 57 | } | |
| 58 | ||
| 59 | exports.sodium_mprotect_readwrite = function (buf) { | |
| 60 | const res = binding.sodium_mprotect_readwrite(buf.buffer) | |
| 61 | ||
| 62 | if (res !== 0) throw new Error('failed to unlock buffer') | |
| 63 | } | |
| 64 | ||
| 65 | // crypto_randombytes | |
| 66 | ||
| 67 | exports.randombytes_buf = function (buffer) { | |
| 68 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 69 | ||
| 70 | binding.randombytes_buf(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 71 | } | |
| 72 | ||
| 73 | exports.randombytes_buf_deterministic = function (buffer, seed) { | |
| 74 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 75 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 76 | assert( | |
| 77 | seed.byteLength === binding.randombytes_SEEDBYTES, | |
| 78 | "seed must be 'randombytes_SEEDBYTES' bytes" | |
| 79 | ) | |
| 80 | ||
| 81 | binding.randombytes_buf_deterministic( | |
| 82 | buffer.buffer, | |
| 83 | buffer.byteOffset, | |
| 84 | buffer.byteLength, | |
| 85 | ||
| 86 | seed.buffer, | |
| 87 | seed.byteOffset, | |
| 88 | seed.byteLength | |
| 89 | ) | |
| 90 | } | |
| 91 | ||
| 92 | // sodium_helpers | |
| 93 | ||
| 94 | exports.sodium_memcmp = function (a, b) { | |
| 95 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 96 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 97 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 98 | ||
| 99 | return binding.sodium_memcmp(a, b) | |
| 100 | } | |
| 101 | ||
| 102 | exports.sodium_add = function (a, b) { | |
| 103 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 104 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 105 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 106 | ||
| 107 | binding.sodium_add(a, b) | |
| 108 | } | |
| 109 | ||
| 110 | exports.sodium_sub = function (a, b) { | |
| 111 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 112 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 113 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 114 | ||
| 115 | binding.sodium_sub(a, b) | |
| 116 | } | |
| 117 | ||
| 118 | exports.sodium_compare = function (a, b) { | |
| 119 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 120 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 121 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 122 | ||
| 123 | return binding.sodium_compare(a, b) | |
| 124 | } | |
| 125 | ||
| 126 | exports.sodium_is_zero = function (buffer, length) { | |
| 127 | if (length === undefined) length = buffer.byteLength | |
| 128 | ||
| 129 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 130 | assert(length >= 0 && length <= buffer.byteLength, 'invalid length') | |
| 131 | ||
| 132 | return binding.sodium_is_zero(buffer, length) | |
| 133 | } | |
| 134 | ||
| 135 | exports.sodium_pad = function (buffer, unpaddedBuflen, blockSize) { | |
| 136 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 137 | assert(unpaddedBuflen <= buffer.byteLength, 'unpadded length cannot exceed buffer length') | |
| 138 | assert(blockSize <= buffer.byteLength, 'block size cannot exceed buffer length') | |
| 139 | assert(blockSize >= 1, 'block size must be at least 1 byte') | |
| 140 | assert( | |
| 141 | buffer.byteLength >= unpaddedBuflen + (blockSize - (unpaddedBuflen % blockSize)), | |
| 142 | 'buf not long enough' | |
| 143 | ) | |
| 144 | ||
| 145 | return binding.sodium_pad(buffer, unpaddedBuflen, blockSize) | |
| 146 | } | |
| 147 | ||
| 148 | exports.sodium_unpad = function (buffer, paddedBuflen, blockSize) { | |
| 149 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 150 | assert(paddedBuflen <= buffer.byteLength, 'unpadded length cannot exceed buffer length') | |
| 151 | assert(blockSize <= buffer.byteLength, 'block size cannot exceed buffer length') | |
| 152 | assert(blockSize >= 1, 'block size must be at least 1 byte') | |
| 153 | ||
| 154 | return binding.sodium_unpad(buffer, paddedBuflen, blockSize) | |
| 155 | } | |
| 156 | ||
| 157 | // crypto_sign | |
| 158 | ||
| 159 | exports.crypto_sign_keypair = function (pk, sk) { | |
| 160 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 161 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 162 | assert( | |
| 163 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 164 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 165 | ) | |
| 166 | assert( | |
| 167 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 168 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 169 | ) | |
| 170 | ||
| 171 | const res = binding.crypto_sign_keypair(pk, sk) | |
| 172 | ||
| 173 | if (res !== 0) throw new Error('status: ' + res) | |
| 174 | } | |
| 175 | ||
| 176 | exports.crypto_sign_seed_keypair = function (pk, sk, seed) { | |
| 177 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 178 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 179 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 180 | assert( | |
| 181 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 182 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 183 | ) | |
| 184 | assert( | |
| 185 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 186 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 187 | ) | |
| 188 | assert( | |
| 189 | seed.byteLength === binding.crypto_sign_SEEDBYTES, | |
| 190 | "seed must be 'crypto_sign_SEEDBYTES' bytes" | |
| 191 | ) | |
| 192 | ||
| 193 | const res = binding.crypto_sign_seed_keypair(pk, sk, seed) | |
| 194 | ||
| 195 | if (res !== 0) throw new Error('status: ' + res) | |
| 196 | } | |
| 197 | ||
| 198 | exports.crypto_sign = function (sm, m, sk) { | |
| 199 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 200 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 201 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 202 | assert( | |
| 203 | sm.byteLength === binding.crypto_sign_BYTES + m.byteLength, | |
| 204 | "sm must be 'm.byteLength + crypto_sign_BYTES' bytes" | |
| 205 | ) | |
| 206 | assert( | |
| 207 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 208 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 209 | ) | |
| 210 | ||
| 211 | const res = binding.crypto_sign(sm, m, sk) | |
| 212 | ||
| 213 | if (res !== 0) throw new Error('status: ' + res) | |
| 214 | } | |
| 215 | ||
| 216 | exports.crypto_sign_open = function (m, sm, pk) { | |
| 217 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 218 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 219 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 220 | assert( | |
| 221 | sm.byteLength >= binding.crypto_sign_BYTES, | |
| 222 | "sm must be at least 'crypto_sign_BYTES' bytes" | |
| 223 | ) | |
| 224 | assert( | |
| 225 | m.byteLength === sm.byteLength - binding.crypto_sign_BYTES, | |
| 226 | "m must be 'sm.byteLength - crypto_sign_BYTES' bytes" | |
| 227 | ) | |
| 228 | assert( | |
| 229 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 230 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 231 | ) | |
| 232 | ||
| 233 | const res = binding.crypto_sign_open(m, sm, pk) | |
| 234 | ||
| 235 | if (res !== 0) throw new Error('status: ' + res) | |
| 236 | } | |
| 237 | ||
| 238 | exports.crypto_sign_open = function (m, sm, pk) { | |
| 239 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 240 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 241 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 242 | assert( | |
| 243 | sm.byteLength >= binding.crypto_sign_BYTES, | |
| 244 | "sm must be at least 'crypto_sign_BYTES' bytes" | |
| 245 | ) | |
| 246 | assert( | |
| 247 | m.byteLength === sm.byteLength - binding.crypto_sign_BYTES, | |
| 248 | "m must be 'sm.byteLength - crypto_sign_BYTES' bytes" | |
| 249 | ) | |
| 250 | assert( | |
| 251 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 252 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 253 | ) | |
| 254 | ||
| 255 | return binding.crypto_sign_open(m, sm, pk) | |
| 256 | } | |
| 257 | ||
| 258 | exports.crypto_sign_detached = function (sig, m, sk) { | |
| 259 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 260 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 261 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 262 | assert(sig.byteLength === binding.crypto_sign_BYTES, "sig must be 'crypto_sign_BYTES' bytes") | |
| 263 | assert( | |
| 264 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 265 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 266 | ) | |
| 267 | ||
| 268 | const res = binding.crypto_sign_detached(sig, m, sk) | |
| 269 | ||
| 270 | if (res !== 0) throw new Error('status: ' + res) | |
| 271 | } | |
| 272 | ||
| 273 | exports.crypto_sign_verify_detached = function (sig, m, pk) { | |
| 274 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 275 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 276 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 277 | assert( | |
| 278 | sig.byteLength >= binding.crypto_sign_BYTES, | |
| 279 | "sig must be at least 'crypto_sign_BYTES' bytes" | |
| 280 | ) | |
| 281 | assert( | |
| 282 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 283 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 284 | ) | |
| 285 | ||
| 286 | return binding.crypto_sign_verify_detached( | |
| 287 | sig.buffer, | |
| 288 | sig.byteOffset, | |
| 289 | sig.byteLength, | |
| 290 | ||
| 291 | m.buffer, | |
| 292 | m.byteOffset, | |
| 293 | m.byteLength, | |
| 294 | ||
| 295 | pk.buffer, | |
| 296 | pk.byteOffset, | |
| 297 | pk.byteLength | |
| 298 | ) | |
| 299 | } | |
| 300 | ||
| 301 | exports.crypto_sign_ed25519_sk_to_pk = function (pk, sk) { | |
| 302 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 303 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 304 | assert( | |
| 305 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 306 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 307 | ) | |
| 308 | assert( | |
| 309 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 310 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 311 | ) | |
| 312 | ||
| 313 | const res = binding.crypto_sign_ed25519_sk_to_pk(pk, sk) | |
| 314 | ||
| 315 | if (res !== 0) throw new Error('status: ' + res) | |
| 316 | } | |
| 317 | ||
| 318 | exports.crypto_sign_ed25519_pk_to_curve25519 = function (x25519pk, ed25519pk) { | |
| 319 | assert(ArrayBuffer.isView(x25519pk), 'x25519pk must be a typed array') | |
| 320 | assert(ArrayBuffer.isView(ed25519pk), 'ed25519pk must be a typed array') | |
| 321 | assert( | |
| 322 | x25519pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 323 | "x25519pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 324 | ) | |
| 325 | assert( | |
| 326 | ed25519pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 327 | "ed25519pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 328 | ) | |
| 329 | ||
| 330 | const res = binding.crypto_sign_ed25519_pk_to_curve25519(x25519pk, ed25519pk) | |
| 331 | ||
| 332 | if (res !== 0) throw new Error('status: ' + res) | |
| 333 | } | |
| 334 | ||
| 335 | exports.crypto_sign_ed25519_sk_to_curve25519 = function (x25519sk, ed25519sk) { | |
| 336 | assert(ArrayBuffer.isView(x25519sk), 'x25519sk must be a typed array') | |
| 337 | assert(ArrayBuffer.isView(ed25519sk), 'ed25519sk must be a typed array') | |
| 338 | assert( | |
| 339 | x25519sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 340 | "x25519sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 341 | ) | |
| 342 | ||
| 343 | const edLen = ed25519sk.byteLength | |
| 344 | ||
| 345 | assert( | |
| 346 | edLen === binding.crypto_sign_SECRETKEYBYTES || edLen === binding.crypto_box_SECRETKEYBYTES, | |
| 347 | "ed25519sk must be 'crypto_sign_SECRETKEYBYTES' or 'crypto_sign_SECRETKEYBYTES - crypto_sign_PUBLICKEYBYTES' bytes" | |
| 348 | ) | |
| 349 | ||
| 350 | const res = binding.crypto_sign_ed25519_sk_to_curve25519(x25519sk, ed25519sk) | |
| 351 | ||
| 352 | if (res !== 0) throw new Error('status: ' + res) | |
| 353 | } | |
| 354 | ||
| 355 | // crypto_box | |
| 356 | ||
| 357 | exports.crypto_box_keypair = function (pk, sk) { | |
| 358 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 359 | assert( | |
| 360 | pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 361 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 362 | ) | |
| 363 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 364 | assert( | |
| 365 | sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 366 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 367 | ) | |
| 368 | ||
| 369 | const res = binding.crypto_box_keypair(pk, sk) | |
| 370 | ||
| 371 | if (res !== 0) throw new Error('status: ' + res) | |
| 372 | } | |
| 373 | ||
| 374 | exports.crypto_box_seed_keypair = function (pk, sk, seed) { | |
| 375 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 376 | assert( | |
| 377 | pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 378 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 379 | ) | |
| 380 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 381 | assert( | |
| 382 | sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 383 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 384 | ) | |
| 385 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 386 | assert( | |
| 387 | seed.byteLength === binding.crypto_box_SEEDBYTES, | |
| 388 | "seed must be 'crypto_box_SEEDBYTES' bytes" | |
| 389 | ) | |
| 390 | ||
| 391 | const res = binding.crypto_box_seed_keypair(pk, sk, seed) | |
| 392 | ||
| 393 | if (res !== 0) throw new Error('status: ' + res) | |
| 394 | } | |
| 395 | ||
| 396 | exports.crypto_box_easy = function (c, m, n, pk, sk) { | |
| 397 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 398 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 399 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 400 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 401 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 402 | assert( | |
| 403 | c.byteLength === m.byteLength + exports.crypto_box_MACBYTES, | |
| 404 | "c must be 'm.byteLength + crypto_box_MACBYTES' bytes" | |
| 405 | ) | |
| 406 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 407 | assert( | |
| 408 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 409 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 410 | ) | |
| 411 | assert( | |
| 412 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 413 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 414 | ) | |
| 415 | ||
| 416 | const res = binding.crypto_box_easy(c, m, n, pk, sk) | |
| 417 | ||
| 418 | if (res !== 0) throw new Error('status: ' + res) | |
| 419 | } | |
| 420 | ||
| 421 | exports.crypto_box_detached = function (c, mac, m, n, pk, sk) { | |
| 422 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 423 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 424 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 425 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 426 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 427 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 428 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 429 | assert(mac.byteLength === exports.crypto_box_MACBYTES, "mac must be 'crypto_box_MACBYTES' bytes") | |
| 430 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 431 | assert( | |
| 432 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 433 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 434 | ) | |
| 435 | assert( | |
| 436 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 437 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 438 | ) | |
| 439 | ||
| 440 | const res = binding.crypto_box_detached(c, mac, m, n, pk, sk) | |
| 441 | ||
| 442 | if (res !== 0) throw new Error('status: ' + res) | |
| 443 | } | |
| 444 | ||
| 445 | exports.crypto_box_open_easy = function (m, c, n, pk, sk) { | |
| 446 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 447 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 448 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 449 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 450 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 451 | assert( | |
| 452 | c.byteLength >= exports.crypto_box_MACBYTES, | |
| 453 | "c must be at least 'crypto_box_MACBYTES' bytes" | |
| 454 | ) | |
| 455 | assert( | |
| 456 | m.byteLength === c.byteLength - exports.crypto_box_MACBYTES, | |
| 457 | "m must be 'c.byteLength - crypto_box_MACBYTES' bytes" | |
| 458 | ) | |
| 459 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 460 | assert( | |
| 461 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 462 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 463 | ) | |
| 464 | assert( | |
| 465 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 466 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 467 | ) | |
| 468 | ||
| 469 | return binding.crypto_box_open_easy(m, c, n, pk, sk) | |
| 470 | } | |
| 471 | ||
| 472 | exports.crypto_box_open_detached = function (m, c, mac, n, pk, sk) { | |
| 473 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 474 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 475 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 476 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 477 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 478 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 479 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 480 | assert(mac.byteLength === exports.crypto_box_MACBYTES, "mac must be 'crypto_box_MACBYTES' bytes") | |
| 481 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 482 | assert( | |
| 483 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 484 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 485 | ) | |
| 486 | assert( | |
| 487 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 488 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 489 | ) | |
| 490 | ||
| 491 | return binding.crypto_box_open_detached(m, c, mac, n, pk, sk) | |
| 492 | } | |
| 493 | ||
| 494 | exports.crypto_box_seal = function (c, m, pk) { | |
| 495 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 496 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 497 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 498 | assert( | |
| 499 | c.byteLength === m.byteLength + exports.crypto_box_SEALBYTES, | |
| 500 | "c must be 'm.byteLength + crypto_box_SEALBYTES' bytes" | |
| 501 | ) | |
| 502 | assert( | |
| 503 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 504 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 505 | ) | |
| 506 | ||
| 507 | const res = binding.crypto_box_seal(c, m, pk) | |
| 508 | ||
| 509 | if (res !== 0) throw new Error('status: ' + res) | |
| 510 | } | |
| 511 | ||
| 512 | exports.crypto_box_seal_open = function (m, c, pk, sk) { | |
| 513 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 514 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 515 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 516 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 517 | assert( | |
| 518 | c.byteLength >= exports.crypto_box_SEALBYTES, | |
| 519 | "c must be at least 'crypto_box_SEALBYTES' bytes" | |
| 520 | ) | |
| 521 | assert( | |
| 522 | m.byteLength === c.byteLength - exports.crypto_box_SEALBYTES, | |
| 523 | "m must be 'c.byteLength - crypto_box_SEALBYTES' bytes" | |
| 524 | ) | |
| 525 | assert( | |
| 526 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 527 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 528 | ) | |
| 529 | assert( | |
| 530 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 531 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 532 | ) | |
| 533 | ||
| 534 | return binding.crypto_box_seal_open( | |
| 535 | m.buffer, | |
| 536 | m.byteOffset, | |
| 537 | m.byteLength, | |
| 538 | ||
| 539 | c.buffer, | |
| 540 | c.byteOffset, | |
| 541 | c.byteLength, | |
| 542 | ||
| 543 | pk.buffer, | |
| 544 | pk.byteOffset, | |
| 545 | pk.byteLength, | |
| 546 | ||
| 547 | sk.buffer, | |
| 548 | sk.byteOffset, | |
| 549 | sk.byteLength | |
| 550 | ) | |
| 551 | } | |
| 552 | ||
| 553 | // crypto_secretbox | |
| 554 | ||
| 555 | exports.crypto_secretbox_easy = function (c, m, n, k) { | |
| 556 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 557 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 558 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 559 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 560 | assert( | |
| 561 | c.byteLength === m.byteLength + binding.crypto_secretbox_MACBYTES, | |
| 562 | "c must be 'm.byteLength + crypto_secretbox_MACBYTES' bytes" | |
| 563 | ) | |
| 564 | assert( | |
| 565 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 566 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 567 | ) | |
| 568 | assert( | |
| 569 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 570 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 571 | ) | |
| 572 | ||
| 573 | const res = binding.crypto_secretbox_easy(c, m, n, k) | |
| 574 | ||
| 575 | if (res !== 0) throw new Error('status: ' + res) | |
| 576 | } | |
| 577 | ||
| 578 | exports.crypto_secretbox_open_easy = function (m, c, n, k) { | |
| 579 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 580 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 581 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 582 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 583 | assert( | |
| 584 | c.byteLength >= binding.crypto_secretbox_MACBYTES, | |
| 585 | "c must be at least 'crypto_secretbox_MACBYTES' bytes" | |
| 586 | ) | |
| 587 | assert( | |
| 588 | m.byteLength === c.byteLength - binding.crypto_secretbox_MACBYTES, | |
| 589 | "m must be 'c.byteLength - crypto_secretbox_MACBYTES' bytes" | |
| 590 | ) | |
| 591 | assert( | |
| 592 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 593 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 594 | ) | |
| 595 | assert( | |
| 596 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 597 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 598 | ) | |
| 599 | ||
| 600 | return binding.crypto_secretbox_open_easy(m, c, n, k) | |
| 601 | } | |
| 602 | ||
| 603 | exports.crypto_secretbox_detached = function (c, mac, m, n, k) { | |
| 604 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 605 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 606 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 607 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 608 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 609 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 610 | assert( | |
| 611 | mac.byteLength === binding.crypto_secretbox_MACBYTES, | |
| 612 | "mac must be 'crypto_secretbox_MACBYTES' bytes" | |
| 613 | ) | |
| 614 | assert( | |
| 615 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 616 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 617 | ) | |
| 618 | assert( | |
| 619 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 620 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 621 | ) | |
| 622 | ||
| 623 | const res = binding.crypto_secretbox_detached(c, mac, m, n, k) | |
| 624 | ||
| 625 | if (res !== 0) throw new Error('status: ' + res) | |
| 626 | } | |
| 627 | ||
| 628 | exports.crypto_secretbox_open_detached = function (m, c, mac, n, k) { | |
| 629 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 630 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 631 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 632 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 633 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 634 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 635 | assert( | |
| 636 | mac.byteLength === binding.crypto_secretbox_MACBYTES, | |
| 637 | "mac must be 'crypto_secretbox_MACBYTES' bytes" | |
| 638 | ) | |
| 639 | assert( | |
| 640 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 641 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 642 | ) | |
| 643 | assert( | |
| 644 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 645 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 646 | ) | |
| 647 | ||
| 648 | return binding.crypto_secretbox_open_detached(m, c, mac, n, k) | |
| 649 | } | |
| 650 | ||
| 651 | // crypto_generichash | |
| 652 | ||
| 653 | exports.crypto_generichash = function (output, input, key) { | |
| 654 | if (!key) key = OPTIONAL | |
| 655 | ||
| 656 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 657 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 658 | assert( | |
| 659 | output.byteLength >= binding.crypto_generichash_BYTES_MIN && | |
| 660 | output.byteLength <= binding.crypto_generichash_BYTES_MAX, | |
| 661 | 'output must be between crypto_generichash_BYTES_MIN and crypto_generichash_BYTES_MAX bytes' | |
| 662 | ) | |
| 663 | ||
| 664 | if (key !== OPTIONAL) { | |
| 665 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 666 | assert( | |
| 667 | key.byteLength >= binding.crypto_generichash_KEYBYTES_MIN && | |
| 668 | key.byteLength <= binding.crypto_generichash_KEYBYTES_MAX, | |
| 669 | 'key must be between crypto_generichash_KEYBYTES_MIN and crypto_generichash_KEYBYTES_MAX bytes' | |
| 670 | ) | |
| 671 | } | |
| 672 | ||
| 673 | const res = binding.crypto_generichash( | |
| 674 | output.buffer, | |
| 675 | output.byteOffset, | |
| 676 | output.byteLength, | |
| 677 | ||
| 678 | input.buffer, | |
| 679 | input.byteOffset, | |
| 680 | input.byteLength, | |
| 681 | ||
| 682 | key.buffer, | |
| 683 | key.byteOffset, | |
| 684 | key.byteLength | |
| 685 | ) | |
| 686 | ||
| 687 | if (res !== 0) throw new Error('status: ' + res) | |
| 688 | } | |
| 689 | ||
| 690 | exports.crypto_generichash_batch = function (output, batch, key) { | |
| 691 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 692 | ||
| 693 | if (isNode || batch.length < 4) { | |
| 694 | const res = binding.crypto_generichash_batch(output, batch, !!key, key || OPTIONAL) | |
| 695 | if (res !== 0) throw new Error('status: ' + res) | |
| 696 | } else { | |
| 697 | const state = Buffer.alloc(binding.crypto_generichash_STATEBYTES) | |
| 698 | ||
| 699 | exports.crypto_generichash_init(state, key, output.byteLength) | |
| 700 | ||
| 701 | for (const buf of batch) { | |
| 702 | exports.crypto_generichash_update(state, buf) | |
| 703 | } | |
| 704 | ||
| 705 | exports.crypto_generichash_final(state, output) | |
| 706 | } | |
| 707 | } | |
| 708 | ||
| 709 | exports.crypto_generichash_keygen = function (key) { | |
| 710 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 711 | assert( | |
| 712 | key.byteLength === binding.crypto_generichash_KEYBYTES, | |
| 713 | "key must be 'crypto_generichash_KEYBYTES' bytes" | |
| 714 | ) | |
| 715 | ||
| 716 | const res = binding.crypto_generichash_keygen(key.buffer, key.byteOffset, key.byteLength) | |
| 717 | ||
| 718 | if (res !== 0) throw new Error('status: ' + res) | |
| 719 | } | |
| 720 | ||
| 721 | exports.crypto_generichash_init = function (state, key, outputLength) { | |
| 722 | if (!key) key = OPTIONAL | |
| 723 | ||
| 724 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 725 | assert( | |
| 726 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 727 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 728 | ) | |
| 729 | ||
| 730 | const res = binding.crypto_generichash_init( | |
| 731 | state.buffer, | |
| 732 | state.byteOffset, | |
| 733 | state.byteLength, | |
| 734 | ||
| 735 | key.buffer, | |
| 736 | key.byteOffset, | |
| 737 | key.byteLength, | |
| 738 | ||
| 739 | outputLength | |
| 740 | ) | |
| 741 | ||
| 742 | if (res !== 0) throw new Error('status: ' + res) | |
| 743 | } | |
| 744 | ||
| 745 | exports.crypto_generichash_update = function (state, input) { | |
| 746 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 747 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 748 | assert( | |
| 749 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 750 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 751 | ) | |
| 752 | ||
| 753 | const res = binding.crypto_generichash_update( | |
| 754 | state.buffer, | |
| 755 | state.byteOffset, | |
| 756 | state.byteLength, | |
| 757 | ||
| 758 | input.buffer, | |
| 759 | input.byteOffset, | |
| 760 | input.byteLength | |
| 761 | ) | |
| 762 | ||
| 763 | if (res !== 0) throw new Error('status: ' + res) | |
| 764 | } | |
| 765 | ||
| 766 | exports.crypto_generichash_final = function (state, output) { | |
| 767 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 768 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 769 | assert( | |
| 770 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 771 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 772 | ) | |
| 773 | ||
| 774 | const res = binding.crypto_generichash_final( | |
| 775 | state.buffer, | |
| 776 | state.byteOffset, | |
| 777 | state.byteLength, | |
| 778 | ||
| 779 | output.buffer, | |
| 780 | output.byteOffset, | |
| 781 | output.byteLength | |
| 782 | ) | |
| 783 | ||
| 784 | if (res !== 0) throw new Error('status: ' + res) | |
| 785 | } | |
| 786 | ||
| 787 | // secretstream | |
| 788 | ||
| 789 | exports.crypto_secretstream_xchacha20poly1305_keygen = function (k) { | |
| 790 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 791 | assert( | |
| 792 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 793 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 794 | ) | |
| 795 | ||
| 796 | binding.crypto_secretstream_xchacha20poly1305_keygen(k.buffer, k.byteOffset, k.byteLength) | |
| 797 | } | |
| 798 | ||
| 799 | exports.crypto_secretstream_xchacha20poly1305_init_push = function (state, header, k) { | |
| 800 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 801 | assert(ArrayBuffer.isView(header), 'header must be a typed array') | |
| 802 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 803 | assert( | |
| 804 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 805 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 806 | ) | |
| 807 | assert( | |
| 808 | header.byteLength === binding.crypto_secretstream_xchacha20poly1305_HEADERBYTES, | |
| 809 | "header must be 'crypto_secretstream_xchacha20poly1305_HEADERBYTES' bytes" | |
| 810 | ) | |
| 811 | assert( | |
| 812 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 813 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 814 | ) | |
| 815 | ||
| 816 | const res = binding.crypto_secretstream_xchacha20poly1305_init_push( | |
| 817 | state.buffer, | |
| 818 | state.byteOffset, | |
| 819 | state.byteLength, | |
| 820 | ||
| 821 | header.buffer, | |
| 822 | header.byteOffset, | |
| 823 | header.byteLength, | |
| 824 | ||
| 825 | k.buffer, | |
| 826 | k.byteOffset, | |
| 827 | k.byteLength | |
| 828 | ) | |
| 829 | ||
| 830 | if (res !== 0) throw new Error('status: ' + res) | |
| 831 | } | |
| 832 | ||
| 833 | exports.crypto_secretstream_xchacha20poly1305_init_pull = function (state, header, k) { | |
| 834 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 835 | assert(ArrayBuffer.isView(header), 'header must be a typed array') | |
| 836 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 837 | assert( | |
| 838 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 839 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 840 | ) | |
| 841 | assert( | |
| 842 | header.byteLength === binding.crypto_secretstream_xchacha20poly1305_HEADERBYTES, | |
| 843 | "header must be 'crypto_secretstream_xchacha20poly1305_HEADERBYTES' bytes" | |
| 844 | ) | |
| 845 | assert( | |
| 846 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 847 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 848 | ) | |
| 849 | ||
| 850 | const res = binding.crypto_secretstream_xchacha20poly1305_init_pull( | |
| 851 | state.buffer, | |
| 852 | state.byteOffset, | |
| 853 | state.byteLength, | |
| 854 | ||
| 855 | header.buffer, | |
| 856 | header.byteOffset, | |
| 857 | header.byteLength, | |
| 858 | ||
| 859 | k.buffer, | |
| 860 | k.byteOffset, | |
| 861 | k.byteLength | |
| 862 | ) | |
| 863 | ||
| 864 | if (res !== 0) throw new Error('status: ' + res) | |
| 865 | } | |
| 866 | ||
| 867 | exports.crypto_secretstream_xchacha20poly1305_push = function (state, c, m, ad, tag) { | |
| 868 | if (!ad) ad = OPTIONAL | |
| 869 | ||
| 870 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 871 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 872 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 873 | assert( | |
| 874 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 875 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 876 | ) | |
| 877 | assert( | |
| 878 | c.byteLength === m.byteLength + binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 879 | "c must be 'm.byteLength + crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 880 | ) | |
| 881 | ||
| 882 | const res = binding.crypto_secretstream_xchacha20poly1305_push( | |
| 883 | state.buffer, | |
| 884 | state.byteOffset, | |
| 885 | state.byteLength, | |
| 886 | ||
| 887 | c.buffer, | |
| 888 | c.byteOffset, | |
| 889 | c.byteLength, | |
| 890 | ||
| 891 | m.buffer, | |
| 892 | m.byteOffset, | |
| 893 | m.byteLength, | |
| 894 | ||
| 895 | ad.buffer, | |
| 896 | ad.byteOffset, | |
| 897 | ad.byteLength, | |
| 898 | ||
| 899 | tag | |
| 900 | ) | |
| 901 | ||
| 902 | if (res < 0) throw new Error('push failed') | |
| 903 | ||
| 904 | return res | |
| 905 | } | |
| 906 | ||
| 907 | exports.crypto_secretstream_xchacha20poly1305_pull = function (state, m, tag, c, ad) { | |
| 908 | if (!ad) ad = OPTIONAL | |
| 909 | ||
| 910 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 911 | assert( | |
| 912 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 913 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 914 | ) | |
| 915 | assert(ArrayBuffer.isView(tag), 'tag must be a typed array') | |
| 916 | assert(tag.byteLength === 1, 'tag must be 1 byte') | |
| 917 | ||
| 918 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 919 | assert( | |
| 920 | c.byteLength >= binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 921 | "c must be at least 'crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 922 | ) | |
| 923 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 924 | assert( | |
| 925 | m.byteLength === c.byteLength - binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 926 | "m must be 'c.byteLength - crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 927 | ) | |
| 928 | ||
| 929 | const res = binding.crypto_secretstream_xchacha20poly1305_pull( | |
| 930 | state.buffer, | |
| 931 | state.byteOffset, | |
| 932 | state.byteLength, | |
| 933 | ||
| 934 | m.buffer, | |
| 935 | m.byteOffset, | |
| 936 | m.byteLength, | |
| 937 | ||
| 938 | tag.buffer, | |
| 939 | tag.byteOffset, | |
| 940 | tag.byteLength, | |
| 941 | ||
| 942 | c.buffer, | |
| 943 | c.byteOffset, | |
| 944 | c.byteLength, | |
| 945 | ||
| 946 | ad.buffer, | |
| 947 | ad.byteOffset, | |
| 948 | ad.byteLength | |
| 949 | ) | |
| 950 | ||
| 951 | if (res < 0) throw new Error('pull failed') | |
| 952 | ||
| 953 | return res | |
| 954 | } | |
| 955 | ||
| 956 | exports.crypto_secretstream_xchacha20poly1305_rekey = function (state) { | |
| 957 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 958 | assert( | |
| 959 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 960 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 961 | ) | |
| 962 | ||
| 963 | binding.crypto_secretstream_xchacha20poly1305_rekey( | |
| 964 | state.buffer, | |
| 965 | state.byteOffset, | |
| 966 | state.byteLength | |
| 967 | ) | |
| 968 | } | |
| 969 | ||
| 970 | // crypto_stream | |
| 971 | ||
| 972 | exports.crypto_stream = function (c, n, k) { | |
| 973 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 974 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 975 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 976 | assert( | |
| 977 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 978 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 979 | ) | |
| 980 | assert( | |
| 981 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 982 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 983 | ) | |
| 984 | ||
| 985 | const res = binding.crypto_stream(c, n, k) | |
| 986 | ||
| 987 | if (res !== 0) throw new Error('status: ' + res) | |
| 988 | } | |
| 989 | ||
| 990 | exports.crypto_stream_xor = function (c, m, n, k) { | |
| 991 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 992 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 993 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 994 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 995 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 996 | assert( | |
| 997 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 998 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 999 | ) | |
| 1000 | assert( | |
| 1001 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 1002 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 1003 | ) | |
| 1004 | ||
| 1005 | const res = binding.crypto_stream_xor( | |
| 1006 | c.buffer, | |
| 1007 | c.byteOffset, | |
| 1008 | c.byteLength, | |
| 1009 | ||
| 1010 | m.buffer, | |
| 1011 | m.byteOffset, | |
| 1012 | m.byteLength, | |
| 1013 | ||
| 1014 | n.buffer, | |
| 1015 | n.byteOffset, | |
| 1016 | n.byteLength, | |
| 1017 | ||
| 1018 | k.buffer, | |
| 1019 | k.byteOffset, | |
| 1020 | k.byteLength | |
| 1021 | ) | |
| 1022 | ||
| 1023 | if (res !== 0) throw new Error('status: ' + res) | |
| 1024 | } | |
| 1025 | ||
| 1026 | exports.crypto_stream_chacha20 = function (c, n, k) { | |
| 1027 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1028 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1029 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1030 | assert( | |
| 1031 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1032 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1033 | ) | |
| 1034 | assert( | |
| 1035 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1036 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1037 | ) | |
| 1038 | ||
| 1039 | const res = binding.crypto_stream_chacha20(c, n, k) | |
| 1040 | ||
| 1041 | if (res !== 0) throw new Error('status: ' + res) | |
| 1042 | } | |
| 1043 | ||
| 1044 | exports.crypto_stream_chacha20_xor = function (c, m, n, k) { | |
| 1045 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1046 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1047 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1048 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1049 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1050 | assert( | |
| 1051 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1052 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1053 | ) | |
| 1054 | assert( | |
| 1055 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1056 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1057 | ) | |
| 1058 | ||
| 1059 | const res = binding.crypto_stream_chacha20_xor(c, m, n, k) | |
| 1060 | ||
| 1061 | if (res !== 0) throw new Error('status: ' + res) | |
| 1062 | } | |
| 1063 | ||
| 1064 | exports.crypto_stream_chacha20_xor_ic = function (c, m, n, ic, k) { | |
| 1065 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1066 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1067 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1068 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1069 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1070 | assert( | |
| 1071 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1072 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1073 | ) | |
| 1074 | assert( | |
| 1075 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1076 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1077 | ) | |
| 1078 | ||
| 1079 | const res = binding.crypto_stream_chacha20_xor_ic(c, m, n, ic, k) | |
| 1080 | ||
| 1081 | if (res !== 0) throw new Error('status: ' + res) | |
| 1082 | } | |
| 1083 | ||
| 1084 | exports.crypto_stream_chacha20_ietf = function (c, n, k) { | |
| 1085 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1086 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1087 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1088 | assert( | |
| 1089 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1090 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1091 | ) | |
| 1092 | assert( | |
| 1093 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1094 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1095 | ) | |
| 1096 | ||
| 1097 | const res = binding.crypto_stream_chacha20_ietf(c, n, k) | |
| 1098 | ||
| 1099 | if (res !== 0) throw new Error('status: ' + res) | |
| 1100 | } | |
| 1101 | ||
| 1102 | exports.crypto_stream_chacha20_ietf_xor = function (c, m, n, k) { | |
| 1103 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1104 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1105 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1106 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1107 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1108 | assert( | |
| 1109 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1110 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1111 | ) | |
| 1112 | assert( | |
| 1113 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1114 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1115 | ) | |
| 1116 | ||
| 1117 | const res = binding.crypto_stream_chacha20_ietf_xor(c, m, n, k) | |
| 1118 | ||
| 1119 | if (res !== 0) throw new Error('status: ' + res) | |
| 1120 | } | |
| 1121 | ||
| 1122 | exports.crypto_stream_chacha20_ietf_xor_ic = function (c, m, n, ic, k) { | |
| 1123 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1124 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1125 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1126 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1127 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1128 | assert( | |
| 1129 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1130 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1131 | ) | |
| 1132 | assert( | |
| 1133 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1134 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1135 | ) | |
| 1136 | ||
| 1137 | const res = binding.crypto_stream_chacha20_ietf_xor_ic(c, m, n, ic, k) | |
| 1138 | ||
| 1139 | if (res !== 0) throw new Error('status: ' + res) | |
| 1140 | } | |
| 1141 | ||
| 1142 | exports.crypto_stream_xchacha20 = function (c, n, k) { | |
| 1143 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1144 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1145 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1146 | assert( | |
| 1147 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1148 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1149 | ) | |
| 1150 | assert( | |
| 1151 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1152 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1153 | ) | |
| 1154 | ||
| 1155 | const res = binding.crypto_stream_xchacha20(c, n, k) | |
| 1156 | ||
| 1157 | if (res !== 0) throw new Error('status: ' + res) | |
| 1158 | } | |
| 1159 | ||
| 1160 | exports.crypto_stream_xchacha20_xor = function (c, m, n, k) { | |
| 1161 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1162 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1163 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1164 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1165 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1166 | assert( | |
| 1167 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1168 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1169 | ) | |
| 1170 | assert( | |
| 1171 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1172 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1173 | ) | |
| 1174 | ||
| 1175 | const res = binding.crypto_stream_xchacha20_xor(c, m, n, k) | |
| 1176 | ||
| 1177 | if (res !== 0) throw new Error('status: ' + res) | |
| 1178 | } | |
| 1179 | ||
| 1180 | exports.crypto_stream_xchacha20_xor_ic = function (c, m, n, ic, k) { | |
| 1181 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1182 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1183 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1184 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1185 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1186 | assert( | |
| 1187 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1188 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1189 | ) | |
| 1190 | assert( | |
| 1191 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1192 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1193 | ) | |
| 1194 | ||
| 1195 | const res = binding.crypto_stream_xchacha20_xor_ic(c, m, n, ic, k) | |
| 1196 | ||
| 1197 | if (res !== 0) throw new Error('status: ' + res) | |
| 1198 | } | |
| 1199 | ||
| 1200 | exports.crypto_stream_salsa20 = function (c, n, k) { | |
| 1201 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1202 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1203 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1204 | assert( | |
| 1205 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1206 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1207 | ) | |
| 1208 | assert( | |
| 1209 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1210 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1211 | ) | |
| 1212 | ||
| 1213 | const res = binding.crypto_stream_salsa20(c, n, k) | |
| 1214 | ||
| 1215 | if (res !== 0) throw new Error('status: ' + res) | |
| 1216 | } | |
| 1217 | ||
| 1218 | exports.crypto_stream_salsa20_xor = function (c, m, n, k) { | |
| 1219 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1220 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1221 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1222 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1223 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1224 | assert( | |
| 1225 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1226 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1227 | ) | |
| 1228 | assert( | |
| 1229 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1230 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1231 | ) | |
| 1232 | ||
| 1233 | const res = binding.crypto_stream_salsa20_xor(c, m, n, k) | |
| 1234 | ||
| 1235 | if (res !== 0) throw new Error('status: ' + res) | |
| 1236 | } | |
| 1237 | ||
| 1238 | exports.crypto_stream_salsa20_xor_ic = function (c, m, n, ic, k) { | |
| 1239 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1240 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1241 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1242 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1243 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1244 | assert( | |
| 1245 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1246 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1247 | ) | |
| 1248 | assert( | |
| 1249 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1250 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1251 | ) | |
| 1252 | ||
| 1253 | const res = binding.crypto_stream_salsa20_xor_ic(c, m, n, ic, k) | |
| 1254 | ||
| 1255 | if (res !== 0) throw new Error('status: ' + res) | |
| 1256 | } | |
| 1257 | ||
| 1258 | // crypto_auth | |
| 1259 | ||
| 1260 | exports.crypto_auth = function (out, input, k) { | |
| 1261 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1262 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1263 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1264 | assert(out.byteLength === binding.crypto_auth_BYTES, "out must be 'crypto_auth_BYTES' bytes") | |
| 1265 | assert(k.byteLength === binding.crypto_auth_KEYBYTES, "k must be 'crypto_auth_KEYBYTES' bytes") | |
| 1266 | ||
| 1267 | const res = binding.crypto_auth(out, input, k) | |
| 1268 | ||
| 1269 | if (res !== 0) throw new Error('status: ' + res) | |
| 1270 | } | |
| 1271 | ||
| 1272 | exports.crypto_auth_verify = function (h, input, k) { | |
| 1273 | assert(ArrayBuffer.isView(h), 'h must be a typed array') | |
| 1274 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1275 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1276 | assert(h.byteLength === binding.crypto_auth_BYTES, "h must be 'crypto_auth_BYTES' bytes") | |
| 1277 | assert(k.byteLength === binding.crypto_auth_KEYBYTES, "k must be 'crypto_auth_KEYBYTES' bytes") | |
| 1278 | ||
| 1279 | return binding.crypto_auth_verify(h, input, k) | |
| 1280 | } | |
| 1281 | ||
| 1282 | // crypto_onetimeauth | |
| 1283 | ||
| 1284 | exports.crypto_onetimeauth = function (out, input, k) { | |
| 1285 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1286 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1287 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1288 | assert( | |
| 1289 | out.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1290 | "out must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1291 | ) | |
| 1292 | assert( | |
| 1293 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1294 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1295 | ) | |
| 1296 | ||
| 1297 | const res = binding.crypto_onetimeauth(out, input, k) | |
| 1298 | ||
| 1299 | if (res !== 0) throw new Error('status: ' + res) | |
| 1300 | } | |
| 1301 | ||
| 1302 | exports.crypto_onetimeauth_init = function (state, k) { | |
| 1303 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1304 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1305 | assert( | |
| 1306 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1307 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1308 | ) | |
| 1309 | assert( | |
| 1310 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1311 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1312 | ) | |
| 1313 | ||
| 1314 | const res = binding.crypto_onetimeauth_init(state, k) | |
| 1315 | ||
| 1316 | if (res !== 0) throw new Error('status: ' + res) | |
| 1317 | } | |
| 1318 | ||
| 1319 | exports.crypto_onetimeauth_update = function (state, input) { | |
| 1320 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1321 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1322 | assert( | |
| 1323 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1324 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1325 | ) | |
| 1326 | ||
| 1327 | const res = binding.crypto_onetimeauth_update(state, input) | |
| 1328 | ||
| 1329 | if (res !== 0) throw new Error('status: ' + res) | |
| 1330 | } | |
| 1331 | ||
| 1332 | exports.crypto_onetimeauth_final = function (state, out) { | |
| 1333 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1334 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1335 | assert( | |
| 1336 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1337 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1338 | ) | |
| 1339 | assert( | |
| 1340 | out.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1341 | "out must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1342 | ) | |
| 1343 | ||
| 1344 | const res = binding.crypto_onetimeauth_final(state, out) | |
| 1345 | ||
| 1346 | if (res !== 0) throw new Error('status: ' + res) | |
| 1347 | } | |
| 1348 | ||
| 1349 | exports.crypto_onetimeauth_verify = function (h, input, k) { | |
| 1350 | assert(ArrayBuffer.isView(h), 'h must be a typed array') | |
| 1351 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1352 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1353 | assert( | |
| 1354 | h.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1355 | "h must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1356 | ) | |
| 1357 | assert( | |
| 1358 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1359 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1360 | ) | |
| 1361 | ||
| 1362 | return binding.crypto_onetimeauth_verify(h, input, k) | |
| 1363 | } | |
| 1364 | ||
| 1365 | // crypto_pwhash | |
| 1366 | ||
| 1367 | exports.crypto_pwhash = function (out, passwd, salt, opslimit, memlimit, alg) { | |
| 1368 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1369 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1370 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1371 | assert( | |
| 1372 | out.byteLength >= binding.crypto_pwhash_BYTES_MIN, | |
| 1373 | "out must be at least 'crypto_pwhash_BYTES_MIN' bytes" | |
| 1374 | ) | |
| 1375 | assert( | |
| 1376 | out.byteLength <= binding.crypto_pwhash_BYTES_MAX, | |
| 1377 | "out must be at most 'crypto_pwhash_BYTES_MAX' bytes" | |
| 1378 | ) | |
| 1379 | assert( | |
| 1380 | salt.byteLength === binding.crypto_pwhash_SALTBYTES, | |
| 1381 | "salt must be 'crypto_pwhash_SALTBYTES' bytes" | |
| 1382 | ) | |
| 1383 | assert( | |
| 1384 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1385 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1386 | ) | |
| 1387 | assert( | |
| 1388 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1389 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1390 | ) | |
| 1391 | assert( | |
| 1392 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1393 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1394 | ) | |
| 1395 | assert( | |
| 1396 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1397 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1398 | ) | |
| 1399 | assert(alg >= 1 && alg <= 2, 'alg must be either Argon2i 1.3 or Argon2id 1.3') | |
| 1400 | ||
| 1401 | const res = binding.crypto_pwhash(out, passwd, salt, opslimit, memlimit, alg) | |
| 1402 | ||
| 1403 | if (res !== 0) throw new Error('status: ' + res) | |
| 1404 | } | |
| 1405 | ||
| 1406 | exports.crypto_pwhash_async = function ( | |
| 1407 | out, | |
| 1408 | passwd, | |
| 1409 | salt, | |
| 1410 | opslimit, | |
| 1411 | memlimit, | |
| 1412 | alg, | |
| 1413 | callback = undefined | |
| 1414 | ) { | |
| 1415 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1416 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1417 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1418 | assert( | |
| 1419 | out.byteLength >= binding.crypto_pwhash_BYTES_MIN, | |
| 1420 | "out must be at least 'crypto_pwhash_BYTES_MIN' bytes" | |
| 1421 | ) | |
| 1422 | assert( | |
| 1423 | out.byteLength <= binding.crypto_pwhash_BYTES_MAX, | |
| 1424 | "out must be at most 'crypto_pwhash_BYTES_MAX' bytes" | |
| 1425 | ) | |
| 1426 | assert( | |
| 1427 | salt.byteLength === binding.crypto_pwhash_SALTBYTES, | |
| 1428 | "salt must be 'crypto_pwhash_SALTBYTES' bytes" | |
| 1429 | ) | |
| 1430 | assert( | |
| 1431 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1432 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1433 | ) | |
| 1434 | assert( | |
| 1435 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1436 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1437 | ) | |
| 1438 | assert( | |
| 1439 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1440 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1441 | ) | |
| 1442 | assert( | |
| 1443 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1444 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1445 | ) | |
| 1446 | assert(alg >= 1 && alg <= 2, 'alg must be either Argon2i 1.3 or Argon2id 1.3') | |
| 1447 | ||
| 1448 | const [done, promise] = checkStatus(callback) | |
| 1449 | ||
| 1450 | binding.crypto_pwhash_async( | |
| 1451 | out.buffer, | |
| 1452 | out.byteOffset, | |
| 1453 | out.byteLength, | |
| 1454 | ||
| 1455 | passwd.buffer, | |
| 1456 | passwd.byteOffset, | |
| 1457 | passwd.byteLength, | |
| 1458 | ||
| 1459 | salt.buffer, | |
| 1460 | salt.byteOffset, | |
| 1461 | salt.byteLength, | |
| 1462 | ||
| 1463 | opslimit, | |
| 1464 | memlimit, | |
| 1465 | alg, | |
| 1466 | ||
| 1467 | done | |
| 1468 | ) | |
| 1469 | ||
| 1470 | return promise | |
| 1471 | } | |
| 1472 | ||
| 1473 | exports.crypto_pwhash_str = function (out, passwd, opslimit, memlimit) { | |
| 1474 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1475 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1476 | assert( | |
| 1477 | out.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1478 | "out must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1479 | ) | |
| 1480 | assert(typeof opslimit === 'number', 'opslimit must be a number') | |
| 1481 | assert( | |
| 1482 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1483 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1484 | ) | |
| 1485 | assert( | |
| 1486 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1487 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1488 | ) | |
| 1489 | assert(typeof memlimit === 'number', 'memlimit must be a number') | |
| 1490 | assert( | |
| 1491 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1492 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1493 | ) | |
| 1494 | assert( | |
| 1495 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1496 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1497 | ) | |
| 1498 | ||
| 1499 | const res = binding.crypto_pwhash_str(out, passwd, opslimit, memlimit) | |
| 1500 | ||
| 1501 | if (res !== 0) throw new Error('status: ' + res) | |
| 1502 | } | |
| 1503 | ||
| 1504 | exports.crypto_pwhash_str_async = function (out, passwd, opslimit, memlimit, callback = undefined) { | |
| 1505 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1506 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1507 | assert( | |
| 1508 | out.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1509 | "out must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1510 | ) | |
| 1511 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1512 | assert(typeof opslimit === 'number', 'opslimit must be a number') | |
| 1513 | assert( | |
| 1514 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1515 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1516 | ) | |
| 1517 | assert( | |
| 1518 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1519 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1520 | ) | |
| 1521 | assert(typeof memlimit === 'number', 'memlimit must be a number') | |
| 1522 | assert( | |
| 1523 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1524 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1525 | ) | |
| 1526 | assert( | |
| 1527 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1528 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1529 | ) | |
| 1530 | ||
| 1531 | const [done, promise] = checkStatus(callback) | |
| 1532 | ||
| 1533 | binding.crypto_pwhash_str_async( | |
| 1534 | out.buffer, | |
| 1535 | out.byteOffset, | |
| 1536 | out.byteLength, | |
| 1537 | ||
| 1538 | passwd.buffer, | |
| 1539 | passwd.byteOffset, | |
| 1540 | passwd.byteLength, | |
| 1541 | ||
| 1542 | opslimit, | |
| 1543 | memlimit, | |
| 1544 | ||
| 1545 | done | |
| 1546 | ) | |
| 1547 | ||
| 1548 | return promise | |
| 1549 | } | |
| 1550 | ||
| 1551 | exports.crypto_pwhash_str_verify = function (str, passwd) { | |
| 1552 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1553 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1554 | assert( | |
| 1555 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1556 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1557 | ) | |
| 1558 | ||
| 1559 | return binding.crypto_pwhash_str_verify(str, passwd) | |
| 1560 | } | |
| 1561 | ||
| 1562 | exports.crypto_pwhash_str_verify_async = function (str, passwd, callback = undefined) { | |
| 1563 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1564 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1565 | assert( | |
| 1566 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1567 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1568 | ) | |
| 1569 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1570 | ||
| 1571 | const [done, promise] = checkStatus(callback, true) | |
| 1572 | ||
| 1573 | binding.crypto_pwhash_str_verify_async( | |
| 1574 | str.buffer, | |
| 1575 | str.byteOffset, | |
| 1576 | str.byteLength, | |
| 1577 | ||
| 1578 | passwd.buffer, | |
| 1579 | passwd.byteOffset, | |
| 1580 | passwd.byteLength, | |
| 1581 | ||
| 1582 | done | |
| 1583 | ) | |
| 1584 | ||
| 1585 | return promise | |
| 1586 | } | |
| 1587 | ||
| 1588 | exports.crypto_pwhash_str_needs_rehash = function (str, opslimit, memlimit) { | |
| 1589 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1590 | assert( | |
| 1591 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1592 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1593 | ) | |
| 1594 | assert( | |
| 1595 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1596 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1597 | ) | |
| 1598 | assert( | |
| 1599 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1600 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1601 | ) | |
| 1602 | assert( | |
| 1603 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1604 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1605 | ) | |
| 1606 | assert( | |
| 1607 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1608 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1609 | ) | |
| 1610 | ||
| 1611 | return binding.crypto_pwhash_str_needs_rehash(str, opslimit, memlimit) | |
| 1612 | } | |
| 1613 | ||
| 1614 | exports.crypto_pwhash_scryptsalsa208sha256 = function (out, passwd, salt, opslimit, memlimit) { | |
| 1615 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1616 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1617 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1618 | assert( | |
| 1619 | out.byteLength >= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN, | |
| 1620 | "out must be at least 'crypto_pwhash_scryptsalsa208sha256_BYTES_MIN' bytes" | |
| 1621 | ) | |
| 1622 | assert( | |
| 1623 | out.byteLength <= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX, | |
| 1624 | "out must be at most 'crypto_pwhash_scryptsalsa208sha256_BYTES_MAX' bytes" | |
| 1625 | ) | |
| 1626 | assert( | |
| 1627 | salt.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_SALTBYTES, | |
| 1628 | "salt must be 'crypto_pwhash_scryptsalsa208sha256_SALTBYTES' bytes" | |
| 1629 | ) | |
| 1630 | assert( | |
| 1631 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1632 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1633 | ) | |
| 1634 | assert( | |
| 1635 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1636 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1637 | ) | |
| 1638 | assert( | |
| 1639 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1640 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1641 | ) | |
| 1642 | assert( | |
| 1643 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1644 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1645 | ) | |
| 1646 | ||
| 1647 | const res = binding.crypto_pwhash_scryptsalsa208sha256(out, passwd, salt, opslimit, memlimit) | |
| 1648 | ||
| 1649 | if (res !== 0) throw new Error('status: ' + res) | |
| 1650 | } | |
| 1651 | ||
| 1652 | exports.crypto_pwhash_scryptsalsa208sha256_async = function ( | |
| 1653 | out, | |
| 1654 | passwd, | |
| 1655 | salt, | |
| 1656 | opslimit, | |
| 1657 | memlimit, | |
| 1658 | callback = undefined | |
| 1659 | ) { | |
| 1660 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1661 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1662 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1663 | assert( | |
| 1664 | out.byteLength >= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN, | |
| 1665 | "out must be at least 'crypto_pwhash_scryptsalsa208sha256_BYTES_MIN' bytes" | |
| 1666 | ) | |
| 1667 | assert( | |
| 1668 | out.byteLength <= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX, | |
| 1669 | "out must be at most 'crypto_pwhash_scryptsalsa208sha256_BYTES_MAX' bytes" | |
| 1670 | ) | |
| 1671 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1672 | assert( | |
| 1673 | salt.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_SALTBYTES, | |
| 1674 | "salt must be 'crypto_pwhash_scryptsalsa208sha256_SALTBYTES' bytes" | |
| 1675 | ) | |
| 1676 | assert( | |
| 1677 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1678 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1679 | ) | |
| 1680 | assert( | |
| 1681 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1682 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1683 | ) | |
| 1684 | assert( | |
| 1685 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1686 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1687 | ) | |
| 1688 | assert( | |
| 1689 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1690 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1691 | ) | |
| 1692 | ||
| 1693 | const [done, promise] = checkStatus(callback) | |
| 1694 | ||
| 1695 | binding.crypto_pwhash_scryptsalsa208sha256_async( | |
| 1696 | out.buffer, | |
| 1697 | out.byteOffset, | |
| 1698 | out.byteLength, | |
| 1699 | ||
| 1700 | passwd.buffer, | |
| 1701 | passwd.byteOffset, | |
| 1702 | passwd.byteLength, | |
| 1703 | ||
| 1704 | salt.buffer, | |
| 1705 | salt.byteOffset, | |
| 1706 | salt.byteLength, | |
| 1707 | ||
| 1708 | opslimit, | |
| 1709 | memlimit, | |
| 1710 | ||
| 1711 | done | |
| 1712 | ) | |
| 1713 | ||
| 1714 | return promise | |
| 1715 | } | |
| 1716 | ||
| 1717 | exports.crypto_pwhash_scryptsalsa208sha256_str_async = function ( | |
| 1718 | out, | |
| 1719 | passwd, | |
| 1720 | opslimit, | |
| 1721 | memlimit, | |
| 1722 | callback = undefined | |
| 1723 | ) { | |
| 1724 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1725 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1726 | assert( | |
| 1727 | out.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1728 | "out must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1729 | ) | |
| 1730 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1731 | assert( | |
| 1732 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1733 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1734 | ) | |
| 1735 | assert( | |
| 1736 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1737 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1738 | ) | |
| 1739 | assert( | |
| 1740 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1741 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1742 | ) | |
| 1743 | assert( | |
| 1744 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1745 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1746 | ) | |
| 1747 | ||
| 1748 | const [done, promise] = checkStatus(callback) | |
| 1749 | ||
| 1750 | binding.crypto_pwhash_scryptsalsa208sha256_str_async( | |
| 1751 | out.buffer, | |
| 1752 | out.byteOffset, | |
| 1753 | out.byteLength, | |
| 1754 | ||
| 1755 | passwd.buffer, | |
| 1756 | passwd.byteOffset, | |
| 1757 | passwd.byteLength, | |
| 1758 | ||
| 1759 | opslimit, | |
| 1760 | memlimit, | |
| 1761 | ||
| 1762 | done | |
| 1763 | ) | |
| 1764 | ||
| 1765 | return promise | |
| 1766 | } | |
| 1767 | ||
| 1768 | exports.crypto_pwhash_scryptsalsa208sha256_str = function (out, passwd, opslimit, memlimit) { | |
| 1769 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1770 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1771 | assert( | |
| 1772 | out.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1773 | "out must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1774 | ) | |
| 1775 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1776 | assert( | |
| 1777 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1778 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1779 | ) | |
| 1780 | assert( | |
| 1781 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1782 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1783 | ) | |
| 1784 | assert( | |
| 1785 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1786 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1787 | ) | |
| 1788 | assert( | |
| 1789 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1790 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1791 | ) | |
| 1792 | ||
| 1793 | const res = binding.crypto_pwhash_scryptsalsa208sha256_str(out, passwd, opslimit, memlimit) | |
| 1794 | ||
| 1795 | if (res !== 0) throw new Error('status: ' + res) | |
| 1796 | } | |
| 1797 | ||
| 1798 | exports.crypto_pwhash_scryptsalsa208sha256_str_verify_async = function ( | |
| 1799 | str, | |
| 1800 | passwd, | |
| 1801 | callback = undefined | |
| 1802 | ) { | |
| 1803 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1804 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1805 | assert( | |
| 1806 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1807 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1808 | ) | |
| 1809 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1810 | ||
| 1811 | const [done, promise] = checkStatus(callback, true) | |
| 1812 | ||
| 1813 | binding.crypto_pwhash_scryptsalsa208sha256_str_verify_async( | |
| 1814 | str.buffer, | |
| 1815 | str.byteOffset, | |
| 1816 | str.byteLength, | |
| 1817 | ||
| 1818 | passwd.buffer, | |
| 1819 | passwd.byteOffset, | |
| 1820 | passwd.byteLength, | |
| 1821 | ||
| 1822 | done | |
| 1823 | ) | |
| 1824 | ||
| 1825 | return promise | |
| 1826 | } | |
| 1827 | ||
| 1828 | exports.crypto_pwhash_scryptsalsa208sha256_str_verify = function (str, passwd) { | |
| 1829 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1830 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1831 | assert( | |
| 1832 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1833 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1834 | ) | |
| 1835 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1836 | ||
| 1837 | return binding.crypto_pwhash_scryptsalsa208sha256_str_verify(str, passwd) | |
| 1838 | } | |
| 1839 | ||
| 1840 | exports.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash = function (str, opslimit, memlimit) { | |
| 1841 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1842 | assert( | |
| 1843 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1844 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1845 | ) | |
| 1846 | assert( | |
| 1847 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1848 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1849 | ) | |
| 1850 | assert( | |
| 1851 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1852 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1853 | ) | |
| 1854 | assert( | |
| 1855 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1856 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1857 | ) | |
| 1858 | assert( | |
| 1859 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1860 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1861 | ) | |
| 1862 | ||
| 1863 | return binding.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(str, opslimit, memlimit) | |
| 1864 | } | |
| 1865 | ||
| 1866 | // crypto_kx | |
| 1867 | ||
| 1868 | exports.crypto_kx_keypair = function (pk, sk) { | |
| 1869 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 1870 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 1871 | assert( | |
| 1872 | pk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1873 | "pk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1874 | ) | |
| 1875 | assert( | |
| 1876 | sk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1877 | "sk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1878 | ) | |
| 1879 | ||
| 1880 | const res = binding.crypto_kx_keypair(pk, sk) | |
| 1881 | ||
| 1882 | if (res !== 0) throw new Error('status: ' + res) | |
| 1883 | } | |
| 1884 | ||
| 1885 | exports.crypto_kx_seed_keypair = function (pk, sk, seed) { | |
| 1886 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 1887 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 1888 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 1889 | assert( | |
| 1890 | pk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1891 | "pk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1892 | ) | |
| 1893 | assert( | |
| 1894 | sk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1895 | "sk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1896 | ) | |
| 1897 | assert( | |
| 1898 | seed.byteLength === binding.crypto_kx_SEEDBYTES, | |
| 1899 | "seed must be 'crypto_kx_SEEDBYTES' bytes" | |
| 1900 | ) | |
| 1901 | ||
| 1902 | const res = binding.crypto_kx_seed_keypair(pk, sk, seed) | |
| 1903 | ||
| 1904 | if (res !== 0) throw new Error('status: ' + res) | |
| 1905 | } | |
| 1906 | ||
| 1907 | exports.crypto_kx_client_session_keys = function (rx, tx, clientPk, clientSk, serverPk) { | |
| 1908 | if (!rx) rx = undefined | |
| 1909 | if (!tx) tx = undefined | |
| 1910 | ||
| 1911 | assert(rx || tx, 'at least one session key must be specified') | |
| 1912 | ||
| 1913 | if (rx) { | |
| 1914 | assert(ArrayBuffer.isView(rx), 'rx must be a typed array') | |
| 1915 | assert( | |
| 1916 | rx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1917 | "rx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1918 | ) | |
| 1919 | } | |
| 1920 | ||
| 1921 | if (tx) { | |
| 1922 | assert(ArrayBuffer.isView(tx), 'tx must be a typed array') | |
| 1923 | assert( | |
| 1924 | tx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1925 | "tx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1926 | ) | |
| 1927 | } | |
| 1928 | ||
| 1929 | assert(ArrayBuffer.isView(clientPk), 'clientPk must be a typed array') | |
| 1930 | assert(ArrayBuffer.isView(clientSk), 'clientSk must be a typed array') | |
| 1931 | assert(ArrayBuffer.isView(serverPk), 'serverPk must be a typed array') | |
| 1932 | assert( | |
| 1933 | clientPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1934 | "clientPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1935 | ) | |
| 1936 | assert( | |
| 1937 | clientSk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1938 | "clientSk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1939 | ) | |
| 1940 | assert( | |
| 1941 | serverPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1942 | "serverPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1943 | ) | |
| 1944 | ||
| 1945 | const res = binding.crypto_kx_client_session_keys(rx, tx, clientPk, clientSk, serverPk) | |
| 1946 | ||
| 1947 | if (res !== 0) throw new Error('status: ' + res) | |
| 1948 | } | |
| 1949 | ||
| 1950 | exports.crypto_kx_server_session_keys = function (rx, tx, serverPk, serverSk, clientPk) { | |
| 1951 | if (!rx) rx = undefined | |
| 1952 | if (!tx) tx = undefined | |
| 1953 | ||
| 1954 | assert(rx || tx, 'at least one session key must be specified') | |
| 1955 | ||
| 1956 | if (rx) { | |
| 1957 | assert(ArrayBuffer.isView(rx), 'rx must be a typed array') | |
| 1958 | assert( | |
| 1959 | rx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1960 | "rx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1961 | ) | |
| 1962 | } | |
| 1963 | ||
| 1964 | if (tx) { | |
| 1965 | assert(ArrayBuffer.isView(tx), 'tx must be a typed array') | |
| 1966 | assert( | |
| 1967 | tx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1968 | "tx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1969 | ) | |
| 1970 | } | |
| 1971 | ||
| 1972 | assert(ArrayBuffer.isView(serverPk), 'serverPk must be a typed array') | |
| 1973 | assert(ArrayBuffer.isView(serverSk), 'serverSk must be a typed array') | |
| 1974 | assert(ArrayBuffer.isView(clientPk), 'clientPk must be a typed array') | |
| 1975 | assert( | |
| 1976 | serverPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1977 | "serverPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1978 | ) | |
| 1979 | assert( | |
| 1980 | serverSk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1981 | "serverSk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1982 | ) | |
| 1983 | assert( | |
| 1984 | clientPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1985 | "clientPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1986 | ) | |
| 1987 | ||
| 1988 | const res = binding.crypto_kx_server_session_keys(rx, tx, serverPk, serverSk, clientPk) | |
| 1989 | ||
| 1990 | if (res !== 0) throw new Error('status: ' + res) | |
| 1991 | } | |
| 1992 | ||
| 1993 | // crypto_scalarmult | |
| 1994 | ||
| 1995 | exports.crypto_scalarmult_base = function (q, n) { | |
| 1996 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 1997 | assert( | |
| 1998 | q.byteLength === binding.crypto_scalarmult_BYTES, | |
| 1999 | "q must be 'crypto_scalarmult_BYTES' bytes" | |
| 2000 | ) | |
| 2001 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2002 | assert( | |
| 2003 | n.byteLength === binding.crypto_scalarmult_SCALARBYTES, | |
| 2004 | "n must be 'crypto_scalarmult_SCALARBYTES' bytes" | |
| 2005 | ) | |
| 2006 | ||
| 2007 | const res = binding.crypto_scalarmult_base(q, n) | |
| 2008 | ||
| 2009 | if (res !== 0) throw new Error('status: ' + res) | |
| 2010 | } | |
| 2011 | ||
| 2012 | exports.crypto_scalarmult = function (q, n, p) { | |
| 2013 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2014 | assert( | |
| 2015 | q.byteLength === binding.crypto_scalarmult_BYTES, | |
| 2016 | "q must be 'crypto_scalarmult_BYTES' bytes" | |
| 2017 | ) | |
| 2018 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2019 | assert( | |
| 2020 | n.byteLength === binding.crypto_scalarmult_SCALARBYTES, | |
| 2021 | "n must be 'crypto_scalarmult_SCALARBYTES' bytes" | |
| 2022 | ) | |
| 2023 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2024 | assert( | |
| 2025 | p.byteLength === binding.crypto_scalarmult_BYTES, | |
| 2026 | "p must be 'crypto_scalarmult_BYTES' bytes" | |
| 2027 | ) | |
| 2028 | ||
| 2029 | const res = binding.crypto_scalarmult(q, n, p) | |
| 2030 | ||
| 2031 | if (res !== 0) throw new Error('status: ' + res) | |
| 2032 | } | |
| 2033 | ||
| 2034 | exports.crypto_scalarmult_ed25519_base = function (q, n) { | |
| 2035 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2036 | assert( | |
| 2037 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2038 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2039 | ) | |
| 2040 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2041 | assert( | |
| 2042 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2043 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2044 | ) | |
| 2045 | ||
| 2046 | const res = binding.crypto_scalarmult_ed25519_base(q, n) | |
| 2047 | ||
| 2048 | if (res !== 0) throw new Error('status: ' + res) | |
| 2049 | } | |
| 2050 | ||
| 2051 | exports.crypto_scalarmult_ed25519 = function (q, n, p) { | |
| 2052 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2053 | assert( | |
| 2054 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2055 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2056 | ) | |
| 2057 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2058 | assert( | |
| 2059 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2060 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2061 | ) | |
| 2062 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2063 | assert( | |
| 2064 | p.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2065 | "p must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2066 | ) | |
| 2067 | ||
| 2068 | const res = binding.crypto_scalarmult_ed25519(q, n, p) | |
| 2069 | ||
| 2070 | if (res !== 0) throw new Error('status: ' + res) | |
| 2071 | } | |
| 2072 | ||
| 2073 | exports.crypto_core_ed25519_is_valid_point = function (p) { | |
| 2074 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2075 | assert( | |
| 2076 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2077 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2078 | ) | |
| 2079 | ||
| 2080 | return binding.crypto_core_ed25519_is_valid_point(p) | |
| 2081 | } | |
| 2082 | ||
| 2083 | exports.crypto_core_ed25519_from_uniform = function (p, r) { | |
| 2084 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2085 | assert( | |
| 2086 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2087 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2088 | ) | |
| 2089 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2090 | assert( | |
| 2091 | r.byteLength === binding.crypto_core_ed25519_UNIFORMBYTES, | |
| 2092 | "r must be 'crypto_core_ed25519_UNIFORMBYTES' bytes" | |
| 2093 | ) | |
| 2094 | ||
| 2095 | const res = binding.crypto_core_ed25519_from_uniform(p, r) | |
| 2096 | ||
| 2097 | if (res !== 0) throw new Error('status: ' + res) | |
| 2098 | } | |
| 2099 | ||
| 2100 | exports.crypto_scalarmult_ed25519_base_noclamp = function (q, n) { | |
| 2101 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2102 | assert( | |
| 2103 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2104 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2105 | ) | |
| 2106 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2107 | assert( | |
| 2108 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2109 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2110 | ) | |
| 2111 | ||
| 2112 | const res = binding.crypto_scalarmult_ed25519_base_noclamp(q, n) | |
| 2113 | ||
| 2114 | if (res !== 0) throw new Error('status: ' + res) | |
| 2115 | } | |
| 2116 | ||
| 2117 | exports.crypto_scalarmult_ed25519_noclamp = function (q, n, p) { | |
| 2118 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2119 | assert( | |
| 2120 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2121 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2122 | ) | |
| 2123 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2124 | assert( | |
| 2125 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2126 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2127 | ) | |
| 2128 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2129 | assert( | |
| 2130 | p.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2131 | "p must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2132 | ) | |
| 2133 | ||
| 2134 | const res = binding.crypto_scalarmult_ed25519_noclamp(q, n, p) | |
| 2135 | ||
| 2136 | if (res !== 0) throw new Error('status: ' + res) | |
| 2137 | } | |
| 2138 | ||
| 2139 | // crypto_core | |
| 2140 | ||
| 2141 | exports.crypto_core_ed25519_add = function (r, p, q) { | |
| 2142 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2143 | assert( | |
| 2144 | r.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2145 | "r must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2146 | ) | |
| 2147 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2148 | assert( | |
| 2149 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2150 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2151 | ) | |
| 2152 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2153 | assert( | |
| 2154 | q.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2155 | "q must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2156 | ) | |
| 2157 | ||
| 2158 | const res = binding.crypto_core_ed25519_add(r, p, q) | |
| 2159 | ||
| 2160 | if (res !== 0) throw new Error('status: ' + res) | |
| 2161 | } | |
| 2162 | ||
| 2163 | exports.crypto_core_ed25519_sub = function (r, p, q) { | |
| 2164 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2165 | assert( | |
| 2166 | r.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2167 | "r must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2168 | ) | |
| 2169 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2170 | assert( | |
| 2171 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2172 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2173 | ) | |
| 2174 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2175 | assert( | |
| 2176 | q.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2177 | "q must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2178 | ) | |
| 2179 | ||
| 2180 | const res = binding.crypto_core_ed25519_sub(r, p, q) | |
| 2181 | ||
| 2182 | if (res !== 0) throw new Error('status: ' + res) | |
| 2183 | } | |
| 2184 | ||
| 2185 | exports.crypto_core_ed25519_scalar_random = function (r) { | |
| 2186 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2187 | assert( | |
| 2188 | r.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2189 | "r must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2190 | ) | |
| 2191 | ||
| 2192 | binding.crypto_core_ed25519_scalar_random(r) | |
| 2193 | } | |
| 2194 | ||
| 2195 | exports.crypto_core_ed25519_scalar_reduce = function (r, s) { | |
| 2196 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2197 | assert( | |
| 2198 | r.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2199 | "r must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2200 | ) | |
| 2201 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2202 | assert( | |
| 2203 | s.byteLength === binding.crypto_core_ed25519_NONREDUCEDSCALARBYTES, | |
| 2204 | "s must be 'crypto_core_ed25519_NONREDUCEDSCALARBYTES' bytes" | |
| 2205 | ) | |
| 2206 | ||
| 2207 | binding.crypto_core_ed25519_scalar_reduce(r, s) | |
| 2208 | } | |
| 2209 | ||
| 2210 | exports.crypto_core_ed25519_scalar_invert = function (recip, s) { | |
| 2211 | assert(ArrayBuffer.isView(recip), 'recip must be a typed array') | |
| 2212 | assert( | |
| 2213 | recip.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2214 | "recip must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2215 | ) | |
| 2216 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2217 | assert( | |
| 2218 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2219 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2220 | ) | |
| 2221 | ||
| 2222 | binding.crypto_core_ed25519_scalar_invert(recip, s) | |
| 2223 | } | |
| 2224 | ||
| 2225 | exports.crypto_core_ed25519_scalar_negate = function (neg, s) { | |
| 2226 | assert(ArrayBuffer.isView(neg), 'neg must be a typed array') | |
| 2227 | assert( | |
| 2228 | neg.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2229 | "neg must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2230 | ) | |
| 2231 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2232 | assert( | |
| 2233 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2234 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2235 | ) | |
| 2236 | ||
| 2237 | binding.crypto_core_ed25519_scalar_negate(neg, s) | |
| 2238 | } | |
| 2239 | ||
| 2240 | exports.crypto_core_ed25519_scalar_complement = function (comp, s) { | |
| 2241 | assert(ArrayBuffer.isView(comp), 'comp must be a typed array') | |
| 2242 | assert( | |
| 2243 | comp.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2244 | "comp must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2245 | ) | |
| 2246 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2247 | assert( | |
| 2248 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2249 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2250 | ) | |
| 2251 | ||
| 2252 | binding.crypto_core_ed25519_scalar_complement(comp, s) | |
| 2253 | } | |
| 2254 | ||
| 2255 | exports.crypto_core_ed25519_scalar_add = function (z, x, y) { | |
| 2256 | assert(ArrayBuffer.isView(z), 'z must be a typed array') | |
| 2257 | assert( | |
| 2258 | z.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2259 | "z must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2260 | ) | |
| 2261 | assert(ArrayBuffer.isView(x), 'x must be a typed array') | |
| 2262 | assert( | |
| 2263 | x.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2264 | "x must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2265 | ) | |
| 2266 | assert(ArrayBuffer.isView(y), 'y must be a typed array') | |
| 2267 | assert( | |
| 2268 | y.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2269 | "y must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2270 | ) | |
| 2271 | ||
| 2272 | binding.crypto_core_ed25519_scalar_add(z, x, y) | |
| 2273 | } | |
| 2274 | ||
| 2275 | exports.crypto_core_ed25519_scalar_sub = function (z, x, y) { | |
| 2276 | assert(ArrayBuffer.isView(z), 'z must be a typed array') | |
| 2277 | assert( | |
| 2278 | z.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2279 | "z must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2280 | ) | |
| 2281 | assert(ArrayBuffer.isView(x), 'x must be a typed array') | |
| 2282 | assert( | |
| 2283 | x.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2284 | "x must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2285 | ) | |
| 2286 | assert(ArrayBuffer.isView(y), 'y must be a typed array') | |
| 2287 | assert( | |
| 2288 | y.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2289 | "y must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2290 | ) | |
| 2291 | ||
| 2292 | binding.crypto_core_ed25519_scalar_sub(z, x, y) | |
| 2293 | } | |
| 2294 | ||
| 2295 | // crypto_shorthash | |
| 2296 | ||
| 2297 | exports.crypto_shorthash = function (out, input, k) { | |
| 2298 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2299 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2300 | assert( | |
| 2301 | out.byteLength === binding.crypto_shorthash_BYTES, | |
| 2302 | "out must be 'crypto_shorthash_BYTES' bytes" | |
| 2303 | ) | |
| 2304 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2305 | assert( | |
| 2306 | k.byteLength === binding.crypto_shorthash_KEYBYTES, | |
| 2307 | "k must be 'crypto_shorthash_KEYBYTES' bytes" | |
| 2308 | ) | |
| 2309 | ||
| 2310 | const res = binding.crypto_shorthash(out, input, k) | |
| 2311 | ||
| 2312 | if (res !== 0) throw new Error('status: ' + res) | |
| 2313 | } | |
| 2314 | ||
| 2315 | // crypto_kdf | |
| 2316 | ||
| 2317 | exports.crypto_kdf_keygen = function (key) { | |
| 2318 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 2319 | assert(key.byteLength === binding.crypto_kdf_KEYBYTES, "key must be 'crypto_kdf_KEYBYTES' bytes") | |
| 2320 | ||
| 2321 | binding.crypto_kdf_keygen(key) | |
| 2322 | } | |
| 2323 | ||
| 2324 | exports.crypto_kdf_derive_from_key = function (subkey, subkeyId, ctx, key) { | |
| 2325 | assert(ArrayBuffer.isView(subkey), 'subkey must be a typed array') | |
| 2326 | assert( | |
| 2327 | subkey.byteLength >= binding.crypto_kdf_BYTES_MIN, | |
| 2328 | "subkey must be at least 'crypto_kdf_BYTES_MIN' bytes" | |
| 2329 | ) | |
| 2330 | assert( | |
| 2331 | subkey.byteLength <= binding.crypto_kdf_BYTES_MAX, | |
| 2332 | "subkey must be at most 'crypto_kdf_BYTES_MAX' bytes" | |
| 2333 | ) | |
| 2334 | assert(ArrayBuffer.isView(ctx), 'ctx must be a typed array') | |
| 2335 | assert( | |
| 2336 | ctx.byteLength === binding.crypto_kdf_CONTEXTBYTES, | |
| 2337 | "ctx must be 'crypto_kdf_CONTEXTBYTES' bytes" | |
| 2338 | ) | |
| 2339 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 2340 | assert(key.byteLength === binding.crypto_kdf_KEYBYTES, "key must be 'crypto_kdf_KEYBYTES' bytes") | |
| 2341 | ||
| 2342 | const res = binding.crypto_kdf_derive_from_key(subkey, subkeyId, ctx, key) | |
| 2343 | ||
| 2344 | if (res !== 0) throw new Error('status: ' + res) | |
| 2345 | } | |
| 2346 | ||
| 2347 | // crypto_hash | |
| 2348 | ||
| 2349 | exports.crypto_hash = function (out, input) { | |
| 2350 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2351 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2352 | assert(out.byteLength === binding.crypto_hash_BYTES, "out must be 'crypto_hash_BYTES' bytes") | |
| 2353 | ||
| 2354 | const res = binding.crypto_hash(out, input) | |
| 2355 | ||
| 2356 | if (res !== 0) throw new Error('status: ' + res) | |
| 2357 | } | |
| 2358 | ||
| 2359 | exports.crypto_hash_sha256 = function (out, input) { | |
| 2360 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2361 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2362 | assert( | |
| 2363 | out.byteLength === binding.crypto_hash_sha256_BYTES, | |
| 2364 | "out must be 'crypto_hash_sha256_BYTES' bytes" | |
| 2365 | ) | |
| 2366 | ||
| 2367 | const res = binding.crypto_hash_sha256(out, input) | |
| 2368 | ||
| 2369 | if (res !== 0) throw new Error('status: ' + res) | |
| 2370 | } | |
| 2371 | ||
| 2372 | exports.crypto_hash_sha256_init = function (state) { | |
| 2373 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2374 | assert( | |
| 2375 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2376 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2377 | ) | |
| 2378 | ||
| 2379 | const res = binding.crypto_hash_sha256_init(state) | |
| 2380 | ||
| 2381 | if (res !== 0) throw new Error('status: ' + res) | |
| 2382 | } | |
| 2383 | ||
| 2384 | exports.crypto_hash_sha256_update = function (state, input) { | |
| 2385 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2386 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2387 | assert( | |
| 2388 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2389 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2390 | ) | |
| 2391 | ||
| 2392 | const res = binding.crypto_hash_sha256_update(state, input) | |
| 2393 | ||
| 2394 | if (res !== 0) throw new Error('status: ' + res) | |
| 2395 | } | |
| 2396 | ||
| 2397 | exports.crypto_hash_sha256_final = function (state, out) { | |
| 2398 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2399 | assert( | |
| 2400 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2401 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2402 | ) | |
| 2403 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2404 | assert( | |
| 2405 | out.byteLength === binding.crypto_hash_sha256_BYTES, | |
| 2406 | "out must be 'crypto_hash_sha256_BYTES' bytes" | |
| 2407 | ) | |
| 2408 | ||
| 2409 | const res = binding.crypto_hash_sha256_final(state, out) | |
| 2410 | ||
| 2411 | if (res !== 0) throw new Error('status: ' + res) | |
| 2412 | } | |
| 2413 | ||
| 2414 | exports.crypto_hash_sha512 = function (out, input) { | |
| 2415 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2416 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2417 | assert( | |
| 2418 | out.byteLength === binding.crypto_hash_sha512_BYTES, | |
| 2419 | "out must be 'crypto_hash_sha512_BYTES' bytes" | |
| 2420 | ) | |
| 2421 | ||
| 2422 | const res = binding.crypto_hash_sha512(out, input) | |
| 2423 | ||
| 2424 | if (res !== 0) throw new Error('status: ' + res) | |
| 2425 | } | |
| 2426 | ||
| 2427 | exports.crypto_hash_sha512_init = function (state) { | |
| 2428 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2429 | assert( | |
| 2430 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2431 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2432 | ) | |
| 2433 | ||
| 2434 | const res = binding.crypto_hash_sha512_init(state) | |
| 2435 | ||
| 2436 | if (res !== 0) throw new Error('status: ' + res) | |
| 2437 | } | |
| 2438 | ||
| 2439 | exports.crypto_hash_sha512_update = function (state, input) { | |
| 2440 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2441 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2442 | assert( | |
| 2443 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2444 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2445 | ) | |
| 2446 | ||
| 2447 | const res = binding.crypto_hash_sha512_update(state, input) | |
| 2448 | ||
| 2449 | if (res !== 0) throw new Error('status: ' + res) | |
| 2450 | } | |
| 2451 | ||
| 2452 | exports.crypto_hash_sha512_final = function (state, out) { | |
| 2453 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2454 | assert( | |
| 2455 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2456 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2457 | ) | |
| 2458 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2459 | assert( | |
| 2460 | out.byteLength === binding.crypto_hash_sha512_BYTES, | |
| 2461 | "out must be 'crypto_hash_sha512_BYTES' bytes" | |
| 2462 | ) | |
| 2463 | ||
| 2464 | const res = binding.crypto_hash_sha512_final(state, out) | |
| 2465 | ||
| 2466 | if (res !== 0) throw new Error('status: ' + res) | |
| 2467 | } | |
| 2468 | ||
| 2469 | // crypto_aead | |
| 2470 | ||
| 2471 | exports.crypto_aead_xchacha20poly1305_ietf_keygen = function (k) { | |
| 2472 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2473 | assert( | |
| 2474 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2475 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2476 | ) | |
| 2477 | ||
| 2478 | binding.crypto_aead_xchacha20poly1305_ietf_keygen(k) | |
| 2479 | } | |
| 2480 | ||
| 2481 | exports.crypto_aead_xchacha20poly1305_ietf_encrypt = function (c, m, ad, nsec, npub, k) { | |
| 2482 | if (!ad) ad = undefined | |
| 2483 | ||
| 2484 | assert(nsec === null, 'nsec must always be set to null') | |
| 2485 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2486 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2487 | assert( | |
| 2488 | c.byteLength === m.byteLength + binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2489 | "c must be 'm.byteLength + crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2490 | ) | |
| 2491 | assert(c.byteLength <= 0xffffffff, 'c.byteLength must be a 32bit integer') | |
| 2492 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2493 | assert( | |
| 2494 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2495 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2496 | ) | |
| 2497 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2498 | assert( | |
| 2499 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2500 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2501 | ) | |
| 2502 | ||
| 2503 | const res = binding.crypto_aead_xchacha20poly1305_ietf_encrypt(c, m, ad, npub, k) | |
| 2504 | ||
| 2505 | if (res < 0) throw new Error('could not encrypt data') | |
| 2506 | ||
| 2507 | return res | |
| 2508 | } | |
| 2509 | ||
| 2510 | exports.crypto_aead_xchacha20poly1305_ietf_decrypt = function (m, nsec, c, ad, npub, k) { | |
| 2511 | if (!ad) ad = undefined | |
| 2512 | ||
| 2513 | assert(nsec === null, 'nsec must always be set to null') | |
| 2514 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2515 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2516 | assert( | |
| 2517 | m.byteLength === c.byteLength - binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2518 | "m must be 'c.byteLength - crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2519 | ) | |
| 2520 | assert(m.byteLength <= 0xffffffff, 'm.byteLength must be a 32bit integer') | |
| 2521 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2522 | assert( | |
| 2523 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2524 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2525 | ) | |
| 2526 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2527 | assert( | |
| 2528 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2529 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2530 | ) | |
| 2531 | ||
| 2532 | const res = binding.crypto_aead_xchacha20poly1305_ietf_decrypt(m, c, ad, npub, k) | |
| 2533 | ||
| 2534 | if (res < 0) throw new Error('could not verify data') | |
| 2535 | ||
| 2536 | return res | |
| 2537 | } | |
| 2538 | ||
| 2539 | exports.crypto_aead_xchacha20poly1305_ietf_encrypt_detached = function ( | |
| 2540 | c, | |
| 2541 | mac, | |
| 2542 | m, | |
| 2543 | ad, | |
| 2544 | nsec, | |
| 2545 | npub, | |
| 2546 | k | |
| 2547 | ) { | |
| 2548 | if (!ad) ad = undefined | |
| 2549 | ||
| 2550 | assert(nsec === null, 'nsec must always be set to null') | |
| 2551 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2552 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2553 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2554 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2555 | assert( | |
| 2556 | mac.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2557 | "mac must be 'crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2558 | ) | |
| 2559 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2560 | assert( | |
| 2561 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2562 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2563 | ) | |
| 2564 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2565 | assert( | |
| 2566 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2567 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2568 | ) | |
| 2569 | ||
| 2570 | const res = binding.crypto_aead_xchacha20poly1305_ietf_encrypt_detached(c, mac, m, ad, npub, k) | |
| 2571 | ||
| 2572 | if (res < 0) throw new Error('could not encrypt data') | |
| 2573 | ||
| 2574 | return res | |
| 2575 | } | |
| 2576 | ||
| 2577 | exports.crypto_aead_xchacha20poly1305_ietf_decrypt_detached = function ( | |
| 2578 | m, | |
| 2579 | nsec, | |
| 2580 | c, | |
| 2581 | mac, | |
| 2582 | ad, | |
| 2583 | npub, | |
| 2584 | k | |
| 2585 | ) { | |
| 2586 | if (!ad) ad = undefined | |
| 2587 | ||
| 2588 | assert(nsec === null, 'nsec must always be set to null') | |
| 2589 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2590 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2591 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 2592 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2593 | assert( | |
| 2594 | mac.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2595 | "mac must be 'crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2596 | ) | |
| 2597 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2598 | assert( | |
| 2599 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2600 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2601 | ) | |
| 2602 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2603 | assert( | |
| 2604 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2605 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2606 | ) | |
| 2607 | ||
| 2608 | const res = binding.crypto_aead_xchacha20poly1305_ietf_decrypt_detached(m, c, mac, ad, npub, k) | |
| 2609 | ||
| 2610 | if (res !== 0) throw new Error('could not verify data') | |
| 2611 | } | |
| 2612 | ||
| 2613 | exports.crypto_aead_chacha20poly1305_ietf_keygen = function (k) { | |
| 2614 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2615 | assert( | |
| 2616 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2617 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2618 | ) | |
| 2619 | ||
| 2620 | binding.crypto_aead_chacha20poly1305_ietf_keygen(k) | |
| 2621 | } | |
| 2622 | ||
| 2623 | exports.crypto_aead_chacha20poly1305_ietf_encrypt = function (c, m, ad, nsec, npub, k) { | |
| 2624 | if (!ad) ad = undefined | |
| 2625 | ||
| 2626 | assert(nsec === null, 'nsec must always be set to null') | |
| 2627 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2628 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2629 | assert( | |
| 2630 | c.byteLength === m.byteLength + binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2631 | "c must be 'm.byteLength + crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2632 | ) | |
| 2633 | assert(c.byteLength <= 0xffffffff, 'c.byteLength must be a 32bit integer') | |
| 2634 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2635 | assert( | |
| 2636 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2637 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2638 | ) | |
| 2639 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2640 | assert( | |
| 2641 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2642 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2643 | ) | |
| 2644 | ||
| 2645 | const res = binding.crypto_aead_chacha20poly1305_ietf_encrypt(c, m, ad, npub, k) | |
| 2646 | ||
| 2647 | if (res < 0) throw new Error('could not encrypt data') | |
| 2648 | ||
| 2649 | return res | |
| 2650 | } | |
| 2651 | ||
| 2652 | exports.crypto_aead_chacha20poly1305_ietf_decrypt = function (m, nsec, c, ad, npub, k) { | |
| 2653 | if (!ad) ad = undefined | |
| 2654 | ||
| 2655 | assert(nsec === null, 'nsec must always be set to null') | |
| 2656 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2657 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2658 | assert( | |
| 2659 | m.byteLength === c.byteLength - binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2660 | "m must be 'c.byteLength - crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2661 | ) | |
| 2662 | assert(m.byteLength <= 0xffffffff, 'm.byteLength must be a 32bit integer') | |
| 2663 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2664 | assert( | |
| 2665 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2666 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2667 | ) | |
| 2668 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2669 | assert( | |
| 2670 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2671 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2672 | ) | |
| 2673 | ||
| 2674 | const res = binding.crypto_aead_chacha20poly1305_ietf_decrypt(m, c, ad, npub, k) | |
| 2675 | ||
| 2676 | if (res < 0) throw new Error('could not verify data') | |
| 2677 | ||
| 2678 | return res | |
| 2679 | } | |
| 2680 | ||
| 2681 | exports.crypto_aead_chacha20poly1305_ietf_encrypt_detached = function ( | |
| 2682 | c, | |
| 2683 | mac, | |
| 2684 | m, | |
| 2685 | ad, | |
| 2686 | nsec, | |
| 2687 | npub, | |
| 2688 | k | |
| 2689 | ) { | |
| 2690 | if (!ad) ad = undefined | |
| 2691 | ||
| 2692 | assert(nsec === null, 'nsec must always be set to null') | |
| 2693 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2694 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2695 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2696 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2697 | assert( | |
| 2698 | mac.byteLength === binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2699 | "mac must be 'crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2700 | ) | |
| 2701 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2702 | assert( | |
| 2703 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2704 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2705 | ) | |
| 2706 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2707 | assert( | |
| 2708 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2709 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2710 | ) | |
| 2711 | ||
| 2712 | const res = binding.crypto_aead_chacha20poly1305_ietf_encrypt_detached(c, mac, m, ad, npub, k) | |
| 2713 | ||
| 2714 | if (res < 0) throw new Error('could not encrypt data') | |
| 2715 | ||
| 2716 | return res | |
| 2717 | } | |
| 2718 | ||
| 2719 | exports.crypto_aead_chacha20poly1305_ietf_decrypt_detached = function ( | |
| 2720 | m, | |
| 2721 | nsec, | |
| 2722 | c, | |
| 2723 | mac, | |
| 2724 | ad, | |
| 2725 | npub, | |
| 2726 | k | |
| 2727 | ) { | |
| 2728 | if (!ad) ad = undefined | |
| 2729 | ||
| 2730 | assert(nsec === null, 'nsec must always be set to null') | |
| 2731 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2732 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2733 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 2734 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2735 | assert( | |
| 2736 | mac.byteLength === binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2737 | "mac must be 'crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2738 | ) | |
| 2739 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2740 | assert( | |
| 2741 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2742 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2743 | ) | |
| 2744 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2745 | assert( | |
| 2746 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2747 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2748 | ) | |
| 2749 | ||
| 2750 | const res = binding.crypto_aead_chacha20poly1305_ietf_decrypt_detached(m, c, mac, ad, npub, k) | |
| 2751 | ||
| 2752 | if (res !== 0) throw new Error('could not verify data') | |
| 2753 | } | |
| 2754 | ||
| 2755 | // crypto_stream | |
| 2756 | ||
| 2757 | exports.crypto_stream_xor_wrap_init = function (state, n, k) { | |
| 2758 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2759 | assert( | |
| 2760 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2761 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2762 | ) | |
| 2763 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2764 | assert( | |
| 2765 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 2766 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 2767 | ) | |
| 2768 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2769 | assert( | |
| 2770 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 2771 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 2772 | ) | |
| 2773 | ||
| 2774 | binding.crypto_stream_xor_wrap_init(state, n, k) | |
| 2775 | } | |
| 2776 | ||
| 2777 | exports.crypto_stream_xor_wrap_update = function (state, c, m) { | |
| 2778 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2779 | assert( | |
| 2780 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2781 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2782 | ) | |
| 2783 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2784 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2785 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2786 | ||
| 2787 | binding.crypto_stream_xor_wrap_update(state, c, m) | |
| 2788 | } | |
| 2789 | ||
| 2790 | exports.crypto_stream_xor_wrap_final = function (state) { | |
| 2791 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2792 | assert( | |
| 2793 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2794 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2795 | ) | |
| 2796 | ||
| 2797 | binding.crypto_stream_xor_wrap_final(state) | |
| 2798 | } | |
| 2799 | ||
| 2800 | exports.crypto_stream_chacha20_xor_wrap_init = function (state, n, k) { | |
| 2801 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2802 | assert( | |
| 2803 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2804 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2805 | ) | |
| 2806 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2807 | assert( | |
| 2808 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 2809 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 2810 | ) | |
| 2811 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2812 | assert( | |
| 2813 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 2814 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 2815 | ) | |
| 2816 | ||
| 2817 | binding.crypto_stream_chacha20_xor_wrap_init(state, n, k) | |
| 2818 | } | |
| 2819 | ||
| 2820 | exports.crypto_stream_chacha20_xor_wrap_update = function (state, c, m) { | |
| 2821 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2822 | assert( | |
| 2823 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2824 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2825 | ) | |
| 2826 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2827 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2828 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2829 | ||
| 2830 | binding.crypto_stream_chacha20_xor_wrap_update(state, c, m) | |
| 2831 | } | |
| 2832 | ||
| 2833 | exports.crypto_stream_chacha20_xor_wrap_final = function (state) { | |
| 2834 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2835 | assert( | |
| 2836 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2837 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2838 | ) | |
| 2839 | ||
| 2840 | binding.crypto_stream_chacha20_xor_wrap_final(state) | |
| 2841 | } | |
| 2842 | ||
| 2843 | exports.crypto_stream_chacha20_ietf_xor_wrap_init = function (state, n, k) { | |
| 2844 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2845 | assert( | |
| 2846 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2847 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2848 | ) | |
| 2849 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2850 | assert( | |
| 2851 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 2852 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 2853 | ) | |
| 2854 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2855 | assert( | |
| 2856 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 2857 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 2858 | ) | |
| 2859 | ||
| 2860 | binding.crypto_stream_chacha20_ietf_xor_wrap_init(state, n, k) | |
| 2861 | } | |
| 2862 | ||
| 2863 | exports.crypto_stream_chacha20_ietf_xor_wrap_update = function (state, c, m) { | |
| 2864 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2865 | assert( | |
| 2866 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2867 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2868 | ) | |
| 2869 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2870 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2871 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2872 | ||
| 2873 | binding.crypto_stream_chacha20_ietf_xor_wrap_update(state, c, m) | |
| 2874 | } | |
| 2875 | ||
| 2876 | exports.crypto_stream_chacha20_ietf_xor_wrap_final = function (state) { | |
| 2877 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2878 | assert( | |
| 2879 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2880 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2881 | ) | |
| 2882 | ||
| 2883 | binding.crypto_stream_chacha20_ietf_xor_wrap_final(state) | |
| 2884 | } | |
| 2885 | ||
| 2886 | exports.crypto_stream_xchacha20_xor_wrap_init = function (state, n, k) { | |
| 2887 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2888 | assert( | |
| 2889 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2890 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2891 | ) | |
| 2892 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2893 | assert( | |
| 2894 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 2895 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 2896 | ) | |
| 2897 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2898 | assert( | |
| 2899 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 2900 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 2901 | ) | |
| 2902 | ||
| 2903 | binding.crypto_stream_xchacha20_xor_wrap_init(state, n, k) | |
| 2904 | } | |
| 2905 | ||
| 2906 | exports.crypto_stream_xchacha20_xor_wrap_update = function (state, c, m) { | |
| 2907 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2908 | assert( | |
| 2909 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2910 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2911 | ) | |
| 2912 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2913 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2914 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2915 | ||
| 2916 | binding.crypto_stream_xchacha20_xor_wrap_update(state, c, m) | |
| 2917 | } | |
| 2918 | ||
| 2919 | exports.crypto_stream_xchacha20_xor_wrap_final = function (state) { | |
| 2920 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2921 | assert( | |
| 2922 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2923 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2924 | ) | |
| 2925 | ||
| 2926 | binding.crypto_stream_xchacha20_xor_wrap_final(state) | |
| 2927 | } | |
| 2928 | ||
| 2929 | exports.crypto_stream_salsa20_xor_wrap_init = function (state, n, k) { | |
| 2930 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2931 | assert( | |
| 2932 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2933 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2934 | ) | |
| 2935 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2936 | assert( | |
| 2937 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 2938 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 2939 | ) | |
| 2940 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2941 | assert( | |
| 2942 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 2943 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 2944 | ) | |
| 2945 | ||
| 2946 | binding.crypto_stream_salsa20_xor_wrap_init(state, n, k) | |
| 2947 | } | |
| 2948 | ||
| 2949 | exports.crypto_stream_salsa20_xor_wrap_update = function (state, c, m) { | |
| 2950 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2951 | assert( | |
| 2952 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2953 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2954 | ) | |
| 2955 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2956 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2957 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2958 | ||
| 2959 | binding.crypto_stream_salsa20_xor_wrap_update(state, c, m) | |
| 2960 | } | |
| 2961 | ||
| 2962 | exports.crypto_stream_salsa20_xor_wrap_final = function (state) { | |
| 2963 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2964 | assert( | |
| 2965 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2966 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2967 | ) | |
| 2968 | ||
| 2969 | binding.crypto_stream_salsa20_xor_wrap_final(state) | |
| 2970 | } | |
| 2971 | ||
| 2972 | // experimental | |
| 2973 | ||
| 2974 | exports.extension_tweak_ed25519_base = function (n, p, ns) { | |
| 2975 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2976 | assert( | |
| 2977 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 2978 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 2979 | ) | |
| 2980 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2981 | assert( | |
| 2982 | p.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 2983 | "p must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 2984 | ) | |
| 2985 | ||
| 2986 | binding.extension_tweak_ed25519_base(n, p, ns) | |
| 2987 | } | |
| 2988 | ||
| 2989 | exports.extension_tweak_ed25519_sign_detached = function (sig, m, scalar, pk) { | |
| 2990 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 2991 | assert(sig.byteLength === binding.crypto_sign_BYTES, "sig must be 'crypto_sign_BYTES' bytes") | |
| 2992 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2993 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 2994 | assert( | |
| 2995 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 2996 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 2997 | ) | |
| 2998 | ||
| 2999 | if (pk) { | |
| 3000 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3001 | assert( | |
| 3002 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3003 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3004 | ) | |
| 3005 | } | |
| 3006 | ||
| 3007 | const res = binding.extension_tweak_ed25519_sign_detached(sig, m, scalar, pk) | |
| 3008 | ||
| 3009 | if (res !== 0) throw new Error('failed to compute signature') | |
| 3010 | } | |
| 3011 | ||
| 3012 | exports.extension_tweak_ed25519_sk_to_scalar = function (n, sk) { | |
| 3013 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 3014 | assert( | |
| 3015 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3016 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3017 | ) | |
| 3018 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 3019 | assert( | |
| 3020 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 3021 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 3022 | ) | |
| 3023 | ||
| 3024 | binding.extension_tweak_ed25519_sk_to_scalar(n, sk) | |
| 3025 | } | |
| 3026 | ||
| 3027 | exports.extension_tweak_ed25519_scalar = function (scalarOut, scalar, ns) { | |
| 3028 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3029 | assert( | |
| 3030 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3031 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3032 | ) | |
| 3033 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 3034 | assert( | |
| 3035 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3036 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3037 | ) | |
| 3038 | ||
| 3039 | binding.extension_tweak_ed25519_scalar(scalarOut, scalar, ns) | |
| 3040 | } | |
| 3041 | ||
| 3042 | exports.extension_tweak_ed25519_pk = function (tpk, pk, ns) { | |
| 3043 | assert(ArrayBuffer.isView(tpk), 'tpk must be a typed array') | |
| 3044 | assert( | |
| 3045 | tpk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3046 | "tpk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3047 | ) | |
| 3048 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3049 | assert( | |
| 3050 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3051 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3052 | ) | |
| 3053 | ||
| 3054 | const res = binding.extension_tweak_ed25519_pk(tpk, pk, ns) | |
| 3055 | ||
| 3056 | if (res !== 0) throw new Error('failed to tweak public key') | |
| 3057 | } | |
| 3058 | ||
| 3059 | exports.extension_tweak_ed25519_keypair = function (pk, scalarOut, scalarIn, ns) { | |
| 3060 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3061 | assert( | |
| 3062 | pk.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 3063 | "pk must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 3064 | ) | |
| 3065 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3066 | assert( | |
| 3067 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3068 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3069 | ) | |
| 3070 | assert(ArrayBuffer.isView(scalarIn), 'scalarIn must be a typed array') | |
| 3071 | assert( | |
| 3072 | scalarIn.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3073 | "scalarIn must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3074 | ) | |
| 3075 | ||
| 3076 | binding.extension_tweak_ed25519_keypair(pk, scalarOut, scalarIn, ns) | |
| 3077 | } | |
| 3078 | ||
| 3079 | exports.extension_tweak_ed25519_scalar_add = function (scalarOut, scalar, n) { | |
| 3080 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3081 | assert( | |
| 3082 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3083 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3084 | ) | |
| 3085 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 3086 | assert( | |
| 3087 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3088 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3089 | ) | |
| 3090 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 3091 | assert( | |
| 3092 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3093 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3094 | ) | |
| 3095 | ||
| 3096 | binding.extension_tweak_ed25519_scalar_add(scalarOut, scalar, n) | |
| 3097 | } | |
| 3098 | ||
| 3099 | exports.extension_tweak_ed25519_pk_add = function (tpk, pk, p) { | |
| 3100 | assert(ArrayBuffer.isView(tpk), 'tpk must be a typed array') | |
| 3101 | assert( | |
| 3102 | tpk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3103 | "tpk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3104 | ) | |
| 3105 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3106 | assert( | |
| 3107 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3108 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3109 | ) | |
| 3110 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 3111 | assert( | |
| 3112 | p.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3113 | "p must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3114 | ) | |
| 3115 | ||
| 3116 | const res = binding.extension_tweak_ed25519_pk_add(tpk, pk, p) | |
| 3117 | ||
| 3118 | if (res !== 0) throw new Error('failed to add tweak to public key') | |
| 3119 | } | |
| 3120 | ||
| 3121 | exports.extension_tweak_ed25519_keypair_add = function (pk, scalarOut, scalarIn, tweak) { | |
| 3122 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3123 | assert( | |
| 3124 | pk.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 3125 | "pk must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 3126 | ) | |
| 3127 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3128 | assert( | |
| 3129 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3130 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3131 | ) | |
| 3132 | assert(ArrayBuffer.isView(scalarIn), 'scalarIn must be a typed array') | |
| 3133 | assert( | |
| 3134 | scalarIn.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3135 | "scalarIn must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3136 | ) | |
| 3137 | assert(ArrayBuffer.isView(tweak), 'tweak must be a typed array') | |
| 3138 | assert( | |
| 3139 | tweak.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3140 | "tweak must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3141 | ) | |
| 3142 | ||
| 3143 | const res = binding.extension_tweak_ed25519_keypair_add(pk, scalarOut, scalarIn, tweak) | |
| 3144 | ||
| 3145 | if (res !== 0) throw new Error('failed to add tweak to keypair') | |
| 3146 | } | |
| 3147 | ||
| 3148 | exports.extension_pbkdf2_sha512_async = function (out, passwd, salt, iter, outlen, callback) { | |
| 3149 | assert( | |
| 3150 | iter >= binding.extension_pbkdf2_sha512_ITERATIONS_MIN, | |
| 3151 | "iter must be at least 'extension_pbkdf2_sha512_ITERATIONS_MIN'" | |
| 3152 | ) | |
| 3153 | assert( | |
| 3154 | outlen <= binding.extension_pbkdf2_sha512_BYTES_MAX, | |
| 3155 | "outlen must be at most 'extension_pbkdf2_sha512_BYTES_MAX'" | |
| 3156 | ) | |
| 3157 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 3158 | assert(out.byteLength >= outlen, "out must be at least 'outlen' bytes") | |
| 3159 | assert(out.byteLength > 0, 'out must not be empty') | |
| 3160 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 3161 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 3162 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 3163 | assert(salt.byteLength > 0, 'salt must not be empty') | |
| 3164 | ||
| 3165 | const [done, promise] = checkStatus(callback) | |
| 3166 | ||
| 3167 | binding.extension_pbkdf2_sha512_async( | |
| 3168 | out.buffer, | |
| 3169 | out.byteOffset, | |
| 3170 | out.byteLength, | |
| 3171 | ||
| 3172 | passwd.buffer, | |
| 3173 | passwd.byteOffset, | |
| 3174 | passwd.byteLength, | |
| 3175 | ||
| 3176 | salt.buffer, | |
| 3177 | salt.byteOffset, | |
| 3178 | salt.byteLength, | |
| 3179 | ||
| 3180 | iter, | |
| 3181 | outlen, | |
| 3182 | ||
| 3183 | done | |
| 3184 | ) | |
| 3185 | ||
| 3186 | return promise | |
| 3187 | } | |
| 3188 | ||
| 3189 | exports.extension_pbkdf2_sha512 = function (out, passwd, salt, iter, outlen) { | |
| 3190 | assert( | |
| 3191 | iter >= binding.extension_pbkdf2_sha512_ITERATIONS_MIN, | |
| 3192 | "iter must be at least 'extension_pbkdf2_sha512_ITERATIONS_MIN'" | |
| 3193 | ) | |
| 3194 | assert( | |
| 3195 | outlen <= binding.extension_pbkdf2_sha512_BYTES_MAX, | |
| 3196 | "outlen must be at most 'extension_pbkdf2_sha512_BYTES_MAX'" | |
| 3197 | ) | |
| 3198 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 3199 | assert(out.byteLength >= outlen, "out must be at least 'outlen' bytes") | |
| 3200 | assert(out.byteLength > 0, 'out must not be empty') | |
| 3201 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 3202 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 3203 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 3204 | assert(salt.byteLength > 0, 'salt must not be empty') | |
| 3205 | ||
| 3206 | const res = binding.extension_pbkdf2_sha512(out, passwd, salt, iter, outlen) | |
| 3207 | ||
| 3208 | if (res !== 0) throw new Error('failed to add tweak to public key') | |
| 3209 | } | |
| 3210 | ||
| 3211 | function checkStatus(callback, booleanResult = false) { | |
| 3212 | let done, promise | |
| 3213 | ||
| 3214 | if (typeof callback === 'function') { | |
| 3215 | done = function (status) { | |
| 3216 | if (booleanResult) callback(null, status === 0) | |
| 3217 | else if (status === 0) callback(null) | |
| 3218 | else callback(new Error('status: ' + status)) | |
| 3219 | } | |
| 3220 | } else { | |
| 3221 | promise = new Promise(function (resolve, reject) { | |
| 3222 | done = function (status) { | |
| 3223 | if (booleanResult) resolve(status === 0) | |
| 3224 | else if (status === 0) resolve() | |
| 3225 | else reject(new Error('status: ' + status)) | |
| 3226 | } | |
| 3227 | }) | |
| 3228 | } | |
| 3229 | ||
| 3230 | return [done, promise] | |
| 3231 | } |
| … | … | @@ -0,0 +1,62 @@ |
| 1 | { | |
| 2 | "name": "sodium-native", | |
| 3 | "version": "5.1.0", | |
| 4 | "description": "Low level bindings for libsodium", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js", | |
| 8 | "binding.cc", | |
| 9 | "binding.js", | |
| 10 | "extensions", | |
| 11 | "prebuilds", | |
| 12 | "CMakeLists.txt" | |
| 13 | ], | |
| 14 | "imports": { | |
| 15 | "assert": { | |
| 16 | "bare": "bare-assert", | |
| 17 | "node": "assert" | |
| 18 | } | |
| 19 | }, | |
| 20 | "addon": true, | |
| 21 | "dependencies": { | |
| 22 | "bare-assert": "^1.2.0", | |
| 23 | "require-addon": "^1.1.0", | |
| 24 | "which-runtime": "^1.2.1" | |
| 25 | }, | |
| 26 | "devDependencies": { | |
| 27 | "bare-compat-napi": "^1.3.5", | |
| 28 | "brittle": "^3.16.2", | |
| 29 | "cmake-bare": "^1.6.1", | |
| 30 | "cmake-fetch": "^1.4.3", | |
| 31 | "cmake-napi": "^1.2.1", | |
| 32 | "prettier": "^3.6.2", | |
| 33 | "prettier-config-holepunch": "^2.0.0" | |
| 34 | }, | |
| 35 | "scripts": { | |
| 36 | "test": "prettier . --check && npm run test:node && npm run test:bare", | |
| 37 | "test:node": "node test/all.js", | |
| 38 | "test:bare": "bare test/all.js" | |
| 39 | }, | |
| 40 | "standard": { | |
| 41 | "ignore": [ | |
| 42 | "/test/fixtures/*.js" | |
| 43 | ] | |
| 44 | }, | |
| 45 | "engines": { | |
| 46 | "bare": ">=1.16.0" | |
| 47 | }, | |
| 48 | "repository": { | |
| 49 | "type": "git", | |
| 50 | "url": "git+https://github.com/holepunchto/sodium-native.git" | |
| 51 | }, | |
| 52 | "contributors": [ | |
| 53 | "Emil Bay <github@tixz.dk> (http://bayes.dk)", | |
| 54 | "Mathias Buus <mathiasbuus@gmail.com> (https://mafinto.sh)", | |
| 55 | "Christophe Diederichs <chm-diederichs@hyperdivision.dk>" | |
| 56 | ], | |
| 57 | "license": "MIT", | |
| 58 | "bugs": { | |
| 59 | "url": "https://github.com/holepunchto/sodium-native/issues" | |
| 60 | }, | |
| 61 | "homepage": "https://github.com/holepunchto/sodium-native" | |
| 62 | } |
| … | … | @@ -0,0 +1,7 @@ |
| 1 | Copyright 2017 Mathias Buus & Emil Bay | |
| 2 | ||
| 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
| 4 | ||
| 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
| 6 | ||
| 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| … | … | @@ -0,0 +1,288 @@ |
| 1 | # sodium-universal | |
| 2 | ||
| 3 | Universal wrapper for `sodium-javascript` and `sodium-native` working in Node.js and the Browser | |
| 4 | ||
| 5 | ```sh | |
| 6 | npm install sodium-universal | |
| 7 | ``` | |
| 8 | ||
| 9 | `sodium-native` ships with prebuilds for macOS, Windows, and Linux. | |
| 10 | ||
| 11 | Note that the module itself is generated on a prepublish hook. | |
| 12 | ||
| 13 | ## Usage | |
| 14 | ||
| 15 | ```js | |
| 16 | var sodium = require('sodium-universal') | |
| 17 | ||
| 18 | var rnd = Buffer.allocUnsafe(12) // Cryptographically random data | |
| 19 | sodium.randombytes_buf(rnd) | |
| 20 | ||
| 21 | console.log(rnd.toString('hex')) | |
| 22 | ||
| 23 | ``` | |
| 24 | ||
| 25 | Works seamlessly with Node.js: | |
| 26 | ||
| 27 | ```sh | |
| 28 | $ node example.js | |
| 29 | c7dbd46a6cc84ff2e0d1285c | |
| 30 | ``` | |
| 31 | ||
| 32 | And the browser: | |
| 33 | ||
| 34 | First install the peer dep, `sodium-javascript`. | |
| 35 | ||
| 36 | ```sh | |
| 37 | npm install --save sodium-javascript | |
| 38 | ``` | |
| 39 | ||
| 40 | And then browserify | |
| 41 | ||
| 42 | ```sh | |
| 43 | browserify example.js > bundle.js | |
| 44 | ``` | |
| 45 | ||
| 46 | ## Introduction | |
| 47 | ||
| 48 | [`libsodium`](https://download.libsodium.org/doc/) is a collection of cryptographic primitives, providing a low-level foundation to build higher-level cryptographic applications and protocols. It is often put in contrast to RSA based cryptography and OpenSSL, even though they all share overlapping algorithms and target slightly different audiences. Sodium is a collection of modern collection of primitives, fulfilling the same cryptographic tasks, but based on simpler and more efficient algorithms. | |
| 49 | ||
| 50 | This library provides seamless bindings to [`sodium-native`](https://github.com/sodium-friends/sodium-native), which is the original C implementation of `libsodium` exposed as a Node native module. For the browser we expose [`sodium-javascript`](https://github.com/sodium-friends/sodium-javascript), using the `package.json` [`browser`](https://github.com/defunctzombie/package-browser-field-spec) field, which is supported by most bundlers. | |
| 51 | ||
| 52 | ## API | |
| 53 | ||
| 54 | Please refer to [`sodium-native`](https://github.com/sodium-friends/sodium-native#api) and [`sodium-javascript`](https://github.com/sodium-friends/sodium-javascript#api). Note that the two modules do not yet have feature parity, where `sodium-native` is the more featureful of the two. | |
| 55 | ||
| 56 | ## Compatibilty | |
| 57 | ||
| 58 | <!-- Generated by build-scripts/symbols.js with | |
| 59 | sodium-native@3.2.0 | |
| 60 | sodium-javascript@0.6.0 --> | |
| 61 | | C Library Symbol | `sodium-native` | `sodium-javascript` | | |
| 62 | |:--------------------------------------------------------:|:--------------------:|:--------------------:| | |
| 63 | | `crypto_aead_chacha20poly1305_ietf_ABYTES` | :white_check_mark: | :white_check_mark: | | |
| 64 | | `crypto_aead_chacha20poly1305_ietf_decrypt` | :white_check_mark: | :white_check_mark: | | |
| 65 | | `crypto_aead_chacha20poly1305_ietf_decrypt_detached` | :white_check_mark: | :white_check_mark: | | |
| 66 | | `crypto_aead_chacha20poly1305_ietf_encrypt` | :white_check_mark: | :white_check_mark: | | |
| 67 | | `crypto_aead_chacha20poly1305_ietf_encrypt_detached` | :white_check_mark: | :white_check_mark: | | |
| 68 | | `crypto_aead_chacha20poly1305_ietf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 69 | | `crypto_aead_chacha20poly1305_ietf_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 70 | | `crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 71 | | `crypto_aead_chacha20poly1305_ietf_NPUBBYTES` | :white_check_mark: | :white_check_mark: | | |
| 72 | | `crypto_aead_chacha20poly1305_ietf_NSECBYTES` | :white_check_mark: | :white_check_mark: | | |
| 73 | | `crypto_aead_xchacha20poly1305_ietf_ABYTES` | :white_check_mark: | :small_red_triangle: | | |
| 74 | | `crypto_aead_xchacha20poly1305_ietf_decrypt` | :white_check_mark: | :small_red_triangle: | | |
| 75 | | `crypto_aead_xchacha20poly1305_ietf_decrypt_detached` | :white_check_mark: | :small_red_triangle: | | |
| 76 | | `crypto_aead_xchacha20poly1305_ietf_encrypt` | :white_check_mark: | :small_red_triangle: | | |
| 77 | | `crypto_aead_xchacha20poly1305_ietf_encrypt_detached` | :white_check_mark: | :small_red_triangle: | | |
| 78 | | `crypto_aead_xchacha20poly1305_ietf_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 79 | | `crypto_aead_xchacha20poly1305_ietf_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 80 | | `crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 81 | | `crypto_aead_xchacha20poly1305_ietf_NPUBBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 82 | | `crypto_aead_xchacha20poly1305_ietf_NSECBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 83 | | `crypto_auth` | :white_check_mark: | :small_red_triangle: | | |
| 84 | | `crypto_auth_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 85 | | `crypto_auth_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 86 | | `crypto_auth_verify` | :white_check_mark: | :small_red_triangle: | | |
| 87 | | `crypto_box_BEFORENMBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 88 | | `crypto_box_BOXZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 89 | | `crypto_box_detached` | :white_check_mark: | :small_red_triangle: | | |
| 90 | | `crypto_box_easy` | :white_check_mark: | :small_red_triangle: | | |
| 91 | | `crypto_box_keypair` | :white_check_mark: | :white_check_mark: | | |
| 92 | | `crypto_box_MACBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 93 | | `crypto_box_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 94 | | `crypto_box_open_detached` | :white_check_mark: | :small_red_triangle: | | |
| 95 | | `crypto_box_open_easy` | :white_check_mark: | :small_red_triangle: | | |
| 96 | | `crypto_box_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 97 | | `crypto_box_seal` | :white_check_mark: | :white_check_mark: | | |
| 98 | | `crypto_box_seal_open` | :white_check_mark: | :white_check_mark: | | |
| 99 | | `crypto_box_SEALBYTES` | :white_check_mark: | :white_check_mark: | | |
| 100 | | `crypto_box_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 101 | | `crypto_box_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 102 | | `crypto_box_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 103 | | `crypto_box_ZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 104 | | `crypto_core_ed25519_add` | :white_check_mark: | :small_red_triangle: | | |
| 105 | | `crypto_core_ed25519_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 106 | | `crypto_core_ed25519_from_uniform` | :white_check_mark: | :small_red_triangle: | | |
| 107 | | `crypto_core_ed25519_is_valid_point` | :white_check_mark: | :small_red_triangle: | | |
| 108 | | `crypto_core_ed25519_NONREDUCEDSCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 109 | | `crypto_core_ed25519_scalar_add` | :white_check_mark: | :small_red_triangle: | | |
| 110 | | `crypto_core_ed25519_scalar_complement` | :white_check_mark: | :small_red_triangle: | | |
| 111 | | `crypto_core_ed25519_scalar_invert` | :white_check_mark: | :small_red_triangle: | | |
| 112 | | `crypto_core_ed25519_scalar_negate` | :white_check_mark: | :small_red_triangle: | | |
| 113 | | `crypto_core_ed25519_scalar_random` | :white_check_mark: | :small_red_triangle: | | |
| 114 | | `crypto_core_ed25519_scalar_reduce` | :white_check_mark: | :small_red_triangle: | | |
| 115 | | `crypto_core_ed25519_scalar_sub` | :white_check_mark: | :small_red_triangle: | | |
| 116 | | `crypto_core_ed25519_SCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 117 | | `crypto_core_ed25519_sub` | :white_check_mark: | :small_red_triangle: | | |
| 118 | | `crypto_core_ed25519_UNIFORMBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 119 | | `crypto_generichash` | :white_check_mark: | :white_check_mark: | | |
| 120 | | `crypto_generichash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 121 | | `crypto_generichash_BYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 122 | | `crypto_generichash_BYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 123 | | `crypto_generichash_final` | :white_check_mark: | :small_red_triangle: | | |
| 124 | | `crypto_generichash_init` | :white_check_mark: | :small_red_triangle: | | |
| 125 | | `crypto_generichash_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 126 | | `crypto_generichash_KEYBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 127 | | `crypto_generichash_KEYBYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 128 | | `crypto_generichash_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 129 | | `crypto_generichash_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 130 | | `crypto_generichash_update` | :white_check_mark: | :small_red_triangle: | | |
| 131 | | `crypto_hash` | :white_check_mark: | :white_check_mark: | | |
| 132 | | `crypto_hash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 133 | | `crypto_hash_sha256` | :white_check_mark: | :white_check_mark: | | |
| 134 | | `crypto_hash_sha256_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 135 | | `crypto_hash_sha256_final` | :white_check_mark: | :small_red_triangle: | | |
| 136 | | `crypto_hash_sha256_init` | :white_check_mark: | :small_red_triangle: | | |
| 137 | | `crypto_hash_sha256_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 138 | | `crypto_hash_sha256_update` | :white_check_mark: | :small_red_triangle: | | |
| 139 | | `crypto_hash_sha512` | :white_check_mark: | :white_check_mark: | | |
| 140 | | `crypto_hash_sha512_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 141 | | `crypto_hash_sha512_final` | :white_check_mark: | :small_red_triangle: | | |
| 142 | | `crypto_hash_sha512_init` | :white_check_mark: | :small_red_triangle: | | |
| 143 | | `crypto_hash_sha512_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 144 | | `crypto_hash_sha512_update` | :white_check_mark: | :small_red_triangle: | | |
| 145 | | `crypto_kdf_BYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 146 | | `crypto_kdf_BYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 147 | | `crypto_kdf_CONTEXTBYTES` | :white_check_mark: | :white_check_mark: | | |
| 148 | | `crypto_kdf_derive_from_key` | :white_check_mark: | :white_check_mark: | | |
| 149 | | `crypto_kdf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 150 | | `crypto_kdf_keygen` | :white_check_mark: | :white_check_mark: | | |
| 151 | | `crypto_kx_client_session_keys` | :white_check_mark: | :small_red_triangle: | | |
| 152 | | `crypto_kx_keypair` | :white_check_mark: | :white_check_mark: | | |
| 153 | | `crypto_kx_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 154 | | `crypto_kx_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 155 | | `crypto_kx_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 156 | | `crypto_kx_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 157 | | `crypto_kx_server_session_keys` | :white_check_mark: | :small_red_triangle: | | |
| 158 | | `crypto_kx_SESSIONKEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 159 | | `crypto_onetimeauth` | :white_check_mark: | :white_check_mark: | | |
| 160 | | `crypto_onetimeauth_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 161 | | `crypto_onetimeauth_final` | :white_check_mark: | :small_red_triangle: | | |
| 162 | | `crypto_onetimeauth_init` | :white_check_mark: | :small_red_triangle: | | |
| 163 | | `crypto_onetimeauth_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 164 | | `crypto_onetimeauth_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 165 | | `crypto_onetimeauth_update` | :white_check_mark: | :small_red_triangle: | | |
| 166 | | `crypto_onetimeauth_verify` | :white_check_mark: | :white_check_mark: | | |
| 167 | | `crypto_pwhash` | :white_check_mark: | :small_red_triangle: | | |
| 168 | | `crypto_pwhash_BYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 169 | | `crypto_pwhash_BYTES_MIN` | :white_check_mark: | :small_red_triangle: | | |
| 170 | | `crypto_pwhash_SALTBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 171 | | `crypto_pwhash_scryptsalsa208sha256` | :white_check_mark: | :small_red_triangle: | | |
| 172 | | `crypto_pwhash_scryptsalsa208sha256_BYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 173 | | `crypto_pwhash_scryptsalsa208sha256_BYTES_MIN` | :white_check_mark: | :small_red_triangle: | | |
| 174 | | `crypto_pwhash_scryptsalsa208sha256_SALTBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 175 | | `crypto_pwhash_scryptsalsa208sha256_str` | :white_check_mark: | :small_red_triangle: | | |
| 176 | | `crypto_pwhash_scryptsalsa208sha256_str_needs_rehash` | :white_check_mark: | :small_red_triangle: | | |
| 177 | | `crypto_pwhash_scryptsalsa208sha256_str_verify` | :white_check_mark: | :small_red_triangle: | | |
| 178 | | `crypto_pwhash_scryptsalsa208sha256_STRBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 179 | | `crypto_pwhash_str` | :white_check_mark: | :small_red_triangle: | | |
| 180 | | `crypto_pwhash_str_needs_rehash` | :white_check_mark: | :small_red_triangle: | | |
| 181 | | `crypto_pwhash_str_verify` | :white_check_mark: | :small_red_triangle: | | |
| 182 | | `crypto_pwhash_STRBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 183 | | `crypto_scalarmult` | :white_check_mark: | :white_check_mark: | | |
| 184 | | `crypto_scalarmult_base` | :white_check_mark: | :white_check_mark: | | |
| 185 | | `crypto_scalarmult_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 186 | | `crypto_scalarmult_ed25519` | :white_check_mark: | :small_red_triangle: | | |
| 187 | | `crypto_scalarmult_ed25519_base` | :white_check_mark: | :small_red_triangle: | | |
| 188 | | `crypto_scalarmult_ed25519_base_noclamp` | :white_check_mark: | :small_red_triangle: | | |
| 189 | | `crypto_scalarmult_ed25519_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 190 | | `crypto_scalarmult_ed25519_noclamp` | :white_check_mark: | :small_red_triangle: | | |
| 191 | | `crypto_scalarmult_ed25519_SCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 192 | | `crypto_scalarmult_SCALARBYTES` | :white_check_mark: | :white_check_mark: | | |
| 193 | | `crypto_secretbox` | :small_red_triangle: | :white_check_mark: | | |
| 194 | | `crypto_secretbox_BOXZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 195 | | `crypto_secretbox_detached` | :white_check_mark: | :white_check_mark: | | |
| 196 | | `crypto_secretbox_easy` | :white_check_mark: | :white_check_mark: | | |
| 197 | | `crypto_secretbox_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 198 | | `crypto_secretbox_MACBYTES` | :white_check_mark: | :white_check_mark: | | |
| 199 | | `crypto_secretbox_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 200 | | `crypto_secretbox_open` | :small_red_triangle: | :white_check_mark: | | |
| 201 | | `crypto_secretbox_open_detached` | :white_check_mark: | :white_check_mark: | | |
| 202 | | `crypto_secretbox_open_easy` | :white_check_mark: | :white_check_mark: | | |
| 203 | | `crypto_secretbox_ZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 204 | | `crypto_secretstream_xchacha20poly1305_ABYTES` | :white_check_mark: | :small_red_triangle: | | |
| 205 | | `crypto_secretstream_xchacha20poly1305_HEADERBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 206 | | `crypto_secretstream_xchacha20poly1305_init_pull` | :white_check_mark: | :small_red_triangle: | | |
| 207 | | `crypto_secretstream_xchacha20poly1305_init_push` | :white_check_mark: | :small_red_triangle: | | |
| 208 | | `crypto_secretstream_xchacha20poly1305_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 209 | | `crypto_secretstream_xchacha20poly1305_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 210 | | `crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 211 | | `crypto_secretstream_xchacha20poly1305_pull` | :white_check_mark: | :small_red_triangle: | | |
| 212 | | `crypto_secretstream_xchacha20poly1305_push` | :white_check_mark: | :small_red_triangle: | | |
| 213 | | `crypto_secretstream_xchacha20poly1305_rekey` | :white_check_mark: | :small_red_triangle: | | |
| 214 | | `crypto_secretstream_xchacha20poly1305_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 215 | | `crypto_shorthash` | :white_check_mark: | :white_check_mark: | | |
| 216 | | `crypto_shorthash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 217 | | `crypto_shorthash_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 218 | | `crypto_sign` | :white_check_mark: | :white_check_mark: | | |
| 219 | | `crypto_sign_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 220 | | `crypto_sign_detached` | :white_check_mark: | :white_check_mark: | | |
| 221 | | `crypto_sign_ed25519_pk_to_curve25519` | :white_check_mark: | :small_red_triangle: | | |
| 222 | | `crypto_sign_ed25519_sk_to_curve25519` | :white_check_mark: | :small_red_triangle: | | |
| 223 | | `crypto_sign_ed25519_sk_to_pk` | :white_check_mark: | :small_red_triangle: | | |
| 224 | | `crypto_sign_keypair` | :white_check_mark: | :white_check_mark: | | |
| 225 | | `crypto_sign_open` | :white_check_mark: | :white_check_mark: | | |
| 226 | | `crypto_sign_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 227 | | `crypto_sign_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 228 | | `crypto_sign_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 229 | | `crypto_sign_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 230 | | `crypto_sign_verify_detached` | :white_check_mark: | :white_check_mark: | | |
| 231 | | `crypto_stream` | :white_check_mark: | :white_check_mark: | | |
| 232 | | `crypto_stream_chacha20` | :white_check_mark: | :white_check_mark: | | |
| 233 | | `crypto_stream_chacha20_ietf` | :white_check_mark: | :white_check_mark: | | |
| 234 | | `crypto_stream_chacha20_ietf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 235 | | `crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 236 | | `crypto_stream_chacha20_ietf_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 237 | | `crypto_stream_chacha20_ietf_xor` | :white_check_mark: | :white_check_mark: | | |
| 238 | | `crypto_stream_chacha20_ietf_xor_ic` | :white_check_mark: | :white_check_mark: | | |
| 239 | | `crypto_stream_chacha20_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 240 | | `crypto_stream_chacha20_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 241 | | `crypto_stream_chacha20_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 242 | | `crypto_stream_chacha20_xor` | :white_check_mark: | :white_check_mark: | | |
| 243 | | `crypto_stream_chacha20_xor_ic` | :white_check_mark: | :white_check_mark: | | |
| 244 | | `crypto_stream_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 245 | | `crypto_stream_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 246 | | `crypto_stream_salsa20_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 247 | | `crypto_stream_salsa20_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 248 | | `crypto_stream_salsa20_NONCEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 249 | | `crypto_stream_salsa20_xor` | :white_check_mark: | :small_red_triangle: | | |
| 250 | | `crypto_stream_salsa20_xor_ic` | :white_check_mark: | :small_red_triangle: | | |
| 251 | | `crypto_stream_xchacha20` | :white_check_mark: | :small_red_triangle: | | |
| 252 | | `crypto_stream_xchacha20_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 253 | | `crypto_stream_xchacha20_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 254 | | `crypto_stream_xchacha20_NONCEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 255 | | `crypto_stream_xchacha20_xor` | :white_check_mark: | :small_red_triangle: | | |
| 256 | | `crypto_stream_xchacha20_xor_ic` | :white_check_mark: | :small_red_triangle: | | |
| 257 | | `crypto_stream_xor` | :white_check_mark: | :white_check_mark: | | |
| 258 | | `crypto_verify_16` | :small_red_triangle: | :white_check_mark: | | |
| 259 | | `crypto_verify_32` | :small_red_triangle: | :white_check_mark: | | |
| 260 | | `randombytes_buf` | :white_check_mark: | :white_check_mark: | | |
| 261 | | `randombytes_buf_deterministic` | :white_check_mark: | :small_red_triangle: | | |
| 262 | | `randombytes_random` | :white_check_mark: | :small_red_triangle: | | |
| 263 | | `randombytes_SEEDBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 264 | | `randombytes_uniform` | :white_check_mark: | :small_red_triangle: | | |
| 265 | | `sodium_add` | :white_check_mark: | :small_red_triangle: | | |
| 266 | | `sodium_compare` | :white_check_mark: | :small_red_triangle: | | |
| 267 | | `sodium_free` | :white_check_mark: | :small_red_triangle: | | |
| 268 | | `sodium_increment` | :white_check_mark: | :small_red_triangle: | | |
| 269 | | `sodium_is_zero` | :white_check_mark: | :white_check_mark: | | |
| 270 | | `sodium_malloc` | :white_check_mark: | :white_check_mark: | | |
| 271 | | `sodium_memcmp` | :white_check_mark: | :white_check_mark: | | |
| 272 | | `sodium_memzero` | :white_check_mark: | :white_check_mark: | | |
| 273 | | `sodium_mlock` | :white_check_mark: | :small_red_triangle: | | |
| 274 | | `sodium_mprotect_noaccess` | :white_check_mark: | :small_red_triangle: | | |
| 275 | | `sodium_mprotect_readonly` | :white_check_mark: | :small_red_triangle: | | |
| 276 | | `sodium_mprotect_readwrite` | :white_check_mark: | :small_red_triangle: | | |
| 277 | | `sodium_munlock` | :white_check_mark: | :small_red_triangle: | | |
| 278 | | `sodium_pad` | :white_check_mark: | :small_red_triangle: | | |
| 279 | | `sodium_sub` | :white_check_mark: | :small_red_triangle: | | |
| 280 | | `sodium_unpad` | :white_check_mark: | :small_red_triangle: | | |
| 281 | ||
| 282 | ### Missing altogether (Ctrl + F friendly) | |
| 283 | ||
| 284 | `crypto_aead_aes256gcm_ABYTES`, `crypto_aead_aes256gcm_beforenm`, `crypto_aead_aes256gcm_decrypt`, `crypto_aead_aes256gcm_decrypt_afternm`, `crypto_aead_aes256gcm_decrypt_detached`, `crypto_aead_aes256gcm_decrypt_detached_afternm`, `crypto_aead_aes256gcm_encrypt`, `crypto_aead_aes256gcm_encrypt_afternm`, `crypto_aead_aes256gcm_encrypt_detached`, `crypto_aead_aes256gcm_encrypt_detached_afternm`, `crypto_aead_aes256gcm_is_available`, `crypto_aead_aes256gcm_KEYBYTES`, `crypto_aead_aes256gcm_keygen`, `crypto_aead_aes256gcm_MESSAGEBYTES_MAX`, `crypto_aead_aes256gcm_NPUBBYTES`, `crypto_aead_aes256gcm_NSECBYTES`, `crypto_aead_aes256gcm_STATEBYTES`, `crypto_aead_chacha20poly1305_ABYTES`, `crypto_aead_chacha20poly1305_decrypt`, `crypto_aead_chacha20poly1305_decrypt_detached`, `crypto_aead_chacha20poly1305_encrypt`, `crypto_aead_chacha20poly1305_encrypt_detached`, `crypto_aead_chacha20poly1305_KEYBYTES`, `crypto_aead_chacha20poly1305_keygen`, `crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX`, `crypto_aead_chacha20poly1305_NPUBBYTES`, `crypto_aead_chacha20poly1305_NSECBYTES`, `crypto_auth_hmacsha256`, `crypto_auth_hmacsha256_BYTES`, `crypto_auth_hmacsha256_final`, `crypto_auth_hmacsha256_init`, `crypto_auth_hmacsha256_KEYBYTES`, `crypto_auth_hmacsha256_keygen`, `crypto_auth_hmacsha256_STATEBYTES`, `crypto_auth_hmacsha256_update`, `crypto_auth_hmacsha256_verify`, `crypto_auth_hmacsha512`, `crypto_auth_hmacsha512256`, `crypto_auth_hmacsha512256_BYTES`, `crypto_auth_hmacsha512256_final`, `crypto_auth_hmacsha512256_init`, `crypto_auth_hmacsha512256_KEYBYTES`, `crypto_auth_hmacsha512256_keygen`, `crypto_auth_hmacsha512256_STATEBYTES`, `crypto_auth_hmacsha512256_update`, `crypto_auth_hmacsha512256_verify`, `crypto_auth_hmacsha512_BYTES`, `crypto_auth_hmacsha512_final`, `crypto_auth_hmacsha512_init`, `crypto_auth_hmacsha512_KEYBYTES`, `crypto_auth_hmacsha512_keygen`, `crypto_auth_hmacsha512_STATEBYTES`, `crypto_auth_hmacsha512_update`, `crypto_auth_hmacsha512_verify`, `crypto_auth_keygen`, `crypto_auth_primitive`, `crypto_box`, `crypto_box_afternm`, `crypto_box_beforenm`, `crypto_box_curve25519xchacha20poly1305_beforenm`, `crypto_box_curve25519xchacha20poly1305_BEFORENMBYTES`, `crypto_box_curve25519xchacha20poly1305_detached`, `crypto_box_curve25519xchacha20poly1305_detached_afternm`, `crypto_box_curve25519xchacha20poly1305_easy`, `crypto_box_curve25519xchacha20poly1305_easy_afternm`, `crypto_box_curve25519xchacha20poly1305_keypair`, `crypto_box_curve25519xchacha20poly1305_MACBYTES`, `crypto_box_curve25519xchacha20poly1305_MESSAGEBYTES_MAX`, `crypto_box_curve25519xchacha20poly1305_NONCEBYTES`, `crypto_box_curve25519xchacha20poly1305_open_detached`, `crypto_box_curve25519xchacha20poly1305_open_detached_afternm`, `crypto_box_curve25519xchacha20poly1305_open_easy`, `crypto_box_curve25519xchacha20poly1305_open_easy_afternm`, `crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES`, `crypto_box_curve25519xchacha20poly1305_seal`, `crypto_box_curve25519xchacha20poly1305_seal_open`, `crypto_box_curve25519xchacha20poly1305_SEALBYTES`, `crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES`, `crypto_box_curve25519xchacha20poly1305_seed_keypair`, `crypto_box_curve25519xchacha20poly1305_SEEDBYTES`, `crypto_box_curve25519xsalsa20poly1305`, `crypto_box_curve25519xsalsa20poly1305_afternm`, `crypto_box_curve25519xsalsa20poly1305_beforenm`, `crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES`, `crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES`, `crypto_box_curve25519xsalsa20poly1305_keypair`, `crypto_box_curve25519xsalsa20poly1305_MACBYTES`, `crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX`, `crypto_box_curve25519xsalsa20poly1305_NONCEBYTES`, `crypto_box_curve25519xsalsa20poly1305_open`, `crypto_box_curve25519xsalsa20poly1305_open_afternm`, `crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES`, `crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES`, `crypto_box_curve25519xsalsa20poly1305_seed_keypair`, `crypto_box_curve25519xsalsa20poly1305_SEEDBYTES`, `crypto_box_curve25519xsalsa20poly1305_ZEROBYTES`, `crypto_box_detached_afternm`, `crypto_box_easy_afternm`, `crypto_box_MESSAGEBYTES_MAX`, `crypto_box_open`, `crypto_box_open_afternm`, `crypto_box_open_detached_afternm`, `crypto_box_open_easy_afternm`, `crypto_box_primitive`, `crypto_core_ed25519_from_hash`, `crypto_core_ed25519_HASHBYTES`, `crypto_core_ed25519_random`, `crypto_core_ed25519_scalar_mul`, `crypto_core_hchacha20`, `crypto_core_hchacha20_CONSTBYTES`, `crypto_core_hchacha20_INPUTBYTES`, `crypto_core_hchacha20_KEYBYTES`, `crypto_core_hchacha20_OUTPUTBYTES`, `crypto_core_hsalsa20`, `crypto_core_hsalsa20_CONSTBYTES`, `crypto_core_hsalsa20_INPUTBYTES`, `crypto_core_hsalsa20_KEYBYTES`, `crypto_core_hsalsa20_OUTPUTBYTES`, `crypto_core_ristretto255_add`, `crypto_core_ristretto255_BYTES`, `crypto_core_ristretto255_from_hash`, `crypto_core_ristretto255_HASHBYTES`, `crypto_core_ristretto255_is_valid_point`, `crypto_core_ristretto255_NONREDUCEDSCALARBYTES`, `crypto_core_ristretto255_random`, `crypto_core_ristretto255_scalar_add`, `crypto_core_ristretto255_scalar_complement`, `crypto_core_ristretto255_scalar_invert`, `crypto_core_ristretto255_scalar_mul`, `crypto_core_ristretto255_scalar_negate`, `crypto_core_ristretto255_scalar_random`, `crypto_core_ristretto255_scalar_reduce`, `crypto_core_ristretto255_scalar_sub`, `crypto_core_ristretto255_SCALARBYTES`, `crypto_core_ristretto255_sub`, `crypto_core_salsa20`, `crypto_core_salsa2012`, `crypto_core_salsa2012_CONSTBYTES`, `crypto_core_salsa2012_INPUTBYTES`, `crypto_core_salsa2012_KEYBYTES`, `crypto_core_salsa2012_OUTPUTBYTES`, `crypto_core_salsa208`, `crypto_core_salsa208_CONSTBYTES`, `crypto_core_salsa208_INPUTBYTES`, `crypto_core_salsa208_KEYBYTES`, `crypto_core_salsa208_OUTPUTBYTES`, `crypto_core_salsa20_CONSTBYTES`, `crypto_core_salsa20_INPUTBYTES`, `crypto_core_salsa20_KEYBYTES`, `crypto_core_salsa20_OUTPUTBYTES`, `crypto_generichash_blake2b`, `crypto_generichash_blake2b_BYTES`, `crypto_generichash_blake2b_BYTES_MAX`, `crypto_generichash_blake2b_BYTES_MIN`, `crypto_generichash_blake2b_final`, `crypto_generichash_blake2b_init`, `crypto_generichash_blake2b_init_salt_personal`, `crypto_generichash_blake2b_KEYBYTES`, `crypto_generichash_blake2b_KEYBYTES_MAX`, `crypto_generichash_blake2b_KEYBYTES_MIN`, `crypto_generichash_blake2b_keygen`, `crypto_generichash_blake2b_PERSONALBYTES`, `crypto_generichash_blake2b_salt_personal`, `crypto_generichash_blake2b_SALTBYTES`, `crypto_generichash_blake2b_STATEBYTES`, `crypto_generichash_blake2b_update`, `crypto_generichash_primitive`, `crypto_hash_primitive`, `crypto_kdf_blake2b_BYTES_MAX`, `crypto_kdf_blake2b_BYTES_MIN`, `crypto_kdf_blake2b_CONTEXTBYTES`, `crypto_kdf_blake2b_derive_from_key`, `crypto_kdf_blake2b_KEYBYTES`, `crypto_kdf_primitive`, `crypto_kx_primitive`, `crypto_onetimeauth_keygen`, `crypto_onetimeauth_poly1305`, `crypto_onetimeauth_poly1305_BYTES`, `crypto_onetimeauth_poly1305_final`, `crypto_onetimeauth_poly1305_init`, `crypto_onetimeauth_poly1305_KEYBYTES`, `crypto_onetimeauth_poly1305_keygen`, `crypto_onetimeauth_poly1305_STATEBYTES`, `crypto_onetimeauth_poly1305_update`, `crypto_onetimeauth_poly1305_verify`, `crypto_onetimeauth_primitive`, `crypto_pwhash_alg_argon2i13`, `crypto_pwhash_alg_argon2id13`, `crypto_pwhash_alg_default`, `crypto_pwhash_argon2i`, `crypto_pwhash_argon2i_alg_argon2i13`, `crypto_pwhash_argon2i_BYTES_MAX`, `crypto_pwhash_argon2i_BYTES_MIN`, `crypto_pwhash_argon2i_memlimit_interactive`, `crypto_pwhash_argon2i_memlimit_max`, `crypto_pwhash_argon2i_memlimit_min`, `crypto_pwhash_argon2i_memlimit_moderate`, `crypto_pwhash_argon2i_memlimit_sensitive`, `crypto_pwhash_argon2i_opslimit_interactive`, `crypto_pwhash_argon2i_opslimit_max`, `crypto_pwhash_argon2i_opslimit_min`, `crypto_pwhash_argon2i_opslimit_moderate`, `crypto_pwhash_argon2i_opslimit_sensitive`, `crypto_pwhash_argon2i_passwd_max`, `crypto_pwhash_argon2i_passwd_min`, `crypto_pwhash_argon2i_SALTBYTES`, `crypto_pwhash_argon2i_str`, `crypto_pwhash_argon2i_str_needs_rehash`, `crypto_pwhash_argon2i_str_verify`, `crypto_pwhash_argon2i_STRBYTES`, `crypto_pwhash_argon2i_strprefix`, `crypto_pwhash_argon2id`, `crypto_pwhash_argon2id_alg_argon2id13`, `crypto_pwhash_argon2id_BYTES_MAX`, `crypto_pwhash_argon2id_BYTES_MIN`, `crypto_pwhash_argon2id_memlimit_interactive`, `crypto_pwhash_argon2id_memlimit_max`, `crypto_pwhash_argon2id_memlimit_min`, `crypto_pwhash_argon2id_memlimit_moderate`, `crypto_pwhash_argon2id_memlimit_sensitive`, `crypto_pwhash_argon2id_opslimit_interactive`, `crypto_pwhash_argon2id_opslimit_max`, `crypto_pwhash_argon2id_opslimit_min`, `crypto_pwhash_argon2id_opslimit_moderate`, `crypto_pwhash_argon2id_opslimit_sensitive`, `crypto_pwhash_argon2id_passwd_max`, `crypto_pwhash_argon2id_passwd_min`, `crypto_pwhash_argon2id_SALTBYTES`, `crypto_pwhash_argon2id_str`, `crypto_pwhash_argon2id_str_needs_rehash`, `crypto_pwhash_argon2id_str_verify`, `crypto_pwhash_argon2id_STRBYTES`, `crypto_pwhash_argon2id_strprefix`, `crypto_pwhash_memlimit_interactive`, `crypto_pwhash_memlimit_max`, `crypto_pwhash_memlimit_min`, `crypto_pwhash_memlimit_moderate`, `crypto_pwhash_memlimit_sensitive`, `crypto_pwhash_opslimit_interactive`, `crypto_pwhash_opslimit_max`, `crypto_pwhash_opslimit_min`, `crypto_pwhash_opslimit_moderate`, `crypto_pwhash_opslimit_sensitive`, `crypto_pwhash_passwd_max`, `crypto_pwhash_passwd_min`, `crypto_pwhash_primitive`, `crypto_pwhash_scryptsalsa208sha256_ll`, `crypto_pwhash_scryptsalsa208sha256_memlimit_interactive`, `crypto_pwhash_scryptsalsa208sha256_memlimit_max`, `crypto_pwhash_scryptsalsa208sha256_memlimit_min`, `crypto_pwhash_scryptsalsa208sha256_memlimit_sensitive`, `crypto_pwhash_scryptsalsa208sha256_opslimit_interactive`, `crypto_pwhash_scryptsalsa208sha256_opslimit_max`, `crypto_pwhash_scryptsalsa208sha256_opslimit_min`, `crypto_pwhash_scryptsalsa208sha256_opslimit_sensitive`, `crypto_pwhash_scryptsalsa208sha256_passwd_max`, `crypto_pwhash_scryptsalsa208sha256_passwd_min`, `crypto_pwhash_scryptsalsa208sha256_strprefix`, `crypto_pwhash_str_alg`, `crypto_pwhash_strprefix`, `crypto_scalarmult_curve25519`, `crypto_scalarmult_curve25519_base`, `crypto_scalarmult_curve25519_BYTES`, `crypto_scalarmult_curve25519_SCALARBYTES`, `crypto_scalarmult_primitive`, `crypto_scalarmult_ristretto255`, `crypto_scalarmult_ristretto255_base`, `crypto_scalarmult_ristretto255_BYTES`, `crypto_scalarmult_ristretto255_SCALARBYTES`, `crypto_secretbox_keygen`, `crypto_secretbox_MESSAGEBYTES_MAX`, `crypto_secretbox_primitive`, `crypto_secretbox_xchacha20poly1305_detached`, `crypto_secretbox_xchacha20poly1305_easy`, `crypto_secretbox_xchacha20poly1305_KEYBYTES`, `crypto_secretbox_xchacha20poly1305_MACBYTES`, `crypto_secretbox_xchacha20poly1305_MESSAGEBYTES_MAX`, `crypto_secretbox_xchacha20poly1305_NONCEBYTES`, `crypto_secretbox_xchacha20poly1305_open_detached`, `crypto_secretbox_xchacha20poly1305_open_easy`, `crypto_secretbox_xsalsa20poly1305`, `crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES`, `crypto_secretbox_xsalsa20poly1305_KEYBYTES`, `crypto_secretbox_xsalsa20poly1305_keygen`, `crypto_secretbox_xsalsa20poly1305_MACBYTES`, `crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX`, `crypto_secretbox_xsalsa20poly1305_NONCEBYTES`, `crypto_secretbox_xsalsa20poly1305_open`, `crypto_secretbox_xsalsa20poly1305_ZEROBYTES`, `crypto_secretstream_xchacha20poly1305_tag_final`, `crypto_secretstream_xchacha20poly1305_tag_message`, `crypto_secretstream_xchacha20poly1305_tag_push`, `crypto_secretstream_xchacha20poly1305_tag_rekey`, `crypto_shorthash_keygen`, `crypto_shorthash_primitive`, `crypto_shorthash_siphash24`, `crypto_shorthash_siphash24_BYTES`, `crypto_shorthash_siphash24_KEYBYTES`, `crypto_shorthash_siphashx24`, `crypto_shorthash_siphashx24_BYTES`, `crypto_shorthash_siphashx24_KEYBYTES`, `crypto_sign_ed25519`, `crypto_sign_ed25519_BYTES`, `crypto_sign_ed25519_detached`, `crypto_sign_ed25519_keypair`, `crypto_sign_ed25519_MESSAGEBYTES_MAX`, `crypto_sign_ed25519_open`, `crypto_sign_ed25519_PUBLICKEYBYTES`, `crypto_sign_ed25519_SECRETKEYBYTES`, `crypto_sign_ed25519_seed_keypair`, `crypto_sign_ed25519_SEEDBYTES`, `crypto_sign_ed25519_sk_to_seed`, `crypto_sign_ed25519_verify_detached`, `crypto_sign_ed25519ph_final_create`, `crypto_sign_ed25519ph_final_verify`, `crypto_sign_ed25519ph_init`, `crypto_sign_ed25519ph_STATEBYTES`, `crypto_sign_ed25519ph_update`, `crypto_sign_edwards25519sha512batch`, `crypto_sign_edwards25519sha512batch_keypair`, `crypto_sign_edwards25519sha512batch_open`, `crypto_sign_final_create`, `crypto_sign_final_verify`, `crypto_sign_init`, `crypto_sign_MESSAGEBYTES_MAX`, `crypto_sign_primitive`, `crypto_sign_STATEBYTES`, `crypto_sign_update`, `crypto_stream_chacha20_ietf_keygen`, `crypto_stream_chacha20_keygen`, `crypto_stream_keygen`, `crypto_stream_MESSAGEBYTES_MAX`, `crypto_stream_primitive`, `crypto_stream_salsa20`, `crypto_stream_salsa2012`, `crypto_stream_salsa2012_KEYBYTES`, `crypto_stream_salsa2012_keygen`, `crypto_stream_salsa2012_MESSAGEBYTES_MAX`, `crypto_stream_salsa2012_NONCEBYTES`, `crypto_stream_salsa2012_xor`, `crypto_stream_salsa208`, `crypto_stream_salsa208_KEYBYTES`, `crypto_stream_salsa208_keygen`, `crypto_stream_salsa208_MESSAGEBYTES_MAX`, `crypto_stream_salsa208_NONCEBYTES`, `crypto_stream_salsa208_xor`, `crypto_stream_salsa20_keygen`, `crypto_stream_xchacha20_keygen`, `crypto_stream_xsalsa20`, `crypto_stream_xsalsa20_KEYBYTES`, `crypto_stream_xsalsa20_keygen`, `crypto_stream_xsalsa20_MESSAGEBYTES_MAX`, `crypto_stream_xsalsa20_NONCEBYTES`, `crypto_stream_xsalsa20_xor`, `crypto_stream_xsalsa20_xor_ic`, `crypto_verify_16_BYTES`, `crypto_verify_32_BYTES`, `crypto_verify_64`, `crypto_verify_64_BYTES`, `randombytes`, `randombytes_close`, `randombytes_implementation_name`, `randombytes_internal_implementation`, `randombytes_set_implementation`, `randombytes_stir`, `randombytes_sysrandom_implementation`, `sodium_allocarray`, `sodium_base642bin`, `sodium_base64_encoded_len`, `sodium_bin2base64`, `sodium_bin2hex`, `sodium_hex2bin`, `sodium_init`, `sodium_library_minimal`, `sodium_library_version_major`, `sodium_library_version_minor`, `sodium_misuse`, `sodium_runtime_has_aesni`, `sodium_runtime_has_avx`, `sodium_runtime_has_avx2`, `sodium_runtime_has_avx512f`, `sodium_runtime_has_neon`, `sodium_runtime_has_pclmul`, `sodium_runtime_has_rdrand`, `sodium_runtime_has_sse2`, `sodium_runtime_has_sse3`, `sodium_runtime_has_sse41`, `sodium_runtime_has_ssse3`, `sodium_set_misuse_handler`, `sodium_stackzero`, `sodium_version_string` | |
| 285 | ||
| 286 | ## License | |
| 287 | ||
| 288 | [MIT](LICENSE.md) |
| … | … | @@ -0,0 +1,33 @@ |
| 1 | #!/usr/bin/env node | |
| 2 | ||
| 3 | const fs = require('fs') | |
| 4 | const path = require('path') | |
| 5 | const root = path.dirname(require.resolve('sodium-javascript/package.json')) | |
| 6 | ||
| 7 | const tmp = "module.exports = require('sodium-native')\n" | |
| 8 | ||
| 9 | function recurse (dir) { | |
| 10 | const ls = fs.readdirSync(dir) | |
| 11 | const subdir = path.relative(root, dir) | |
| 12 | if (subdir) fs.mkdirSync(subdir, { recursive: true }) | |
| 13 | ||
| 14 | for (const file of ls) { | |
| 15 | if (file === 'internal') recurse(path.join(dir, file)) | |
| 16 | if (!/\.js$/i.test(file)) continue | |
| 17 | if (file === 'example.js') continue | |
| 18 | if (file === 'test.js') continue | |
| 19 | ||
| 20 | fs.writeFileSync(path.join(__dirname, '..', subdir, file), tmp) | |
| 21 | } | |
| 22 | } | |
| 23 | ||
| 24 | recurse(root) | |
| 25 | ||
| 26 | const pkg = require('sodium-javascript/package.json') | |
| 27 | const myPkg = require('../package.json') | |
| 28 | ||
| 29 | for (const key of Object.keys(pkg.dependencies)) { | |
| 30 | myPkg.dependencies[key] = pkg.dependencies[key] | |
| 31 | } | |
| 32 | ||
| 33 | fs.writeFileSync(path.join(__dirname, '../package.json'), JSON.stringify(myPkg, null, 2) + '\n') |
| … | … | @@ -0,0 +1,32 @@ |
| 1 | var exec = require('child_process').execSync | |
| 2 | var path = require('path') | |
| 3 | ||
| 4 | var symbols = exec(`nm -gU "${path.join(require.resolve('sodium-native'), '../prebuilds/darwin-x64/libsodium.dylib')}"`).toString().trim().split('\n') | |
| 5 | var native = new Set(Object.keys(require('sodium-native')).sort()) | |
| 6 | var js = new Set(Object.keys(require('sodium-javascript')).sort()) | |
| 7 | ||
| 8 | symbols = symbols.map(l => l.split(' ').pop().replace(/^_/, '').replace(/_[^_]*bytes.*/, (s) => s.toUpperCase())) | |
| 9 | ||
| 10 | var missing = [] | |
| 11 | ||
| 12 | console.log(` | |
| 13 | <!-- Generated by build-scripts/symbols.js with | |
| 14 | sodium-native@${require('sodium-native/package.json').version} | |
| 15 | sodium-javascript@${require('sodium-javascript/package.json').version} -->\ | |
| 16 | `) | |
| 17 | ||
| 18 | const max = symbols.filter(s => native.has(s) || js.has(s)).map(s => s.length).reduce((a, b) => Math.max(a, b)) | |
| 19 | const lines = ':-' + ''.padEnd(max, '--') + '-:' | |
| 20 | const center = s => s.padStart(Math.floor((max + s.length) / 2), ' ').padEnd(max, ' ') | |
| 21 | ||
| 22 | console.log(`| ${center('C Library Symbol')} | \`sodium-native\` | \`sodium-javascript\` |`) | |
| 23 | console.log(`|${lines}|:--------------------:|:--------------------:|`) | |
| 24 | ||
| 25 | for (var i = 0; i < symbols.length; i++) { | |
| 26 | var s = symbols[i] | |
| 27 | if (!(native.has(s) || js.has(s))) missing.push(s) | |
| 28 | else console.log(`| \`${(s + '`').padEnd(max + 1, ' ')} | ${native.has(s) ? ' :white_check_mark: ' : ':small_red_triangle:'} | ${js.has(s) ? ' :white_check_mark: ' : ':small_red_triangle:'} |`) | |
| 29 | } | |
| 30 | ||
| 31 | console.log('\n### Missing altogether (Ctrl + F friendly)\n') | |
| 32 | console.log(missing.map(s => '`' + s + '`').join(', ')) |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | const fs = require('fs') | |
| 2 | const path = require('path') | |
| 3 | const { Transform, PassThrough } = require('stream') | |
| 4 | ||
| 5 | module.exports = function (file, opts) { | |
| 6 | const basedir = path.resolve(__dirname, '..') | |
| 7 | const relname = path.relative(basedir, file) | |
| 8 | ||
| 9 | if (opts._flags.browserField === false) { | |
| 10 | return new PassThrough() | |
| 11 | } | |
| 12 | ||
| 13 | return new Transform({ | |
| 14 | transform (data, enc, cb) { | |
| 15 | cb() | |
| 16 | }, | |
| 17 | flush (cb) { | |
| 18 | const filename = require.resolve('sodium-javascript/' + relname) | |
| 19 | fs.readFile(filename, (err, buf) => { | |
| 20 | if (err) return cb(err) | |
| 21 | this.push(buf) | |
| 22 | cb(null) | |
| 23 | }) | |
| 24 | } | |
| 25 | }) | |
| 26 | } |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1,49 @@ |
| 1 | { | |
| 2 | "name": "sodium-universal", | |
| 3 | "version": "5.0.1", | |
| 4 | "description": "Universal wrapper for sodium-javascript and sodium-native working in Node.js and the Browser", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "sodium-native": "^5.0.1" | |
| 8 | }, | |
| 9 | "peerDependencies": { | |
| 10 | "sodium-javascript": "~0.8.0" | |
| 11 | }, | |
| 12 | "peerDependenciesMeta": { | |
| 13 | "sodium-javascript": { | |
| 14 | "optional": true | |
| 15 | } | |
| 16 | }, | |
| 17 | "scripts": { | |
| 18 | "prepublish": "./build-scripts/generate.js" | |
| 19 | }, | |
| 20 | "browser": { | |
| 21 | "sodium-native": "sodium-javascript" | |
| 22 | }, | |
| 23 | "browserify": { | |
| 24 | "transform": [ | |
| 25 | "./build-scripts/transform.js" | |
| 26 | ] | |
| 27 | }, | |
| 28 | "repository": { | |
| 29 | "type": "git", | |
| 30 | "url": "git+https://github.com/holepunchto/sodium-universal.git" | |
| 31 | }, | |
| 32 | "keywords": [ | |
| 33 | "libsodium", | |
| 34 | "sodium", | |
| 35 | "sodium-native", | |
| 36 | "sodium-javascript", | |
| 37 | "browserify" | |
| 38 | ], | |
| 39 | "contributors": [ | |
| 40 | "Emil Bay <github@tixz.dk> (http://bayes.dk)", | |
| 41 | "Mathias Buus <mathiasbuus@gmail.com> (https://mafinto.sh)", | |
| 42 | "Christophe Diederichs <chm-diederichs@hyperdivision.dk>" | |
| 43 | ], | |
| 44 | "license": "MIT", | |
| 45 | "bugs": { | |
| 46 | "url": "https://github.com/holepunchto/sodium-universal/issues" | |
| 47 | }, | |
| 48 | "homepage": "https://github.com/holepunchto/sodium-universal#readme" | |
| 49 | } |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1,31 @@ |
| 1 | { | |
| 2 | "name": "hypercore-crypto", | |
| 3 | "version": "3.6.1", | |
| 4 | "description": "The crypto primitives used in hypercore, extracted into a separate module", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js" | |
| 8 | ], | |
| 9 | "dependencies": { | |
| 10 | "b4a": "^1.6.6", | |
| 11 | "compact-encoding": "^2.15.0", | |
| 12 | "sodium-universal": "^5.0.0" | |
| 13 | }, | |
| 14 | "devDependencies": { | |
| 15 | "brittle": "^3.5.0", | |
| 16 | "standard": "^17.1.0" | |
| 17 | }, | |
| 18 | "scripts": { | |
| 19 | "test": "standard && brittle test.js" | |
| 20 | }, | |
| 21 | "repository": { | |
| 22 | "type": "git", | |
| 23 | "url": "https://github.com/mafintosh/hypercore-crypto.git" | |
| 24 | }, | |
| 25 | "author": "Mathias Buus (@mafintosh)", | |
| 26 | "license": "MIT", | |
| 27 | "bugs": { | |
| 28 | "url": "https://github.com/mafintosh/hypercore-crypto/issues" | |
| 29 | }, | |
| 30 | "homepage": "https://github.com/mafintosh/hypercore-crypto" | |
| 31 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,43 @@ |
| 1 | # hypercore-errors | |
| 2 | ||
| 3 | Hypercore errors | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i hypercore-errors | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const { BLOCK_NOT_AVAILABLE } = require('hypercore-errors') | |
| 13 | ||
| 14 | throw BLOCK_NOT_AVAILABLE() | |
| 15 | ``` | |
| 16 | ||
| 17 | ## List | |
| 18 | ||
| 19 | - BAD_ARGUMENT | |
| 20 | - STORAGE_EMPTY | |
| 21 | - STORAGE_CONFLICT | |
| 22 | - INVALID_SIGNATURE | |
| 23 | - INVALID_CAPABILITY | |
| 24 | - INVALID_CHECKSUM | |
| 25 | - INVALID_OPERATION | |
| 26 | - INVALID_PROOF | |
| 27 | - BLOCK_NOT_AVAILABLE | |
| 28 | - SNAPSHOT_NOT_AVAILABLE | |
| 29 | - REQUEST_CANCELLED | |
| 30 | - REQUEST_TIMEOUT | |
| 31 | - SESSION_NOT_WRITABLE | |
| 32 | - SESSION_CLOSED | |
| 33 | - BATCH_UNFLUSHED | |
| 34 | - BATCH_ALREADY_EXISTS | |
| 35 | - BATCH_ALREADY_FLUSHED | |
| 36 | - OPLOG_CORRUPT | |
| 37 | - OPLOG_HEADER_OVERFLOW | |
| 38 | - INVALID_OPLOG_VERSION | |
| 39 | - WRITE_FAILED | |
| 40 | ||
| 41 | ## License | |
| 42 | ||
| 43 | Apache-2.0 |
| … | … | @@ -0,0 +1,115 @@ |
| 1 | const IdEnc = require('hypercore-id-encoding') | |
| 2 | ||
| 3 | module.exports = class HypercoreError extends Error { | |
| 4 | constructor (msg, code, fn = HypercoreError, discoveryKey = null) { | |
| 5 | if (discoveryKey) msg = `${msg} (discovery key: ${IdEnc.normalize(discoveryKey)})` | |
| 6 | super(`${code}: ${msg}`) | |
| 7 | ||
| 8 | this.code = code | |
| 9 | this.discoveryKey = discoveryKey | |
| 10 | ||
| 11 | if (Error.captureStackTrace) { | |
| 12 | Error.captureStackTrace(this, fn) | |
| 13 | } | |
| 14 | } | |
| 15 | ||
| 16 | get name () { | |
| 17 | return 'HypercoreError' | |
| 18 | } | |
| 19 | ||
| 20 | static ASSERTION (msg, discoveryKey = null) { // ERR_ASSERTION is picked up by safety-catch also | |
| 21 | return new HypercoreError(msg, 'ERR_ASSERTION', HypercoreError.ASSERT, discoveryKey) | |
| 22 | } | |
| 23 | ||
| 24 | static BAD_ARGUMENT (msg, discoveryKey = null) { | |
| 25 | return new HypercoreError(msg, 'BAD_ARGUMENT', HypercoreError.BAD_ARGUMENT, discoveryKey) | |
| 26 | } | |
| 27 | ||
| 28 | static STORAGE_EMPTY (msg, discoveryKey = null) { | |
| 29 | return new HypercoreError(msg, 'STORAGE_EMPTY', HypercoreError.STORAGE_EMPTY, discoveryKey) | |
| 30 | } | |
| 31 | ||
| 32 | static STORAGE_CONFLICT (msg, discoveryKey = null) { | |
| 33 | return new HypercoreError(msg, 'STORAGE_CONFLICT', HypercoreError.STORAGE_CONFLICT, discoveryKey) | |
| 34 | } | |
| 35 | ||
| 36 | static INVALID_SIGNATURE (msg, discoveryKey = null) { | |
| 37 | return new HypercoreError(msg, 'INVALID_SIGNATURE', HypercoreError.INVALID_SIGNATURE, discoveryKey) | |
| 38 | } | |
| 39 | ||
| 40 | static INVALID_CAPABILITY (msg, discoveryKey = null) { | |
| 41 | return new HypercoreError(msg, 'INVALID_CAPABILITY', HypercoreError.INVALID_CAPABILITY, discoveryKey) | |
| 42 | } | |
| 43 | ||
| 44 | static INVALID_CHECKSUM (msg = 'Invalid checksum', discoveryKey = null) { | |
| 45 | return new HypercoreError(msg, 'INVALID_CHECKSUM', HypercoreError.INVALID_CHECKSUM, discoveryKey) | |
| 46 | } | |
| 47 | ||
| 48 | static INVALID_OPERATION (msg, discoveryKey = null) { | |
| 49 | return new HypercoreError(msg, 'INVALID_OPERATION', HypercoreError.INVALID_OPERATION, discoveryKey) | |
| 50 | } | |
| 51 | ||
| 52 | static INVALID_PROOF (msg = 'Proof not verifiable', discoveryKey = null) { | |
| 53 | return new HypercoreError(msg, 'INVALID_PROOF', HypercoreError.INVALID_PROOF, discoveryKey) | |
| 54 | } | |
| 55 | ||
| 56 | static BLOCK_NOT_AVAILABLE (msg = 'Block is not available', discoveryKey = null) { | |
| 57 | return new HypercoreError(msg, 'BLOCK_NOT_AVAILABLE', HypercoreError.BLOCK_NOT_AVAILABLE, discoveryKey) | |
| 58 | } | |
| 59 | ||
| 60 | static SNAPSHOT_NOT_AVAILABLE (msg = 'Snapshot is not available', discoveryKey = null) { | |
| 61 | return new HypercoreError(msg, 'SNAPSHOT_NOT_AVAILABLE', HypercoreError.SNAPSHOT_NOT_AVAILABLE, discoveryKey) | |
| 62 | } | |
| 63 | ||
| 64 | static REQUEST_CANCELLED (msg = 'Request was cancelled', discoveryKey = null) { | |
| 65 | return new HypercoreError(msg, 'REQUEST_CANCELLED', HypercoreError.REQUEST_CANCELLED, discoveryKey) | |
| 66 | } | |
| 67 | ||
| 68 | static REQUEST_TIMEOUT (msg = 'Request timed out', discoveryKey = null) { | |
| 69 | return new HypercoreError(msg, 'REQUEST_TIMEOUT', HypercoreError.REQUEST_TIMEOUT, discoveryKey) | |
| 70 | } | |
| 71 | ||
| 72 | static SESSION_NOT_WRITABLE (msg = 'Session is not writable', discoveryKey = null) { | |
| 73 | return new HypercoreError(msg, 'SESSION_NOT_WRITABLE', HypercoreError.SESSION_NOT_WRITABLE, discoveryKey) | |
| 74 | } | |
| 75 | ||
| 76 | static SESSION_CLOSED (msg = 'Session is closed', discoveryKey = null) { | |
| 77 | return new HypercoreError(msg, 'SESSION_CLOSED', HypercoreError.SESSION_CLOSED, discoveryKey) | |
| 78 | } | |
| 79 | ||
| 80 | static BATCH_UNFLUSHED (msg = 'Batch not yet flushed', discoveryKey = null) { | |
| 81 | return new HypercoreError(msg, 'BATCH_UNFLUSHED', HypercoreError.BATCH_UNFLUSHED, discoveryKey) | |
| 82 | } | |
| 83 | ||
| 84 | static BATCH_ALREADY_EXISTS (msg = 'Batch already exists', discoveryKey = null) { | |
| 85 | return new HypercoreError(msg, 'BATCH_ALREADY_EXISTS', HypercoreError.BATCH_ALREADY_EXISTS, discoveryKey) | |
| 86 | } | |
| 87 | ||
| 88 | static BATCH_ALREADY_FLUSHED (msg = 'Batch has already been flushed', discoveryKey = null) { | |
| 89 | return new HypercoreError(msg, 'BATCH_ALREADY_FLUSHED', HypercoreError.BATCH_ALREADY_FLUSHED, discoveryKey) | |
| 90 | } | |
| 91 | ||
| 92 | static OPLOG_CORRUPT (msg = 'Oplog file appears corrupt or out of date', discoveryKey = null) { | |
| 93 | return new HypercoreError(msg, 'OPLOG_CORRUPT', HypercoreError.OPLOG_CORRUPT, discoveryKey) | |
| 94 | } | |
| 95 | ||
| 96 | static OPLOG_HEADER_OVERFLOW (msg = 'Oplog header exceeds page size', discoveryKey = null) { | |
| 97 | return new HypercoreError(msg, 'OPLOG_HEADER_OVERFLOW', HypercoreError.OPLOG_HEADER_OVERFLOW, discoveryKey) | |
| 98 | } | |
| 99 | ||
| 100 | static INVALID_OPLOG_VERSION (msg = 'Invalid header version', discoveryKey = null) { | |
| 101 | return new HypercoreError(msg, 'INVALID_OPLOG_VERSION', HypercoreError.INVALID_OPLOG_VERSION, discoveryKey) | |
| 102 | } | |
| 103 | ||
| 104 | static WRITE_FAILED (msg = 'Write to storage failed', discoveryKey = null) { | |
| 105 | return new HypercoreError(msg, 'WRITE_FAILED', HypercoreError.WRITE_FAILED, discoveryKey) | |
| 106 | } | |
| 107 | ||
| 108 | static DECODING_ERROR (msg = 'Decoding error', discoveryKey = null) { | |
| 109 | return new HypercoreError(msg, 'DECODING_ERROR', HypercoreError.DECODING_ERROR, discoveryKey) | |
| 110 | } | |
| 111 | ||
| 112 | static SESSION_MOVED (msg = 'Session moved', discoveryKey = null) { | |
| 113 | return new HypercoreError(msg, 'SESSION_MOVED', HypercoreError.SESSION_MOVED, discoveryKey) | |
| 114 | } | |
| 115 | } |
| … | … | @@ -0,0 +1,30 @@ |
| 1 | { | |
| 2 | "name": "hypercore-errors", | |
| 3 | "version": "1.5.0", | |
| 4 | "description": "Hypercore errors", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js" | |
| 8 | ], | |
| 9 | "scripts": { | |
| 10 | "test": "standard && brittle test.js" | |
| 11 | }, | |
| 12 | "repository": { | |
| 13 | "type": "git", | |
| 14 | "url": "https://github.com/holepunchto/hypercore-errors.git" | |
| 15 | }, | |
| 16 | "author": "Holepunch", | |
| 17 | "license": "Apache-2.0", | |
| 18 | "bugs": { | |
| 19 | "url": "https://github.com/holepunchto/hypercore-errors/issues" | |
| 20 | }, | |
| 21 | "homepage": "https://github.com/holepunchto/hypercore-errors#readme", | |
| 22 | "devDependencies": { | |
| 23 | "b4a": "^1.6.7", | |
| 24 | "brittle": "^3.1.3", | |
| 25 | "standard": "^17.0.0" | |
| 26 | }, | |
| 27 | "dependencies": { | |
| 28 | "hypercore-id-encoding": "^1.3.0" | |
| 29 | } | |
| 30 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,55 @@ |
| 1 | # hypercore-id-encoding | |
| 2 | ||
| 3 | Encodes Hypercore keys into z-base32 ids, and decodes both hex/z-base32 strings. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install hypercore-id-encoding | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const { encode, decode, normalize } = require('hypercore-id-encoding') | |
| 13 | ||
| 14 | const id = encode(core.key) // (z-base32 String) | |
| 15 | const hexEncoded = core.key.toString('hex') | |
| 16 | ||
| 17 | const core = new Hypercore(ram, decode(id)) | |
| 18 | const core2 = new Hypercore(ram, decode(hexEncoded)) // Will also work with hex | |
| 19 | ||
| 20 | const id2 = normalize(id) | |
| 21 | const id3 = normalize(hexEncoded) | |
| 22 | ``` | |
| 23 | ||
| 24 | ## API | |
| 25 | ||
| 26 | #### `const id = encode(hypercoreKey)` | |
| 27 | ||
| 28 | Encodes a 32-byte Hypercore key into a z-base32 id. | |
| 29 | ||
| 30 | `hypercoreKey` must be a Buffer or an ArrayBuffer. | |
| 31 | ||
| 32 | #### `const buf = decode(hypercoreId)` | |
| 33 | ||
| 34 | Decodes an id or pear link into a Hypercore key. | |
| 35 | ||
| 36 | If `hypercoreId` is a 52-character String, it will be decoded as z-base32. | |
| 37 | ||
| 38 | If `hypercoreId` is a 64-character String, it will be decoded as hex. | |
| 39 | ||
| 40 | If `hypercoreId` is already a Buffer and is a valid id, it will be returned as-is. | |
| 41 | ||
| 42 | #### `const id = normalize(any)` | |
| 43 | ||
| 44 | Decodes and encodes the input `any` to always return a z-base32 id. | |
| 45 | ||
| 46 | #### `isValid(any)` | |
| 47 | ||
| 48 | Returns a boolean indicating whether the key is a valid hypercore key (meaning that it can be decoded/normalised). | |
| 49 | ||
| 50 | ## License | |
| 51 | ||
| 52 | Apache-2.0 | |
| 53 | ||
| 54 | ||
| 55 |
| … | … | @@ -0,0 +1,44 @@ |
| 1 | const z32 = require('z32') | |
| 2 | const b4a = require('b4a') | |
| 3 | ||
| 4 | module.exports = { | |
| 5 | encode, | |
| 6 | decode, | |
| 7 | normalize, | |
| 8 | isValid | |
| 9 | } | |
| 10 | ||
| 11 | function encode (key) { | |
| 12 | if (!b4a.isBuffer(key)) throw new Error('Key must be a Buffer') | |
| 13 | if (key.byteLength !== 32) throw new Error('Key must be 32-bytes long') | |
| 14 | return z32.encode(key) | |
| 15 | } | |
| 16 | ||
| 17 | function decode (id) { | |
| 18 | if (b4a.isBuffer(id)) { | |
| 19 | if (id.byteLength !== 32) throw new Error('ID must be 32-bytes long') | |
| 20 | return id | |
| 21 | } | |
| 22 | if (typeof id === 'string') { | |
| 23 | if (id.startsWith('pear://')) id = id.slice(7).split('/')[0] | |
| 24 | if (id.length === 52) return z32.decode(id) | |
| 25 | if (id.length === 64) { | |
| 26 | const buf = b4a.from(id, 'hex') | |
| 27 | if (buf.byteLength === 32) return buf | |
| 28 | } | |
| 29 | } | |
| 30 | throw new Error('Invalid Hypercore key') | |
| 31 | } | |
| 32 | ||
| 33 | function normalize (any) { | |
| 34 | return encode(decode(any)) | |
| 35 | } | |
| 36 | ||
| 37 | function isValid (any) { | |
| 38 | try { | |
| 39 | decode(any) | |
| 40 | return true | |
| 41 | } catch { | |
| 42 | return false | |
| 43 | } | |
| 44 | } |
| … | … | @@ -0,0 +1,33 @@ |
| 1 | { | |
| 2 | "name": "hypercore-id-encoding", | |
| 3 | "version": "1.3.0", | |
| 4 | "description": "Convert Hypercore keys to/from z-base32 or hex", | |
| 5 | "main": "index.js", | |
| 6 | "scripts": { | |
| 7 | "test": "standard && brittle test.js" | |
| 8 | }, | |
| 9 | "files": [ | |
| 10 | "index.js" | |
| 11 | ], | |
| 12 | "dependencies": { | |
| 13 | "b4a": "^1.5.3", | |
| 14 | "z32": "^1.0.0" | |
| 15 | }, | |
| 16 | "devDependencies": { | |
| 17 | "brittle": "^3.1.1", | |
| 18 | "hypercore": "^10.0.0", | |
| 19 | "random-access-memory": "^6.0.0", | |
| 20 | "standard": "^17.1.0" | |
| 21 | }, | |
| 22 | "license": "Apache-2.0", | |
| 23 | "repository": { | |
| 24 | "type": "git", | |
| 25 | "url": "git+https://github.com/holepunchto/hypercore-id-encoding.git" | |
| 26 | }, | |
| 27 | "keywords": [], | |
| 28 | "author": "", | |
| 29 | "bugs": { | |
| 30 | "url": "https://github.com/holepunchto/hypercore-id-encoding/issues" | |
| 31 | }, | |
| 32 | "homepage": "https://github.com/holepunchto/hypercore-id-encoding#readme" | |
| 33 | } |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2016 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,488 @@ |
| 1 | # Hypercore | |
| 2 | ||
| 3 | ### [See the full API docs at docs.pears.com](https://docs.pears.com/building-blocks/hypercore) | |
| 4 | ||
| 5 | Hypercore is a secure, distributed append-only log. | |
| 6 | ||
| 7 | Built for sharing large datasets and streams of real time data | |
| 8 | ||
| 9 | ## Features | |
| 10 | ||
| 11 | * **Sparse replication.** Only download the data you are interested in. | |
| 12 | * **Realtime.** Get the latest updates to the log fast and securely. | |
| 13 | * **Performant.** Uses a simple flat file structure to maximize I/O performance. | |
| 14 | * **Secure.** Uses signed merkle trees to verify log integrity in real time. | |
| 15 | * **Modular.** Hypercore aims to do one thing and one thing well - distributing a stream of data. | |
| 16 | ||
| 17 | Note that the latest release is Hypercore 10, which adds support for truncate and many other things. | |
| 18 | Version 10 is not compatible with earlier versions (9 and earlier), but is considered LTS, meaning the storage format and wire protocol is forward compatible with future versions. | |
| 19 | ||
| 20 | ## Install | |
| 21 | ||
| 22 | ```sh | |
| 23 | npm install hypercore | |
| 24 | ``` | |
| 25 | ||
| 26 | ## API | |
| 27 | ||
| 28 | #### `const core = new Hypercore(storage, [key], [options])` | |
| 29 | ||
| 30 | Make a new Hypercore instance. | |
| 31 | ||
| 32 | `storage` should be set to a directory where you want to store the data and core metadata. | |
| 33 | ||
| 34 | ``` js | |
| 35 | const core = new Hypercore('./directory') // store data in ./directory | |
| 36 | ``` | |
| 37 | ||
| 38 | Alternatively you can pass a function instead that is called with every filename Hypercore needs to function and return your own [abstract-random-access](https://github.com/random-access-storage/abstract-random-access) instance that is used to store the data. | |
| 39 | ||
| 40 | ``` js | |
| 41 | const RAM = require('random-access-memory') | |
| 42 | const core = new Hypercore((filename) => { | |
| 43 | // filename will be one of: data, bitfield, tree, signatures, key, secret_key | |
| 44 | // the data file will contain all your data concatenated. | |
| 45 | ||
| 46 | // just store all files in ram by returning a random-access-memory instance | |
| 47 | return new RAM() | |
| 48 | }) | |
| 49 | ``` | |
| 50 | ||
| 51 | Per default Hypercore uses [random-access-file](https://github.com/random-access-storage/random-access-file). This is also useful if you want to store specific files in other directories. | |
| 52 | ||
| 53 | Hypercore will produce the following files: | |
| 54 | ||
| 55 | * `oplog` - The internal truncating journal/oplog that tracks mutations, the public key and other metadata. | |
| 56 | * `tree` - The Merkle Tree file. | |
| 57 | * `bitfield` - The bitfield of which data blocks this core has. | |
| 58 | * `data` - The raw data of each block. | |
| 59 | ||
| 60 | Note that `tree`, `data`, and `bitfield` are normally heavily sparse files. | |
| 61 | ||
| 62 | `key` can be set to a Hypercore public key. If you do not set this the public key will be loaded from storage. If no key exists a new key pair will be generated. | |
| 63 | ||
| 64 | `options` include: | |
| 65 | ||
| 66 | ``` js | |
| 67 | { | |
| 68 | createIfMissing: true, // create a new Hypercore key pair if none was present in storage | |
| 69 | overwrite: false, // overwrite any old Hypercore that might already exist | |
| 70 | sparse: true, // enable sparse mode, counting unavailable blocks towards core.length and core.byteLength | |
| 71 | valueEncoding: 'json' | 'utf-8' | 'binary', // defaults to binary | |
| 72 | encodeBatch: batch => { ... }, // optionally apply an encoding to complete batches | |
| 73 | keyPair: kp, // optionally pass the public key and secret key as a key pair | |
| 74 | encryptionKey: k, // optionally pass an encryption key to enable block encryption | |
| 75 | onwait: () => {}, // hook that is called if gets are waiting for download | |
| 76 | timeout: 0, // wait at max some milliseconds (0 means no timeout) | |
| 77 | writable: true, // disable appends and truncates | |
| 78 | inflightRange: null // Advanced option. Set to [minInflight, maxInflight] to change the min and max inflight blocks per peer when downloading. | |
| 79 | } | |
| 80 | ``` | |
| 81 | ||
| 82 | You can also set valueEncoding to any [abstract-encoding](https://github.com/mafintosh/abstract-encoding) or [compact-encoding](https://github.com/compact-encoding) instance. | |
| 83 | ||
| 84 | valueEncodings will be applied to individual blocks, even if you append batches. If you want to control encoding at the batch-level, you can use the `encodeBatch` option, which is a function that takes a batch and returns a binary-encoded batch. If you provide a custom valueEncoding, it will not be applied prior to `encodeBatch`. | |
| 85 | ||
| 86 | #### `const { length, byteLength } = await core.append(block)` | |
| 87 | ||
| 88 | Append a block of data (or an array of blocks) to the core. | |
| 89 | Returns the new length and byte length of the core. | |
| 90 | ||
| 91 | ``` js | |
| 92 | // simple call append with a new block of data | |
| 93 | await core.append(Buffer.from('I am a block of data')) | |
| 94 | ||
| 95 | // pass an array to append multiple blocks as a batch | |
| 96 | await core.append([Buffer.from('batch block 1'), Buffer.from('batch block 2')]) | |
| 97 | ``` | |
| 98 | ||
| 99 | #### `const block = await core.get(index, [options])` | |
| 100 | ||
| 101 | Get a block of data. | |
| 102 | If the data is not available locally this method will prioritize and wait for the data to be downloaded. | |
| 103 | ||
| 104 | ``` js | |
| 105 | // get block #42 | |
| 106 | const block = await core.get(42) | |
| 107 | ||
| 108 | // get block #43, but only wait 5s | |
| 109 | const blockIfFast = await core.get(43, { timeout: 5000 }) | |
| 110 | ||
| 111 | // get block #44, but only if we have it locally | |
| 112 | const blockLocal = await core.get(44, { wait: false }) | |
| 113 | ``` | |
| 114 | ||
| 115 | `options` include: | |
| 116 | ||
| 117 | ``` js | |
| 118 | { | |
| 119 | wait: true, // wait for block to be downloaded | |
| 120 | onwait: () => {}, // hook that is called if the get is waiting for download | |
| 121 | timeout: 0, // wait at max some milliseconds (0 means no timeout) | |
| 122 | valueEncoding: 'json' | 'utf-8' | 'binary', // defaults to the core's valueEncoding | |
| 123 | decrypt: true // automatically decrypts the block if encrypted | |
| 124 | } | |
| 125 | ``` | |
| 126 | ||
| 127 | #### `const has = await core.has(start, [end])` | |
| 128 | ||
| 129 | Check if the core has all blocks between `start` and `end`. | |
| 130 | ||
| 131 | #### `const updated = await core.update([options])` | |
| 132 | ||
| 133 | Waits for initial proof of the new core length until all `findingPeers` calls has finished. | |
| 134 | ||
| 135 | ``` js | |
| 136 | const updated = await core.update() | |
| 137 | ||
| 138 | console.log('core was updated?', updated, 'length is', core.length) | |
| 139 | ``` | |
| 140 | ||
| 141 | `options` include: | |
| 142 | ||
| 143 | ``` js | |
| 144 | { | |
| 145 | wait: false | |
| 146 | } | |
| 147 | ``` | |
| 148 | ||
| 149 | Use `core.findingPeers()` or `{ wait: true }` to make `await core.update()` blocking. | |
| 150 | ||
| 151 | #### `const [index, relativeOffset] = await core.seek(byteOffset, [options])` | |
| 152 | ||
| 153 | Seek to a byte offset. | |
| 154 | ||
| 155 | Returns `[index, relativeOffset]`, where `index` is the data block the `byteOffset` is contained in and `relativeOffset` is | |
| 156 | the relative byte offset in the data block. | |
| 157 | ||
| 158 | ``` js | |
| 159 | await core.append([Buffer.from('abc'), Buffer.from('d'), Buffer.from('efg')]) | |
| 160 | ||
| 161 | const first = await core.seek(1) // returns [0, 1] | |
| 162 | const second = await core.seek(3) // returns [1, 0] | |
| 163 | const third = await core.seek(5) // returns [2, 1] | |
| 164 | ``` | |
| 165 | ||
| 166 | ``` js | |
| 167 | { | |
| 168 | wait: true, // wait for data to be downloaded | |
| 169 | timeout: 0 // wait at max some milliseconds (0 means no timeout) | |
| 170 | } | |
| 171 | ``` | |
| 172 | ||
| 173 | #### `const stream = core.createReadStream([options])` | |
| 174 | ||
| 175 | Make a read stream to read a range of data out at once. | |
| 176 | ||
| 177 | ``` js | |
| 178 | // read the full core | |
| 179 | const fullStream = core.createReadStream() | |
| 180 | ||
| 181 | // read from block 10-15 | |
| 182 | const partialStream = core.createReadStream({ start: 10, end: 15 }) | |
| 183 | ||
| 184 | // pipe the stream somewhere using the .pipe method on Node.js or consume it as | |
| 185 | // an async iterator | |
| 186 | ||
| 187 | for await (const data of fullStream) { | |
| 188 | console.log('data:', data) | |
| 189 | } | |
| 190 | ``` | |
| 191 | ||
| 192 | `options` include: | |
| 193 | ||
| 194 | ``` js | |
| 195 | { | |
| 196 | start: 0, | |
| 197 | end: core.length, | |
| 198 | live: false, | |
| 199 | snapshot: true // auto set end to core.length on open or update it on every read | |
| 200 | } | |
| 201 | ``` | |
| 202 | ||
| 203 | #### `const bs = core.createByteStream([options])` | |
| 204 | ||
| 205 | Make a byte stream to read a range of bytes. | |
| 206 | ||
| 207 | ``` js | |
| 208 | // Read the full core | |
| 209 | const fullStream = core.createByteStream() | |
| 210 | ||
| 211 | // Read from byte 3, and from there read 50 bytes | |
| 212 | const partialStream = core.createByteStream({ byteOffset: 3, byteLength: 50 }) | |
| 213 | ||
| 214 | // Consume it as an async iterator | |
| 215 | for await (const data of fullStream) { | |
| 216 | console.log('data:', data) | |
| 217 | } | |
| 218 | ||
| 219 | // Or pipe it somewhere like any stream: | |
| 220 | partialStream.pipe(process.stdout) | |
| 221 | ``` | |
| 222 | ||
| 223 | `options` include: | |
| 224 | ||
| 225 | ``` js | |
| 226 | { | |
| 227 | byteOffset: 0, | |
| 228 | byteLength: core.byteLength - options.byteOffset, | |
| 229 | prefetch: 32 | |
| 230 | } | |
| 231 | ``` | |
| 232 | ||
| 233 | #### `const cleared = await core.clear(start, [end], [options])` | |
| 234 | ||
| 235 | Clear stored blocks between `start` and `end`, reclaiming storage when possible. | |
| 236 | ||
| 237 | ``` js | |
| 238 | await core.clear(4) // clear block 4 from your local cache | |
| 239 | await core.clear(0, 10) // clear block 0-10 from your local cache | |
| 240 | ``` | |
| 241 | ||
| 242 | The core will also gossip to peers it is connected to, that is no longer has these blocks. | |
| 243 | ||
| 244 | `options` include: | |
| 245 | ```js | |
| 246 | { | |
| 247 | diff: false // Returned `cleared` bytes object is null unless you enable this | |
| 248 | } | |
| 249 | ``` | |
| 250 | ||
| 251 | #### `await core.truncate(newLength, [forkId])` | |
| 252 | ||
| 253 | Truncate the core to a smaller length. | |
| 254 | ||
| 255 | Per default this will update the fork id of the core to `+ 1`, but you can set the fork id you prefer with the option. | |
| 256 | Note that the fork id should be monotonely incrementing. | |
| 257 | ||
| 258 | #### `await core.purge()` | |
| 259 | ||
| 260 | Purge the hypercore from your storage, completely removing all data. | |
| 261 | ||
| 262 | #### `const hash = await core.treeHash([length])` | |
| 263 | ||
| 264 | Get the Merkle Tree hash of the core at a given length, defaulting to the current length of the core. | |
| 265 | ||
| 266 | #### `const range = core.download([range])` | |
| 267 | ||
| 268 | Download a range of data. | |
| 269 | ||
| 270 | You can await when the range has been fully downloaded by doing: | |
| 271 | ||
| 272 | ```js | |
| 273 | await range.done() | |
| 274 | ``` | |
| 275 | ||
| 276 | A range can have the following properties: | |
| 277 | ||
| 278 | ``` js | |
| 279 | { | |
| 280 | start: startIndex, | |
| 281 | end: nonInclusiveEndIndex, | |
| 282 | blocks: [index1, index2, ...], | |
| 283 | linear: false // download range linearly and not randomly | |
| 284 | } | |
| 285 | ``` | |
| 286 | ||
| 287 | To download the full core continuously (often referred to as non sparse mode) do | |
| 288 | ||
| 289 | ``` js | |
| 290 | // Note that this will never be considered downloaded as the range | |
| 291 | // will keep waiting for new blocks to be appended. | |
| 292 | core.download({ start: 0, end: -1 }) | |
| 293 | ``` | |
| 294 | ||
| 295 | To download a discrete range of blocks pass a list of indices. | |
| 296 | ||
| 297 | ```js | |
| 298 | core.download({ blocks: [4, 9, 7] }) | |
| 299 | ``` | |
| 300 | ||
| 301 | To cancel downloading a range simply destroy the range instance. | |
| 302 | ||
| 303 | ``` js | |
| 304 | // will stop downloading now | |
| 305 | range.destroy() | |
| 306 | ``` | |
| 307 | ||
| 308 | #### `const session = await core.session([options])` | |
| 309 | ||
| 310 | Creates a new Hypercore instance that shares the same underlying core. | |
| 311 | ||
| 312 | You must close any session you make. | |
| 313 | ||
| 314 | Options are inherited from the parent instance, unless they are re-set. | |
| 315 | ||
| 316 | `options` are the same as in the constructor. | |
| 317 | ||
| 318 | #### `const info = await core.info([options])` | |
| 319 | ||
| 320 | Get information about this core, such as its total size in bytes. | |
| 321 | ||
| 322 | The object will look like this: | |
| 323 | ||
| 324 | ```js | |
| 325 | Info { | |
| 326 | key: Buffer(...), | |
| 327 | discoveryKey: Buffer(...), | |
| 328 | length: 18, | |
| 329 | contiguousLength: 16, | |
| 330 | byteLength: 742, | |
| 331 | fork: 0, | |
| 332 | padding: 8, | |
| 333 | storage: { | |
| 334 | oplog: 8192, | |
| 335 | tree: 4096, | |
| 336 | blocks: 4096, | |
| 337 | bitfield: 4096 | |
| 338 | } | |
| 339 | } | |
| 340 | ``` | |
| 341 | ||
| 342 | `options` include: | |
| 343 | ||
| 344 | ```js | |
| 345 | { | |
| 346 | storage: false // get storage estimates in bytes, disabled by default | |
| 347 | } | |
| 348 | ``` | |
| 349 | ||
| 350 | #### `await core.close()` | |
| 351 | ||
| 352 | Fully close this core. | |
| 353 | ||
| 354 | #### `core.on('close')` | |
| 355 | ||
| 356 | Emitted when the core has been fully closed. | |
| 357 | ||
| 358 | #### `await core.ready()` | |
| 359 | ||
| 360 | Wait for the core to fully open. | |
| 361 | ||
| 362 | After this has called `core.length` and other properties have been set. | |
| 363 | ||
| 364 | In general you do NOT need to wait for `ready`, unless checking a synchronous property, | |
| 365 | as all internals await this themself. | |
| 366 | ||
| 367 | #### `core.on('ready')` | |
| 368 | ||
| 369 | Emitted after the core has initially opened all its internal state. | |
| 370 | ||
| 371 | #### `core.writable` | |
| 372 | ||
| 373 | Can we append to this core? | |
| 374 | ||
| 375 | Populated after `ready` has been emitted. Will be `false` before the event. | |
| 376 | ||
| 377 | #### `core.readable` | |
| 378 | ||
| 379 | Can we read from this core? After closing the core this will be false. | |
| 380 | ||
| 381 | Populated after `ready` has been emitted. Will be `false` before the event. | |
| 382 | ||
| 383 | #### `core.id` | |
| 384 | ||
| 385 | String containing the id (z-base-32 of the public key) identifying this core. | |
| 386 | ||
| 387 | Populated after `ready` has been emitted. Will be `null` before the event. | |
| 388 | ||
| 389 | #### `core.key` | |
| 390 | ||
| 391 | Buffer containing the public key identifying this core. | |
| 392 | ||
| 393 | Populated after `ready` has been emitted. Will be `null` before the event. | |
| 394 | ||
| 395 | #### `core.keyPair` | |
| 396 | ||
| 397 | Object containing buffers of the core's public and secret key | |
| 398 | ||
| 399 | Populated after `ready` has been emitted. Will be `null` before the event. | |
| 400 | ||
| 401 | #### `core.discoveryKey` | |
| 402 | ||
| 403 | Buffer containing a key derived from the core's public key. | |
| 404 | In contrast to `core.key` this key does not allow you to verify the data but can be used to announce or look for peers that are sharing the same core, without leaking the core key. | |
| 405 | ||
| 406 | Populated after `ready` has been emitted. Will be `null` before the event. | |
| 407 | ||
| 408 | #### `core.encryptionKey` | |
| 409 | ||
| 410 | Buffer containing the optional block encryption key of this core. Will be `null` unless block encryption is enabled. | |
| 411 | ||
| 412 | #### `core.length` | |
| 413 | ||
| 414 | How many blocks of data are available on this core? If `sparse: false`, this will equal `core.contiguousLength`. | |
| 415 | ||
| 416 | Populated after `ready` has been emitted. Will be `0` before the event. | |
| 417 | ||
| 418 | #### `core.contiguousLength` | |
| 419 | ||
| 420 | How many blocks are contiguously available starting from the first block of this core? | |
| 421 | ||
| 422 | Populated after `ready` has been emitted. Will be `0` before the event. | |
| 423 | ||
| 424 | #### `core.fork` | |
| 425 | ||
| 426 | What is the current fork id of this core? | |
| 427 | ||
| 428 | Populated after `ready` has been emitted. Will be `0` before the event. | |
| 429 | ||
| 430 | #### `core.padding` | |
| 431 | ||
| 432 | How much padding is applied to each block of this core? Will be `0` unless block encryption is enabled. | |
| 433 | ||
| 434 | #### `const stream = core.replicate(isInitiatorOrReplicationStream)` | |
| 435 | ||
| 436 | Create a replication stream. You should pipe this to another Hypercore instance. | |
| 437 | ||
| 438 | The `isInitiator` argument is a boolean indicating whether you are the initiator of the connection (ie the client) | |
| 439 | or if you are the passive part (ie the server). | |
| 440 | ||
| 441 | If you are using a P2P swarm like [Hyperswarm](https://github.com/hyperswarm/hyperswarm) you can know this by checking if the swarm connection is a client socket or server socket. In Hyperswarm you can check that using the [client property on the peer details object](https://github.com/hyperswarm/hyperswarm#swarmonconnection-socket-details--) | |
| 442 | ||
| 443 | If you want to multiplex the replication over an existing Hypercore replication stream you can pass | |
| 444 | another stream instance instead of the `isInitiator` boolean. | |
| 445 | ||
| 446 | ``` js | |
| 447 | // assuming we have two cores, localCore + remoteCore, sharing the same key | |
| 448 | // on a server | |
| 449 | const net = require('net') | |
| 450 | const server = net.createServer(function (socket) { | |
| 451 | socket.pipe(remoteCore.replicate(false)).pipe(socket) | |
| 452 | }) | |
| 453 | ||
| 454 | // on a client | |
| 455 | const socket = net.connect(...) | |
| 456 | socket.pipe(localCore.replicate(true)).pipe(socket) | |
| 457 | ``` | |
| 458 | ||
| 459 | #### `const done = core.findingPeers()` | |
| 460 | ||
| 461 | Create a hook that tells Hypercore you are finding peers for this core in the background. Call `done` when your current discovery iteration is done. | |
| 462 | If you're using Hyperswarm, you'd normally call this after a `swarm.flush()` finishes. | |
| 463 | ||
| 464 | This allows `core.update` to wait for either the `findingPeers` hook to finish or one peer to appear before deciding whether it should wait for a merkle tree update before returning. | |
| 465 | ||
| 466 | #### `core.on('append')` | |
| 467 | ||
| 468 | Emitted when the core has been appended to (i.e. has a new length / byteLength), either locally or remotely. | |
| 469 | ||
| 470 | #### `core.on('truncate', ancestors, forkId)` | |
| 471 | ||
| 472 | Emitted when the core has been truncated, either locally or remotely. | |
| 473 | ||
| 474 | #### `core.on('peer-add')` | |
| 475 | ||
| 476 | Emitted when a new connection has been established with a peer. | |
| 477 | ||
| 478 | #### `core.on('peer-remove')` | |
| 479 | ||
| 480 | Emitted when a peer's connection has been closed. | |
| 481 | ||
| 482 | #### `core.on('upload', index, byteLength, peer)` | |
| 483 | ||
| 484 | Emitted when a block is uploaded to a peer. | |
| 485 | ||
| 486 | #### `core.on('download', index, byteLength, peer)` | |
| 487 | ||
| 488 | Emitted when a block is downloaded from a peer. |
| … | … | @@ -0,0 +1,2 @@ |
| 1 | // explicitly exposed as hypercore/errors | |
| 2 | module.exports = require('hypercore-errors') |
| … | … | @@ -0,0 +1,1149 @@ |
| 1 | const { EventEmitter } = require('events') | |
| 2 | const RAF = require('random-access-file') | |
| 3 | const isOptions = require('is-options') | |
| 4 | const hypercoreCrypto = require('hypercore-crypto') | |
| 5 | const c = require('compact-encoding') | |
| 6 | const b4a = require('b4a') | |
| 7 | const Xache = require('xache') | |
| 8 | const NoiseSecretStream = require('@hyperswarm/secret-stream') | |
| 9 | const Protomux = require('protomux') | |
| 10 | const z32 = require('z32') | |
| 11 | const id = require('hypercore-id-encoding') | |
| 12 | const safetyCatch = require('safety-catch') | |
| 13 | const unslab = require('unslab') | |
| 14 | ||
| 15 | const Replicator = require('./lib/replicator') | |
| 16 | const Core = require('./lib/core') | |
| 17 | const BlockEncryption = require('./lib/block-encryption') | |
| 18 | const Info = require('./lib/info') | |
| 19 | const Download = require('./lib/download') | |
| 20 | const Batch = require('./lib/batch') | |
| 21 | const { manifestHash, createManifest } = require('./lib/verifier') | |
| 22 | const { ReadStream, WriteStream, ByteStream } = require('./lib/streams') | |
| 23 | const { | |
| 24 | ASSERTION, | |
| 25 | BAD_ARGUMENT, | |
| 26 | SESSION_CLOSED, | |
| 27 | SESSION_NOT_WRITABLE, | |
| 28 | SNAPSHOT_NOT_AVAILABLE, | |
| 29 | DECODING_ERROR | |
| 30 | } = require('hypercore-errors') | |
| 31 | ||
| 32 | const promises = Symbol.for('hypercore.promises') | |
| 33 | const inspect = Symbol.for('nodejs.util.inspect.custom') | |
| 34 | ||
| 35 | // Hypercore actually does not have any notion of max/min block sizes | |
| 36 | // but we enforce 15mb to ensure smooth replication (each block is transmitted atomically) | |
| 37 | const MAX_SUGGESTED_BLOCK_SIZE = 15 * 1024 * 1024 | |
| 38 | ||
| 39 | module.exports = class Hypercore extends EventEmitter { | |
| 40 | constructor (storage, key, opts) { | |
| 41 | super() | |
| 42 | ||
| 43 | if (isOptions(storage)) { | |
| 44 | opts = storage | |
| 45 | storage = null | |
| 46 | key = opts.key || null | |
| 47 | } else if (isOptions(key)) { | |
| 48 | opts = key | |
| 49 | key = opts.key || null | |
| 50 | } | |
| 51 | ||
| 52 | if (key && typeof key === 'string') key = id.decode(key) | |
| 53 | if (!opts) opts = {} | |
| 54 | ||
| 55 | if (!storage) storage = opts.storage | |
| 56 | ||
| 57 | this[promises] = true | |
| 58 | ||
| 59 | this.storage = null | |
| 60 | this.crypto = opts.crypto || hypercoreCrypto | |
| 61 | this.core = null | |
| 62 | this.replicator = null | |
| 63 | this.encryption = null | |
| 64 | this.extensions = new Map() | |
| 65 | this.cache = createCache(opts.cache) | |
| 66 | ||
| 67 | this.valueEncoding = null | |
| 68 | this.encodeBatch = null | |
| 69 | this.activeRequests = [] | |
| 70 | ||
| 71 | this.id = null | |
| 72 | this.key = key || null | |
| 73 | this.keyPair = opts.keyPair || null | |
| 74 | this.readable = true | |
| 75 | this.writable = false | |
| 76 | this.opened = false | |
| 77 | this.closed = false | |
| 78 | this.snapshotted = !!opts.snapshot | |
| 79 | this.sparse = opts.sparse !== false | |
| 80 | this.sessions = opts._sessions || [this] | |
| 81 | this.autoClose = !!opts.autoClose | |
| 82 | this.onwait = opts.onwait || null | |
| 83 | this.wait = opts.wait !== false | |
| 84 | this.timeout = opts.timeout || 0 | |
| 85 | this.closing = null | |
| 86 | this.opening = null | |
| 87 | ||
| 88 | this._readonly = opts.writable === false | |
| 89 | this._preappend = preappend.bind(this) | |
| 90 | this._snapshot = null | |
| 91 | this._findingPeers = 0 | |
| 92 | this._active = opts.active !== false | |
| 93 | ||
| 94 | this.opening = this._openSession(key, storage, opts) | |
| 95 | this.opening.catch(safetyCatch) | |
| 96 | } | |
| 97 | ||
| 98 | [inspect] (depth, opts) { | |
| 99 | let indent = '' | |
| 100 | if (typeof opts.indentationLvl === 'number') { | |
| 101 | while (indent.length < opts.indentationLvl) indent += ' ' | |
| 102 | } | |
| 103 | ||
| 104 | let peers = '' | |
| 105 | const min = Math.min(this.peers.length, 5) | |
| 106 | ||
| 107 | for (let i = 0; i < min; i++) { | |
| 108 | const peer = this.peers[i] | |
| 109 | ||
| 110 | peers += indent + ' Peer(\n' | |
| 111 | peers += indent + ' remotePublicKey: ' + opts.stylize(toHex(peer.remotePublicKey), 'string') + '\n' | |
| 112 | peers += indent + ' remoteLength: ' + opts.stylize(peer.remoteLength, 'number') + '\n' | |
| 113 | peers += indent + ' remoteFork: ' + opts.stylize(peer.remoteFork, 'number') + '\n' | |
| 114 | peers += indent + ' remoteCanUpgrade: ' + opts.stylize(peer.remoteCanUpgrade, 'boolean') + '\n' | |
| 115 | peers += indent + ' )' + '\n' | |
| 116 | } | |
| 117 | ||
| 118 | if (this.peers.length > 5) { | |
| 119 | peers += indent + ' ... and ' + (this.peers.length - 5) + ' more\n' | |
| 120 | } | |
| 121 | ||
| 122 | if (peers) peers = '[\n' + peers + indent + ' ]' | |
| 123 | else peers = '[ ' + opts.stylize(0, 'number') + ' ]' | |
| 124 | ||
| 125 | return this.constructor.name + '(\n' + | |
| 126 | indent + ' id: ' + opts.stylize(this.id, 'string') + '\n' + | |
| 127 | indent + ' key: ' + opts.stylize(toHex(this.key), 'string') + '\n' + | |
| 128 | indent + ' discoveryKey: ' + opts.stylize(toHex(this.discoveryKey), 'string') + '\n' + | |
| 129 | indent + ' opened: ' + opts.stylize(this.opened, 'boolean') + '\n' + | |
| 130 | indent + ' closed: ' + opts.stylize(this.closed, 'boolean') + '\n' + | |
| 131 | indent + ' snapshotted: ' + opts.stylize(this.snapshotted, 'boolean') + '\n' + | |
| 132 | indent + ' sparse: ' + opts.stylize(this.sparse, 'boolean') + '\n' + | |
| 133 | indent + ' writable: ' + opts.stylize(this.writable, 'boolean') + '\n' + | |
| 134 | indent + ' length: ' + opts.stylize(this.length, 'number') + '\n' + | |
| 135 | indent + ' fork: ' + opts.stylize(this.fork, 'number') + '\n' + | |
| 136 | indent + ' sessions: [ ' + opts.stylize(this.sessions.length, 'number') + ' ]\n' + | |
| 137 | indent + ' activeRequests: [ ' + opts.stylize(this.activeRequests.length, 'number') + ' ]\n' + | |
| 138 | indent + ' peers: ' + peers + '\n' + | |
| 139 | indent + ')' | |
| 140 | } | |
| 141 | ||
| 142 | static MAX_SUGGESTED_BLOCK_SIZE = MAX_SUGGESTED_BLOCK_SIZE | |
| 143 | ||
| 144 | static key (manifest, { compat, version, namespace } = {}) { | |
| 145 | if (b4a.isBuffer(manifest)) manifest = { version, signers: [{ publicKey: manifest, namespace }] } | |
| 146 | return compat ? manifest.signers[0].publicKey : manifestHash(createManifest(manifest)) | |
| 147 | } | |
| 148 | ||
| 149 | static discoveryKey (key) { | |
| 150 | return hypercoreCrypto.discoveryKey(key) | |
| 151 | } | |
| 152 | ||
| 153 | static getProtocolMuxer (stream) { | |
| 154 | return stream.noiseStream.userData | |
| 155 | } | |
| 156 | ||
| 157 | static createProtocolStream (isInitiator, opts = {}) { | |
| 158 | let outerStream = Protomux.isProtomux(isInitiator) | |
| 159 | ? isInitiator.stream | |
| 160 | : isStream(isInitiator) | |
| 161 | ? isInitiator | |
| 162 | : opts.stream | |
| 163 | ||
| 164 | let noiseStream = null | |
| 165 | ||
| 166 | if (outerStream) { | |
| 167 | noiseStream = outerStream.noiseStream | |
| 168 | } else { | |
| 169 | noiseStream = new NoiseSecretStream(isInitiator, null, opts) | |
| 170 | outerStream = noiseStream.rawStream | |
| 171 | } | |
| 172 | if (!noiseStream) throw BAD_ARGUMENT('Invalid stream') | |
| 173 | ||
| 174 | if (!noiseStream.userData) { | |
| 175 | const protocol = Protomux.from(noiseStream) | |
| 176 | ||
| 177 | if (opts.keepAlive !== false) { | |
| 178 | noiseStream.setKeepAlive(5000) | |
| 179 | } | |
| 180 | noiseStream.userData = protocol | |
| 181 | } | |
| 182 | ||
| 183 | if (opts.ondiscoverykey) { | |
| 184 | noiseStream.userData.pair({ protocol: 'hypercore/alpha' }, opts.ondiscoverykey) | |
| 185 | } | |
| 186 | ||
| 187 | return outerStream | |
| 188 | } | |
| 189 | ||
| 190 | static defaultStorage (storage, opts = {}) { | |
| 191 | if (typeof storage !== 'string') { | |
| 192 | if (!isRandomAccessClass(storage)) return storage | |
| 193 | const Cls = storage // just to satisfy standard... | |
| 194 | return name => new Cls(name) | |
| 195 | } | |
| 196 | ||
| 197 | const directory = storage | |
| 198 | const toLock = opts.unlocked ? null : (opts.lock || 'oplog') | |
| 199 | const pool = opts.pool || (opts.poolSize ? RAF.createPool(opts.poolSize) : null) | |
| 200 | const rmdir = !!opts.rmdir | |
| 201 | const writable = opts.writable !== false | |
| 202 | ||
| 203 | return createFile | |
| 204 | ||
| 205 | function createFile (name) { | |
| 206 | const lock = toLock === null ? false : isFile(name, toLock) | |
| 207 | const sparse = isFile(name, 'data') || isFile(name, 'bitfield') || isFile(name, 'tree') | |
| 208 | return new RAF(name, { directory, lock, sparse, pool: lock ? null : pool, rmdir, writable }) | |
| 209 | } | |
| 210 | ||
| 211 | function isFile (name, n) { | |
| 212 | return name === n || name.endsWith('/' + n) | |
| 213 | } | |
| 214 | } | |
| 215 | ||
| 216 | snapshot (opts) { | |
| 217 | return this.session({ ...opts, snapshot: true }) | |
| 218 | } | |
| 219 | ||
| 220 | session (opts = {}) { | |
| 221 | if (this.closing) { | |
| 222 | // This makes the closing logic a lot easier. If this turns out to be a problem | |
| 223 | // in practice, open an issue and we'll try to make a solution for it. | |
| 224 | throw SESSION_CLOSED('Cannot make sessions on a closing core') | |
| 225 | } | |
| 226 | ||
| 227 | const sparse = opts.sparse === false ? false : this.sparse | |
| 228 | const wait = opts.wait === false ? false : this.wait | |
| 229 | const writable = opts.writable === false ? false : !this._readonly | |
| 230 | const onwait = opts.onwait === undefined ? this.onwait : opts.onwait | |
| 231 | const timeout = opts.timeout === undefined ? this.timeout : opts.timeout | |
| 232 | const Clz = opts.class || Hypercore | |
| 233 | const s = new Clz(this.storage, this.key, { | |
| 234 | ...opts, | |
| 235 | sparse, | |
| 236 | wait, | |
| 237 | onwait, | |
| 238 | timeout, | |
| 239 | writable, | |
| 240 | _opening: this.opening, | |
| 241 | _sessions: this.sessions | |
| 242 | }) | |
| 243 | ||
| 244 | s._passCapabilities(this) | |
| 245 | ||
| 246 | // Configure the cache unless explicitly disabled. | |
| 247 | if (opts.cache !== false) { | |
| 248 | s.cache = opts.cache === true || !opts.cache ? this.cache : opts.cache | |
| 249 | } | |
| 250 | ||
| 251 | if (this.opened) ensureEncryption(s, opts) | |
| 252 | this._addSession(s) | |
| 253 | ||
| 254 | return s | |
| 255 | } | |
| 256 | ||
| 257 | _addSession (s) { | |
| 258 | this.sessions.push(s) | |
| 259 | if (this.core) this.core.active++ | |
| 260 | } | |
| 261 | ||
| 262 | async setEncryptionKey (encryptionKey, opts) { | |
| 263 | if (!this.opened) await this.opening | |
| 264 | this.encryption = encryptionKey ? new BlockEncryption(encryptionKey, this.key, { compat: this.core.compat, ...opts }) : null | |
| 265 | } | |
| 266 | ||
| 267 | setKeyPair (keyPair) { | |
| 268 | this.keyPair = keyPair | |
| 269 | this.writable = this._isWritable() | |
| 270 | } | |
| 271 | ||
| 272 | setActive (bool) { | |
| 273 | const active = !!bool | |
| 274 | if (active === this._active || this.closing) return | |
| 275 | this._active = active | |
| 276 | if (!this.opened) return | |
| 277 | this.replicator.updateActivity(this._active ? 1 : -1) | |
| 278 | } | |
| 279 | ||
| 280 | _passCapabilities (o) { | |
| 281 | if (!this.keyPair) this.keyPair = o.keyPair | |
| 282 | this.crypto = o.crypto | |
| 283 | this.id = o.id | |
| 284 | this.key = o.key | |
| 285 | this.core = o.core | |
| 286 | this.replicator = o.replicator | |
| 287 | this.encryption = o.encryption | |
| 288 | this.writable = this._isWritable() | |
| 289 | this.autoClose = o.autoClose | |
| 290 | ||
| 291 | if (this.snapshotted && this.core && !this._snapshot) this._updateSnapshot() | |
| 292 | } | |
| 293 | ||
| 294 | async _openFromExisting (from, opts) { | |
| 295 | if (!from.opened) await from.opening | |
| 296 | ||
| 297 | // includes ourself as well, so the loop below also updates us | |
| 298 | const sessions = this.sessions | |
| 299 | ||
| 300 | for (const s of sessions) { | |
| 301 | s.sessions = from.sessions | |
| 302 | s._passCapabilities(from) | |
| 303 | s._addSession(s) | |
| 304 | } | |
| 305 | ||
| 306 | this.storage = from.storage | |
| 307 | this.replicator.findingPeers += this._findingPeers | |
| 308 | ||
| 309 | ensureEncryption(this, opts) | |
| 310 | ||
| 311 | // we need to manually fwd the encryption cap as the above removes it potentially | |
| 312 | if (this.encryption && !from.encryption) { | |
| 313 | for (const s of sessions) s.encryption = this.encryption | |
| 314 | } | |
| 315 | } | |
| 316 | ||
| 317 | async _openSession (key, storage, opts) { | |
| 318 | const isFirst = !opts._opening | |
| 319 | ||
| 320 | if (!isFirst) { | |
| 321 | await opts._opening | |
| 322 | } | |
| 323 | if (opts.preload) opts = { ...opts, ...(await this._retryPreload(opts.preload)) } | |
| 324 | if (this.cache === null && opts.cache) this.cache = createCache(opts.cache) | |
| 325 | ||
| 326 | if (isFirst) { | |
| 327 | await this._openCapabilities(key, storage, opts) | |
| 328 | ||
| 329 | // check we are the actual root and not a opts.from session | |
| 330 | if (!opts.from) { | |
| 331 | // Only the root session should pass capabilities to other sessions. | |
| 332 | for (let i = 0; i < this.sessions.length; i++) { | |
| 333 | const s = this.sessions[i] | |
| 334 | if (s !== this) s._passCapabilities(this) | |
| 335 | } | |
| 336 | } | |
| 337 | } else { | |
| 338 | ensureEncryption(this, opts) | |
| 339 | } | |
| 340 | ||
| 341 | if (opts.manifest && !this.core.header.manifest) { | |
| 342 | await this.core.setManifest(opts.manifest) | |
| 343 | } | |
| 344 | ||
| 345 | this.writable = this._isWritable() | |
| 346 | ||
| 347 | if (opts.valueEncoding) { | |
| 348 | this.valueEncoding = c.from(opts.valueEncoding) | |
| 349 | } | |
| 350 | if (opts.encodeBatch) { | |
| 351 | this.encodeBatch = opts.encodeBatch | |
| 352 | } | |
| 353 | ||
| 354 | // Start continous replication if not in sparse mode. | |
| 355 | if (!this.sparse) this.download({ start: 0, end: -1 }) | |
| 356 | ||
| 357 | // This is a hidden option that's only used by Corestore. | |
| 358 | // It's required so that corestore can load a name from userData before 'ready' is emitted. | |
| 359 | if (opts._preready) await opts._preready(this) | |
| 360 | ||
| 361 | this.replicator.updateActivity(this._active ? 1 : 0) | |
| 362 | ||
| 363 | this.opened = true | |
| 364 | this.emit('ready') | |
| 365 | } | |
| 366 | ||
| 367 | async _retryPreload (preload) { | |
| 368 | while (true) { // TODO: better long term fix is allowing lib/core.js creation from the outside... | |
| 369 | const result = await preload() | |
| 370 | const from = result && result.from | |
| 371 | if (from) { | |
| 372 | if (!from.opened) await from.ready() | |
| 373 | if (from.closing) continue | |
| 374 | } | |
| 375 | return result | |
| 376 | } | |
| 377 | } | |
| 378 | ||
| 379 | async _openCapabilities (key, storage, opts) { | |
| 380 | if (opts.from) return this._openFromExisting(opts.from, opts) | |
| 381 | ||
| 382 | const unlocked = !!opts.unlocked | |
| 383 | this.storage = Hypercore.defaultStorage(opts.storage || storage, { unlocked, writable: !unlocked }) | |
| 384 | ||
| 385 | this.core = await Core.open(this.storage, { | |
| 386 | compat: opts.compat, | |
| 387 | force: opts.force, | |
| 388 | sessions: this.sessions, | |
| 389 | createIfMissing: opts.createIfMissing, | |
| 390 | readonly: unlocked, | |
| 391 | overwrite: opts.overwrite, | |
| 392 | key, | |
| 393 | keyPair: opts.keyPair, | |
| 394 | crypto: this.crypto, | |
| 395 | legacy: opts.legacy, | |
| 396 | manifest: opts.manifest, | |
| 397 | globalCache: opts.globalCache || null, // This is a temp option, not to be relied on unless you know what you are doing (no semver guarantees) | |
| 398 | onupdate: this._oncoreupdate.bind(this), | |
| 399 | onconflict: this._oncoreconflict.bind(this) | |
| 400 | }) | |
| 401 | ||
| 402 | if (opts.userData) { | |
| 403 | for (const [key, value] of Object.entries(opts.userData)) { | |
| 404 | await this.core.userData(key, value) | |
| 405 | } | |
| 406 | } | |
| 407 | ||
| 408 | this.key = this.core.header.key | |
| 409 | this.keyPair = this.core.header.keyPair | |
| 410 | this.id = z32.encode(this.key) | |
| 411 | ||
| 412 | this.replicator = new Replicator(this.core, this.key, { | |
| 413 | eagerUpgrade: true, | |
| 414 | notDownloadingLinger: opts.notDownloadingLinger, | |
| 415 | allowFork: opts.allowFork !== false, | |
| 416 | inflightRange: opts.inflightRange, | |
| 417 | onpeerupdate: this._onpeerupdate.bind(this), | |
| 418 | onupload: this._onupload.bind(this), | |
| 419 | oninvalid: this._oninvalid.bind(this) | |
| 420 | }) | |
| 421 | ||
| 422 | this.replicator.findingPeers += this._findingPeers | |
| 423 | ||
| 424 | if (!this.encryption && opts.encryptionKey) { | |
| 425 | this.encryption = new BlockEncryption(opts.encryptionKey, this.key, { compat: this.core.compat, isBlockKey: opts.isBlockKey }) | |
| 426 | } | |
| 427 | } | |
| 428 | ||
| 429 | _getSnapshot () { | |
| 430 | if (this.sparse) { | |
| 431 | return { | |
| 432 | length: this.core.tree.length, | |
| 433 | byteLength: this.core.tree.byteLength, | |
| 434 | fork: this.core.tree.fork, | |
| 435 | compatLength: this.core.tree.length | |
| 436 | } | |
| 437 | } | |
| 438 | ||
| 439 | return { | |
| 440 | length: this.core.header.hints.contiguousLength, | |
| 441 | byteLength: 0, | |
| 442 | fork: this.core.tree.fork, | |
| 443 | compatLength: this.core.header.hints.contiguousLength | |
| 444 | } | |
| 445 | } | |
| 446 | ||
| 447 | _updateSnapshot () { | |
| 448 | const prev = this._snapshot | |
| 449 | const next = this._snapshot = this._getSnapshot() | |
| 450 | ||
| 451 | if (!prev) return true | |
| 452 | return prev.length !== next.length || prev.fork !== next.fork | |
| 453 | } | |
| 454 | ||
| 455 | _isWritable () { | |
| 456 | return !this._readonly && !!(this.keyPair && this.keyPair.secretKey) | |
| 457 | } | |
| 458 | ||
| 459 | close (err) { | |
| 460 | if (this.closing) return this.closing | |
| 461 | this.closing = this._close(err || null) | |
| 462 | return this.closing | |
| 463 | } | |
| 464 | ||
| 465 | async _close (err) { | |
| 466 | if (this.opened === false) await this.opening | |
| 467 | ||
| 468 | const i = this.sessions.indexOf(this) | |
| 469 | if (i === -1) return | |
| 470 | ||
| 471 | this.sessions.splice(i, 1) | |
| 472 | this.core.active-- | |
| 473 | this.readable = false | |
| 474 | this.writable = false | |
| 475 | this.closed = true | |
| 476 | this.opened = false | |
| 477 | ||
| 478 | const gc = [] | |
| 479 | for (const ext of this.extensions.values()) { | |
| 480 | if (ext.session === this) gc.push(ext) | |
| 481 | } | |
| 482 | for (const ext of gc) ext.destroy() | |
| 483 | ||
| 484 | if (this.replicator !== null) { | |
| 485 | this.replicator.findingPeers -= this._findingPeers | |
| 486 | this.replicator.clearRequests(this.activeRequests, err) | |
| 487 | this.replicator.updateActivity(this._active ? -1 : 0) | |
| 488 | } | |
| 489 | ||
| 490 | this._findingPeers = 0 | |
| 491 | ||
| 492 | if (this.sessions.length || this.core.active > 0) { | |
| 493 | // if this is the last session and we are auto closing, trigger that first to enforce error handling | |
| 494 | if (this.sessions.length === 1 && this.core.active === 1 && this.autoClose) await this.sessions[0].close(err) | |
| 495 | // emit "fake" close as this is a session | |
| 496 | this.emit('close', false) | |
| 497 | return | |
| 498 | } | |
| 499 | ||
| 500 | if (this.replicator !== null) { | |
| 501 | await this.replicator.destroy() | |
| 502 | } | |
| 503 | ||
| 504 | await this.core.close() | |
| 505 | ||
| 506 | this.emit('close', true) | |
| 507 | } | |
| 508 | ||
| 509 | replicate (isInitiator, opts = {}) { | |
| 510 | // Only limitation here is that ondiscoverykey doesn't work atm when passing a muxer directly, | |
| 511 | // because it doesn't really make a lot of sense. | |
| 512 | if (Protomux.isProtomux(isInitiator)) return this._attachToMuxer(isInitiator, opts) | |
| 513 | ||
| 514 | // if same stream is passed twice, ignore the 2nd one before we make sessions etc | |
| 515 | if (isStream(isInitiator) && this._isAttached(isInitiator)) return isInitiator | |
| 516 | ||
| 517 | const protocolStream = Hypercore.createProtocolStream(isInitiator, opts) | |
| 518 | const noiseStream = protocolStream.noiseStream | |
| 519 | const protocol = noiseStream.userData | |
| 520 | const useSession = !!opts.session | |
| 521 | ||
| 522 | this._attachToMuxer(protocol, useSession) | |
| 523 | ||
| 524 | return protocolStream | |
| 525 | } | |
| 526 | ||
| 527 | _isAttached (stream) { | |
| 528 | return stream.userData && this.replicator && this.replicator.attached(stream.userData) | |
| 529 | } | |
| 530 | ||
| 531 | _attachToMuxer (mux, useSession) { | |
| 532 | if (this.opened) { | |
| 533 | this._attachToMuxerOpened(mux, useSession) | |
| 534 | } else { | |
| 535 | this.opening.then(this._attachToMuxerOpened.bind(this, mux, useSession), mux.destroy.bind(mux)) | |
| 536 | } | |
| 537 | ||
| 538 | return mux | |
| 539 | } | |
| 540 | ||
| 541 | _attachToMuxerOpened (mux, useSession) { | |
| 542 | // If the user wants to, we can make this replication run in a session | |
| 543 | // that way the core wont close "under them" during replication | |
| 544 | this.replicator.attachTo(mux, useSession) | |
| 545 | } | |
| 546 | ||
| 547 | get discoveryKey () { | |
| 548 | return this.replicator === null ? null : this.replicator.discoveryKey | |
| 549 | } | |
| 550 | ||
| 551 | get manifest () { | |
| 552 | return this.core === null ? null : this.core.header.manifest | |
| 553 | } | |
| 554 | ||
| 555 | get length () { | |
| 556 | if (this._snapshot) return this._snapshot.length | |
| 557 | if (this.core === null) return 0 | |
| 558 | if (!this.sparse) return this.contiguousLength | |
| 559 | return this.core.tree.length | |
| 560 | } | |
| 561 | ||
| 562 | get signedLength () { | |
| 563 | return this.length | |
| 564 | } | |
| 565 | ||
| 566 | get indexedLength () { | |
| 567 | return this.length | |
| 568 | } | |
| 569 | ||
| 570 | /** | |
| 571 | * Deprecated. Use `const { byteLength } = await core.info()`. | |
| 572 | */ | |
| 573 | get byteLength () { | |
| 574 | if (this._snapshot) return this._snapshot.byteLength | |
| 575 | if (this.core === null) return 0 | |
| 576 | if (!this.sparse) return this.contiguousByteLength | |
| 577 | return this.core.tree.byteLength - (this.core.tree.length * this.padding) | |
| 578 | } | |
| 579 | ||
| 580 | get contiguousLength () { | |
| 581 | return this.core === null ? 0 : Math.min(this.core.tree.length, this.core.header.hints.contiguousLength) | |
| 582 | } | |
| 583 | ||
| 584 | get contiguousByteLength () { | |
| 585 | return 0 | |
| 586 | } | |
| 587 | ||
| 588 | get fork () { | |
| 589 | return this.core === null ? 0 : this.core.tree.fork | |
| 590 | } | |
| 591 | ||
| 592 | get peers () { | |
| 593 | return this.replicator === null ? [] : this.replicator.peers | |
| 594 | } | |
| 595 | ||
| 596 | get encryptionKey () { | |
| 597 | return this.encryption && this.encryption.key | |
| 598 | } | |
| 599 | ||
| 600 | get padding () { | |
| 601 | return this.encryption === null ? 0 : this.encryption.padding | |
| 602 | } | |
| 603 | ||
| 604 | get globalCache () { | |
| 605 | return this.core && this.core.globalCache | |
| 606 | } | |
| 607 | ||
| 608 | ready () { | |
| 609 | return this.opening | |
| 610 | } | |
| 611 | ||
| 612 | _onupload (index, value, from) { | |
| 613 | const byteLength = value.byteLength - this.padding | |
| 614 | ||
| 615 | for (let i = 0; i < this.sessions.length; i++) { | |
| 616 | this.sessions[i].emit('upload', index, byteLength, from) | |
| 617 | } | |
| 618 | } | |
| 619 | ||
| 620 | _oninvalid (err, req, res, from) { | |
| 621 | for (let i = 0; i < this.sessions.length; i++) { | |
| 622 | this.sessions[i].emit('verification-error', err, req, res, from) | |
| 623 | } | |
| 624 | } | |
| 625 | ||
| 626 | async _oncoreconflict (proof, from) { | |
| 627 | await this.replicator.onconflict(from) | |
| 628 | ||
| 629 | for (const s of this.sessions) s.emit('conflict', proof.upgrade.length, proof.fork, proof) | |
| 630 | ||
| 631 | const err = new Error('Two conflicting signatures exist for length ' + proof.upgrade.length) | |
| 632 | await this._closeAllSessions(err) | |
| 633 | } | |
| 634 | ||
| 635 | async _closeAllSessions (err) { | |
| 636 | // this.sessions modifies itself when a session closes | |
| 637 | // This way we ensure we indeed iterate over all sessions | |
| 638 | const sessions = [...this.sessions] | |
| 639 | ||
| 640 | const all = [] | |
| 641 | for (const s of sessions) all.push(s.close(err)) | |
| 642 | await Promise.allSettled(all) | |
| 643 | } | |
| 644 | ||
| 645 | _oncoreupdate (status, bitfield, value, from) { | |
| 646 | if (status !== 0) { | |
| 647 | const truncatedNonSparse = (status & 0b1000) !== 0 | |
| 648 | const appendedNonSparse = (status & 0b0100) !== 0 | |
| 649 | const truncated = (status & 0b0010) !== 0 | |
| 650 | const appended = (status & 0b0001) !== 0 | |
| 651 | ||
| 652 | if (truncated) { | |
| 653 | this.replicator.ontruncate(bitfield.start, bitfield.length) | |
| 654 | } | |
| 655 | ||
| 656 | if ((status & 0b10011) !== 0) { | |
| 657 | this.replicator.onupgrade() | |
| 658 | } | |
| 659 | ||
| 660 | if (status & 0b10000) { | |
| 661 | for (let i = 0; i < this.sessions.length; i++) { | |
| 662 | const s = this.sessions[i] | |
| 663 | ||
| 664 | if (s.encryption && s.encryption.compat !== this.core.compat) { | |
| 665 | s.encryption = new BlockEncryption(s.encryption.key, this.key, { compat: this.core.compat, isBlockKey: s.encryption.isBlockKey }) | |
| 666 | } | |
| 667 | } | |
| 668 | ||
| 669 | for (let i = 0; i < this.sessions.length; i++) { | |
| 670 | this.sessions[i].emit('manifest') | |
| 671 | } | |
| 672 | } | |
| 673 | ||
| 674 | for (let i = 0; i < this.sessions.length; i++) { | |
| 675 | const s = this.sessions[i] | |
| 676 | ||
| 677 | if (truncated) { | |
| 678 | if (s.cache) s.cache.clear() | |
| 679 | ||
| 680 | // If snapshotted, make sure to update our compat so we can fail gets | |
| 681 | if (s._snapshot && bitfield.start < s._snapshot.compatLength) s._snapshot.compatLength = bitfield.start | |
| 682 | } | |
| 683 | ||
| 684 | if (s.sparse ? truncated : truncatedNonSparse) { | |
| 685 | s.emit('truncate', bitfield.start, this.core.tree.fork) | |
| 686 | } | |
| 687 | ||
| 688 | // For sparse sessions, immediately emit appends. If non-sparse, emit if contig length has updated | |
| 689 | if (s.sparse ? appended : appendedNonSparse) { | |
| 690 | s.emit('append') | |
| 691 | } | |
| 692 | } | |
| 693 | ||
| 694 | const contig = this.core.header.hints.contiguousLength | |
| 695 | ||
| 696 | // When the contig length catches up, broadcast the non-sparse length to peers | |
| 697 | if (appendedNonSparse && contig === this.core.tree.length) { | |
| 698 | for (const peer of this.peers) { | |
| 699 | if (peer.broadcastedNonSparse) continue | |
| 700 | ||
| 701 | peer.broadcastRange(0, contig) | |
| 702 | peer.broadcastedNonSparse = true | |
| 703 | } | |
| 704 | } | |
| 705 | } | |
| 706 | ||
| 707 | if (bitfield) { | |
| 708 | this.replicator.onhave(bitfield.start, bitfield.length, bitfield.drop) | |
| 709 | } | |
| 710 | ||
| 711 | if (value) { | |
| 712 | const byteLength = value.byteLength - this.padding | |
| 713 | ||
| 714 | for (let i = 0; i < this.sessions.length; i++) { | |
| 715 | this.sessions[i].emit('download', bitfield.start, byteLength, from) | |
| 716 | } | |
| 717 | } | |
| 718 | } | |
| 719 | ||
| 720 | _onpeerupdate (added, peer) { | |
| 721 | const name = added ? 'peer-add' : 'peer-remove' | |
| 722 | ||
| 723 | for (let i = 0; i < this.sessions.length; i++) { | |
| 724 | this.sessions[i].emit(name, peer) | |
| 725 | ||
| 726 | if (added) { | |
| 727 | for (const ext of this.sessions[i].extensions.values()) { | |
| 728 | peer.extensions.set(ext.name, ext) | |
| 729 | } | |
| 730 | } | |
| 731 | } | |
| 732 | } | |
| 733 | ||
| 734 | async setUserData (key, value, { flush = false } = {}) { | |
| 735 | if (this.opened === false) await this.opening | |
| 736 | return this.core.userData(key, value, flush) | |
| 737 | } | |
| 738 | ||
| 739 | async getUserData (key) { | |
| 740 | if (this.opened === false) await this.opening | |
| 741 | for (const { key: savedKey, value } of this.core.header.userData) { | |
| 742 | if (key === savedKey) return value | |
| 743 | } | |
| 744 | return null | |
| 745 | } | |
| 746 | ||
| 747 | createTreeBatch () { | |
| 748 | return this.core.tree.batch() | |
| 749 | } | |
| 750 | ||
| 751 | findingPeers () { | |
| 752 | this._findingPeers++ | |
| 753 | if (this.replicator !== null && !this.closing) this.replicator.findingPeers++ | |
| 754 | ||
| 755 | let once = true | |
| 756 | ||
| 757 | return () => { | |
| 758 | if (this.closing || !once) return | |
| 759 | once = false | |
| 760 | this._findingPeers-- | |
| 761 | if (this.replicator !== null && --this.replicator.findingPeers === 0) { | |
| 762 | this.replicator.updateAll() | |
| 763 | } | |
| 764 | } | |
| 765 | } | |
| 766 | ||
| 767 | async info (opts) { | |
| 768 | if (this.opened === false) await this.opening | |
| 769 | ||
| 770 | return Info.from(this, opts) | |
| 771 | } | |
| 772 | ||
| 773 | async update (opts) { | |
| 774 | if (this.opened === false) await this.opening | |
| 775 | if (this.closing !== null) return false | |
| 776 | ||
| 777 | if (this.writable && (!opts || opts.force !== true)) { | |
| 778 | if (!this.snapshotted) return false | |
| 779 | return this._updateSnapshot() | |
| 780 | } | |
| 781 | ||
| 782 | const remoteWait = this._shouldWait(opts, this.replicator.findingPeers > 0) | |
| 783 | ||
| 784 | let upgraded = false | |
| 785 | ||
| 786 | if (await this.replicator.applyPendingReorg()) { | |
| 787 | upgraded = true | |
| 788 | } | |
| 789 | ||
| 790 | if (!upgraded && remoteWait) { | |
| 791 | const activeRequests = (opts && opts.activeRequests) || this.activeRequests | |
| 792 | const req = this.replicator.addUpgrade(activeRequests) | |
| 793 | ||
| 794 | upgraded = await req.promise | |
| 795 | } | |
| 796 | ||
| 797 | if (!upgraded) return false | |
| 798 | if (this.snapshotted) return this._updateSnapshot() | |
| 799 | return true | |
| 800 | } | |
| 801 | ||
| 802 | batch ({ checkout = -1, autoClose = true, session = true, restore = false, clear = false } = {}) { | |
| 803 | return new Batch(session ? this.session() : this, checkout, autoClose, restore, clear) | |
| 804 | } | |
| 805 | ||
| 806 | async seek (bytes, opts) { | |
| 807 | if (this.opened === false) await this.opening | |
| 808 | if (!isValidIndex(bytes)) throw ASSERTION('seek is invalid') | |
| 809 | ||
| 810 | const tree = (opts && opts.tree) || this.core.tree | |
| 811 | const s = tree.seek(bytes, this.padding) | |
| 812 | ||
| 813 | const offset = await s.update() | |
| 814 | if (offset) return offset | |
| 815 | ||
| 816 | if (this.closing !== null) throw SESSION_CLOSED() | |
| 817 | ||
| 818 | if (!this._shouldWait(opts, this.wait)) return null | |
| 819 | ||
| 820 | const activeRequests = (opts && opts.activeRequests) || this.activeRequests | |
| 821 | const req = this.replicator.addSeek(activeRequests, s) | |
| 822 | ||
| 823 | const timeout = opts && opts.timeout !== undefined ? opts.timeout : this.timeout | |
| 824 | if (timeout) req.context.setTimeout(req, timeout) | |
| 825 | ||
| 826 | return req.promise | |
| 827 | } | |
| 828 | ||
| 829 | async has (start, end = start + 1) { | |
| 830 | if (this.opened === false) await this.opening | |
| 831 | if (!isValidIndex(start) || !isValidIndex(end)) throw ASSERTION('has range is invalid') | |
| 832 | ||
| 833 | if (end === start + 1) return this.core.bitfield.get(start) | |
| 834 | ||
| 835 | const i = this.core.bitfield.firstUnset(start) | |
| 836 | return i === -1 || i >= end | |
| 837 | } | |
| 838 | ||
| 839 | async get (index, opts) { | |
| 840 | if (this.opened === false) await this.opening | |
| 841 | if (!isValidIndex(index)) throw ASSERTION('block index is invalid') | |
| 842 | ||
| 843 | if (this.closing !== null) throw SESSION_CLOSED() | |
| 844 | if (this._snapshot !== null && index >= this._snapshot.compatLength) throw SNAPSHOT_NOT_AVAILABLE() | |
| 845 | ||
| 846 | const encoding = (opts && opts.valueEncoding && c.from(opts.valueEncoding)) || this.valueEncoding | |
| 847 | ||
| 848 | let req = this.cache && this.cache.get(index) | |
| 849 | if (!req) req = this._get(index, opts) | |
| 850 | ||
| 851 | let block = await req | |
| 852 | if (!block) return null | |
| 853 | ||
| 854 | if (opts && opts.raw) return block | |
| 855 | ||
| 856 | if (this.encryption && (!opts || opts.decrypt !== false)) { | |
| 857 | // Copy the block as it might be shared with other sessions. | |
| 858 | block = b4a.from(block) | |
| 859 | ||
| 860 | this.encryption.decrypt(index, block) | |
| 861 | } | |
| 862 | ||
| 863 | return this._decode(encoding, block) | |
| 864 | } | |
| 865 | ||
| 866 | async clear (start, end = start + 1, opts) { | |
| 867 | if (this.opened === false) await this.opening | |
| 868 | if (this.closing !== null) throw SESSION_CLOSED() | |
| 869 | ||
| 870 | if (typeof end === 'object') { | |
| 871 | opts = end | |
| 872 | end = start + 1 | |
| 873 | } | |
| 874 | ||
| 875 | if (!isValidIndex(start) || !isValidIndex(end)) throw ASSERTION('clear range is invalid') | |
| 876 | ||
| 877 | const cleared = (opts && opts.diff) ? { blocks: 0 } : null | |
| 878 | ||
| 879 | if (start >= end) return cleared | |
| 880 | if (start >= this.length) return cleared | |
| 881 | ||
| 882 | await this.core.clear(start, end, cleared) | |
| 883 | ||
| 884 | return cleared | |
| 885 | } | |
| 886 | ||
| 887 | async purge () { | |
| 888 | await this._closeAllSessions(null) | |
| 889 | await this.core.purge() | |
| 890 | } | |
| 891 | ||
| 892 | async _get (index, opts) { | |
| 893 | let block | |
| 894 | ||
| 895 | if (this.core.bitfield.get(index)) { | |
| 896 | const tree = (opts && opts.tree) || this.core.tree | |
| 897 | block = this.core.blocks.get(index, tree) | |
| 898 | ||
| 899 | if (this.cache) this.cache.set(index, block) | |
| 900 | } else { | |
| 901 | if (!this._shouldWait(opts, this.wait)) return null | |
| 902 | ||
| 903 | if (opts && opts.onwait) opts.onwait(index, this) | |
| 904 | if (this.onwait) this.onwait(index, this) | |
| 905 | ||
| 906 | const activeRequests = (opts && opts.activeRequests) || this.activeRequests | |
| 907 | ||
| 908 | const req = this.replicator.addBlock(activeRequests, index) | |
| 909 | req.snapshot = index < this.length | |
| 910 | ||
| 911 | const timeout = opts && opts.timeout !== undefined ? opts.timeout : this.timeout | |
| 912 | if (timeout) req.context.setTimeout(req, timeout) | |
| 913 | ||
| 914 | block = this._cacheOnResolve(index, req.promise, this.core.tree.fork) | |
| 915 | } | |
| 916 | ||
| 917 | return block | |
| 918 | } | |
| 919 | ||
| 920 | async _cacheOnResolve (index, req, fork) { | |
| 921 | const resolved = await req | |
| 922 | ||
| 923 | // Unslab only when it takes up less then half the slab | |
| 924 | const block = resolved !== null && 2 * resolved.byteLength < resolved.buffer.byteLength | |
| 925 | ? unslab(resolved) | |
| 926 | : resolved | |
| 927 | ||
| 928 | if (this.cache && fork === this.core.tree.fork) { | |
| 929 | this.cache.set(index, Promise.resolve(block)) | |
| 930 | } | |
| 931 | ||
| 932 | return block | |
| 933 | } | |
| 934 | ||
| 935 | _shouldWait (opts, defaultValue) { | |
| 936 | if (opts) { | |
| 937 | if (opts.wait === false) return false | |
| 938 | if (opts.wait === true) return true | |
| 939 | } | |
| 940 | return defaultValue | |
| 941 | } | |
| 942 | ||
| 943 | createReadStream (opts) { | |
| 944 | return new ReadStream(this, opts) | |
| 945 | } | |
| 946 | ||
| 947 | createWriteStream (opts) { | |
| 948 | return new WriteStream(this, opts) | |
| 949 | } | |
| 950 | ||
| 951 | createByteStream (opts) { | |
| 952 | return new ByteStream(this, opts) | |
| 953 | } | |
| 954 | ||
| 955 | download (range) { | |
| 956 | const req = this._download(range) | |
| 957 | ||
| 958 | // do not crash in the background... | |
| 959 | req.catch(safetyCatch) | |
| 960 | ||
| 961 | return new Download(req) | |
| 962 | } | |
| 963 | ||
| 964 | async _download (range) { | |
| 965 | if (this.opened === false) await this.opening | |
| 966 | ||
| 967 | const activeRequests = (range && range.activeRequests) || this.activeRequests | |
| 968 | return this.replicator.addRange(activeRequests, range) | |
| 969 | } | |
| 970 | ||
| 971 | // TODO: get rid of this / deprecate it? | |
| 972 | undownload (range) { | |
| 973 | range.destroy(null) | |
| 974 | } | |
| 975 | ||
| 976 | // TODO: get rid of this / deprecate it? | |
| 977 | cancel (request) { | |
| 978 | // Do nothing for now | |
| 979 | } | |
| 980 | ||
| 981 | async truncate (newLength = 0, opts = {}) { | |
| 982 | if (this.opened === false) await this.opening | |
| 983 | ||
| 984 | const { | |
| 985 | fork = this.core.tree.fork + 1, | |
| 986 | keyPair = this.keyPair, | |
| 987 | signature = null | |
| 988 | } = typeof opts === 'number' ? { fork: opts } : opts | |
| 989 | ||
| 990 | const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey)) | |
| 991 | if (writable === false && (newLength > 0 || fork !== this.core.tree.fork)) throw SESSION_NOT_WRITABLE() | |
| 992 | ||
| 993 | await this.core.truncate(newLength, fork, { keyPair, signature }) | |
| 994 | ||
| 995 | // TODO: Should propagate from an event triggered by the oplog | |
| 996 | this.replicator.updateAll() | |
| 997 | } | |
| 998 | ||
| 999 | async append (blocks, opts = {}) { | |
| 1000 | if (this.opened === false) await this.opening | |
| 1001 | ||
| 1002 | const { keyPair = this.keyPair, signature = null } = opts | |
| 1003 | const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey)) | |
| 1004 | ||
| 1005 | if (writable === false) throw SESSION_NOT_WRITABLE() | |
| 1006 | ||
| 1007 | blocks = Array.isArray(blocks) ? blocks : [blocks] | |
| 1008 | ||
| 1009 | const preappend = this.encryption && this._preappend | |
| 1010 | ||
| 1011 | const buffers = this.encodeBatch !== null ? this.encodeBatch(blocks) : new Array(blocks.length) | |
| 1012 | ||
| 1013 | if (this.encodeBatch === null) { | |
| 1014 | for (let i = 0; i < blocks.length; i++) { | |
| 1015 | buffers[i] = this._encode(this.valueEncoding, blocks[i]) | |
| 1016 | } | |
| 1017 | } | |
| 1018 | for (const b of buffers) { | |
| 1019 | if (b.byteLength > MAX_SUGGESTED_BLOCK_SIZE) { | |
| 1020 | throw BAD_ARGUMENT('Appended block exceeds the maximum suggested block size') | |
| 1021 | } | |
| 1022 | } | |
| 1023 | ||
| 1024 | return this.core.append(buffers, { keyPair, signature, preappend }) | |
| 1025 | } | |
| 1026 | ||
| 1027 | async treeHash (length) { | |
| 1028 | if (length === undefined) { | |
| 1029 | await this.ready() | |
| 1030 | length = this.core.tree.length | |
| 1031 | } | |
| 1032 | ||
| 1033 | const roots = await this.core.tree.getRoots(length) | |
| 1034 | return this.crypto.tree(roots) | |
| 1035 | } | |
| 1036 | ||
| 1037 | registerExtension (name, handlers = {}) { | |
| 1038 | if (this.extensions.has(name)) { | |
| 1039 | const ext = this.extensions.get(name) | |
| 1040 | ext.handlers = handlers | |
| 1041 | ext.encoding = c.from(handlers.encoding || c.buffer) | |
| 1042 | ext.session = this | |
| 1043 | return ext | |
| 1044 | } | |
| 1045 | ||
| 1046 | const ext = { | |
| 1047 | name, | |
| 1048 | handlers, | |
| 1049 | encoding: c.from(handlers.encoding || c.buffer), | |
| 1050 | session: this, | |
| 1051 | send (message, peer) { | |
| 1052 | const buffer = c.encode(this.encoding, message) | |
| 1053 | peer.extension(name, buffer) | |
| 1054 | }, | |
| 1055 | broadcast (message) { | |
| 1056 | const buffer = c.encode(this.encoding, message) | |
| 1057 | for (const peer of this.session.peers) { | |
| 1058 | peer.extension(name, buffer) | |
| 1059 | } | |
| 1060 | }, | |
| 1061 | destroy () { | |
| 1062 | for (const peer of this.session.peers) { | |
| 1063 | if (peer.extensions.get(name) === ext) peer.extensions.delete(name) | |
| 1064 | } | |
| 1065 | this.session.extensions.delete(name) | |
| 1066 | }, | |
| 1067 | _onmessage (state, peer) { | |
| 1068 | const m = this.encoding.decode(state) | |
| 1069 | if (this.handlers.onmessage) this.handlers.onmessage(m, peer) | |
| 1070 | } | |
| 1071 | } | |
| 1072 | ||
| 1073 | this.extensions.set(name, ext) | |
| 1074 | for (const peer of this.peers) { | |
| 1075 | peer.extensions.set(name, ext) | |
| 1076 | } | |
| 1077 | ||
| 1078 | return ext | |
| 1079 | } | |
| 1080 | ||
| 1081 | _encode (enc, val) { | |
| 1082 | const state = { start: this.padding, end: this.padding, buffer: null } | |
| 1083 | ||
| 1084 | if (b4a.isBuffer(val)) { | |
| 1085 | if (state.start === 0) return val | |
| 1086 | state.end += val.byteLength | |
| 1087 | } else if (enc) { | |
| 1088 | enc.preencode(state, val) | |
| 1089 | } else { | |
| 1090 | val = b4a.from(val) | |
| 1091 | if (state.start === 0) return val | |
| 1092 | state.end += val.byteLength | |
| 1093 | } | |
| 1094 | ||
| 1095 | state.buffer = b4a.allocUnsafe(state.end) | |
| 1096 | ||
| 1097 | if (enc) enc.encode(state, val) | |
| 1098 | else state.buffer.set(val, state.start) | |
| 1099 | ||
| 1100 | return state.buffer | |
| 1101 | } | |
| 1102 | ||
| 1103 | _decode (enc, block) { | |
| 1104 | if (this.padding) block = block.subarray(this.padding) | |
| 1105 | try { | |
| 1106 | if (enc) return c.decode(enc, block) | |
| 1107 | } catch { | |
| 1108 | throw DECODING_ERROR() | |
| 1109 | } | |
| 1110 | return block | |
| 1111 | } | |
| 1112 | } | |
| 1113 | ||
| 1114 | function isStream (s) { | |
| 1115 | return typeof s === 'object' && s && typeof s.pipe === 'function' | |
| 1116 | } | |
| 1117 | ||
| 1118 | function isRandomAccessClass (fn) { | |
| 1119 | return !!(typeof fn === 'function' && fn.prototype && typeof fn.prototype.open === 'function') | |
| 1120 | } | |
| 1121 | ||
| 1122 | function toHex (buf) { | |
| 1123 | return buf && b4a.toString(buf, 'hex') | |
| 1124 | } | |
| 1125 | ||
| 1126 | function preappend (blocks) { | |
| 1127 | const offset = this.core.tree.length | |
| 1128 | const fork = this.core.tree.fork | |
| 1129 | ||
| 1130 | for (let i = 0; i < blocks.length; i++) { | |
| 1131 | this.encryption.encrypt(offset + i, blocks[i], fork) | |
| 1132 | } | |
| 1133 | } | |
| 1134 | ||
| 1135 | function ensureEncryption (core, opts) { | |
| 1136 | if (!opts.encryptionKey) return | |
| 1137 | // Only override the block encryption if it's either not already set or if | |
| 1138 | // the caller provided a different key. | |
| 1139 | if (core.encryption && b4a.equals(core.encryption.key, opts.encryptionKey) && core.encryption.compat === core.core.compat) return | |
| 1140 | core.encryption = new BlockEncryption(opts.encryptionKey, core.key, { compat: core.core ? core.core.compat : true, isBlockKey: opts.isBlockKey }) | |
| 1141 | } | |
| 1142 | ||
| 1143 | function createCache (cache) { | |
| 1144 | return cache === true ? new Xache({ maxSize: 65536, maxAge: 0 }) : (cache || null) | |
| 1145 | } | |
| 1146 | ||
| 1147 | function isValidIndex (index) { | |
| 1148 | return index === 0 || index > 0 | |
| 1149 | } |
| … | … | @@ -0,0 +1,129 @@ |
| 1 | const hypercoreCrypto = require('hypercore-crypto') | |
| 2 | const flat = require('flat-tree') | |
| 3 | const c = require('compact-encoding') | |
| 4 | const b4a = require('b4a') | |
| 5 | ||
| 6 | const empty = b4a.alloc(32) | |
| 7 | ||
| 8 | // this is optimised for speed over mem atm | |
| 9 | // can be tweaked in the future | |
| 10 | ||
| 11 | module.exports = async function auditCore (core) { | |
| 12 | const corrections = { | |
| 13 | tree: 0, | |
| 14 | blocks: 0 | |
| 15 | } | |
| 16 | ||
| 17 | const length = core.header.tree.length | |
| 18 | ||
| 19 | const data = await readFullStorage(core.blocks.storage) | |
| 20 | const tree = await readFullStorage(core.tree.storage) | |
| 21 | ||
| 22 | const valid = new Uint8Array(Math.ceil(tree.byteLength / 40)) | |
| 23 | const stack = [] | |
| 24 | ||
| 25 | for (const r of core.tree.roots) { | |
| 26 | valid[r.index] = 1 | |
| 27 | stack.push(r) | |
| 28 | } | |
| 29 | ||
| 30 | while (stack.length > 0) { | |
| 31 | const node = stack.pop() | |
| 32 | if ((node.index & 1) === 0) continue | |
| 33 | ||
| 34 | const [left, right] = flat.children(node.index) | |
| 35 | const leftNode = getNode(left) | |
| 36 | const rightNode = getNode(right) | |
| 37 | ||
| 38 | if (!rightNode && !leftNode) continue | |
| 39 | ||
| 40 | stack.push(leftNode, rightNode) | |
| 41 | ||
| 42 | if (valid[node.index]) { | |
| 43 | const hash = hypercoreCrypto.parent(leftNode, rightNode) | |
| 44 | if (b4a.equals(hash, node.hash) && node.size === (leftNode.size + rightNode.size)) { | |
| 45 | valid[leftNode.index] = 1 | |
| 46 | valid[rightNode.index] = 1 | |
| 47 | continue | |
| 48 | } | |
| 49 | } | |
| 50 | ||
| 51 | if (leftNode.size) clearNode(leftNode) | |
| 52 | if (rightNode.size) clearNode(rightNode) | |
| 53 | } | |
| 54 | ||
| 55 | if (corrections.tree) { | |
| 56 | core.tree.cache.clear() | |
| 57 | } | |
| 58 | ||
| 59 | let i = 0 | |
| 60 | let nextOffset = -1 | |
| 61 | while (i < length) { | |
| 62 | const has = core.bitfield.get(i) | |
| 63 | ||
| 64 | if (!has) { | |
| 65 | if (i + 1 === length) break | |
| 66 | i = core.bitfield.findFirst(true, i + 1) | |
| 67 | if (i < 0) break | |
| 68 | nextOffset = -1 | |
| 69 | continue | |
| 70 | } | |
| 71 | ||
| 72 | if (nextOffset === -1) { | |
| 73 | try { | |
| 74 | nextOffset = await core.tree.byteOffset(i * 2) | |
| 75 | } catch { | |
| 76 | core._setBitfield(i, false) | |
| 77 | corrections.blocks++ | |
| 78 | i++ | |
| 79 | continue | |
| 80 | } | |
| 81 | } | |
| 82 | ||
| 83 | const node = getNode(i * 2) | |
| 84 | const blk = data.subarray(nextOffset, nextOffset + node.size) | |
| 85 | const hash = hypercoreCrypto.data(blk) | |
| 86 | ||
| 87 | nextOffset += blk.byteLength | |
| 88 | ||
| 89 | if (!b4a.equals(hash, node.hash)) { | |
| 90 | core._setBitfield(i, false) | |
| 91 | corrections.blocks++ | |
| 92 | } | |
| 93 | ||
| 94 | i++ | |
| 95 | } | |
| 96 | ||
| 97 | return corrections | |
| 98 | ||
| 99 | function getNode (index) { | |
| 100 | if (index * 40 + 40 > tree.byteLength) return null | |
| 101 | const state = { start: index * 40, end: index * 40 + 40, buffer: tree } | |
| 102 | const size = c.uint64.decode(state) | |
| 103 | const hash = c.fixed32.decode(state) | |
| 104 | if (size === 0 && hash.equals(empty)) return null | |
| 105 | return { index, size, hash } | |
| 106 | } | |
| 107 | ||
| 108 | function clearNode (node) { | |
| 109 | valid[node.index] = 0 | |
| 110 | ||
| 111 | if (node.size) { | |
| 112 | b4a.fill(tree, 0, node.index * 40, node.index * 40 + 40) | |
| 113 | core.tree.unflushed.set(node.index, core.tree.blankNode(node.index)) | |
| 114 | corrections.tree++ | |
| 115 | } | |
| 116 | } | |
| 117 | } | |
| 118 | ||
| 119 | function readFullStorage (storage) { | |
| 120 | return new Promise((resolve, reject) => { | |
| 121 | storage.stat((_, st) => { | |
| 122 | if (!st) return resolve(b4a.alloc(0)) | |
| 123 | storage.read(0, st.size, (err, data) => { | |
| 124 | if (err) reject(err) | |
| 125 | else resolve(data) | |
| 126 | }) | |
| 127 | }) | |
| 128 | }) | |
| 129 | } |
| … | … | @@ -0,0 +1,431 @@ |
| 1 | const { BLOCK_NOT_AVAILABLE, SESSION_CLOSED } = require('hypercore-errors') | |
| 2 | const EventEmitter = require('events') | |
| 3 | const c = require('compact-encoding') | |
| 4 | const b4a = require('b4a') | |
| 5 | const safetyCatch = require('safety-catch') | |
| 6 | ||
| 7 | module.exports = class HypercoreBatch extends EventEmitter { | |
| 8 | constructor (session, checkoutLength, autoClose, restore, clear) { | |
| 9 | super() | |
| 10 | ||
| 11 | this.session = session | |
| 12 | this.opened = false | |
| 13 | this.closed = false | |
| 14 | this.opening = null | |
| 15 | this.closing = null | |
| 16 | this.writable = true // always writable... | |
| 17 | this.autoClose = autoClose | |
| 18 | this.restore = restore | |
| 19 | this.fork = 0 | |
| 20 | ||
| 21 | this._appends = [] | |
| 22 | this._appendsActual = null | |
| 23 | this._checkoutLength = checkoutLength | |
| 24 | this._byteLength = 0 | |
| 25 | this._sessionLength = 0 | |
| 26 | this._sessionByteLength = 0 | |
| 27 | this._sessionBatch = null | |
| 28 | this._cachedBatch = null | |
| 29 | this._flushing = null | |
| 30 | this._clear = clear | |
| 31 | ||
| 32 | this.opening = this._open() | |
| 33 | this.opening.catch(safetyCatch) | |
| 34 | } | |
| 35 | ||
| 36 | get id () { | |
| 37 | return this.session.id | |
| 38 | } | |
| 39 | ||
| 40 | get key () { | |
| 41 | return this.session.key | |
| 42 | } | |
| 43 | ||
| 44 | get discoveryKey () { | |
| 45 | return this.session.discoveryKey | |
| 46 | } | |
| 47 | ||
| 48 | get indexedLength () { | |
| 49 | return Math.min(this._sessionLength, this.session.core === null ? 0 : this.session.core.tree.length) | |
| 50 | } | |
| 51 | ||
| 52 | get flushedLength () { | |
| 53 | return this._sessionLength | |
| 54 | } | |
| 55 | ||
| 56 | get indexedByteLength () { | |
| 57 | return this._sessionByteLength | |
| 58 | } | |
| 59 | ||
| 60 | get length () { | |
| 61 | return this._sessionLength + this._appends.length | |
| 62 | } | |
| 63 | ||
| 64 | get byteLength () { | |
| 65 | return this._sessionByteLength + this._byteLength | |
| 66 | } | |
| 67 | ||
| 68 | get core () { | |
| 69 | return this.session.core | |
| 70 | } | |
| 71 | ||
| 72 | get manifest () { | |
| 73 | return this.session.manifest | |
| 74 | } | |
| 75 | ||
| 76 | ready () { | |
| 77 | return this.opening | |
| 78 | } | |
| 79 | ||
| 80 | async _open () { | |
| 81 | await this.session.ready() | |
| 82 | ||
| 83 | if (this._clear) this._checkoutLength = this.core.tree.length | |
| 84 | ||
| 85 | if (this._checkoutLength !== -1) { | |
| 86 | const batch = await this.session.core.tree.restoreBatch(this._checkoutLength) | |
| 87 | batch.treeLength = this._checkoutLength | |
| 88 | this._sessionLength = batch.length | |
| 89 | this._sessionByteLength = batch.byteLength | |
| 90 | this._sessionBatch = batch | |
| 91 | if (this._clear) await this.core.clearBatch() | |
| 92 | } else { | |
| 93 | const last = this.restore ? this.session.core.bitfield.findFirst(false, this.session.length) : 0 | |
| 94 | ||
| 95 | if (last > this.session.length) { | |
| 96 | const batch = await this.session.core.tree.restoreBatch(last) | |
| 97 | this._sessionLength = batch.length | |
| 98 | this._sessionByteLength = batch.byteLength - this.session.padding * batch.length | |
| 99 | this._sessionBatch = batch | |
| 100 | } else { | |
| 101 | this._sessionLength = this.session.length | |
| 102 | this._sessionByteLength = this.session.byteLength | |
| 103 | this._sessionBatch = this.session.createTreeBatch() | |
| 104 | } | |
| 105 | } | |
| 106 | ||
| 107 | this._appendsActual = this.session.encryption ? [] : this._appends | |
| 108 | this.fork = this.session.fork | |
| 109 | this.opened = true | |
| 110 | this.emit('ready') | |
| 111 | } | |
| 112 | ||
| 113 | async has (index) { | |
| 114 | if (this.opened === false) await this.ready() | |
| 115 | if (index >= this._sessionLength) return index < this.length | |
| 116 | return this.session.has(index) | |
| 117 | } | |
| 118 | ||
| 119 | async update (opts) { | |
| 120 | if (this.opened === false) await this.ready() | |
| 121 | await this.session.update(opts) | |
| 122 | } | |
| 123 | ||
| 124 | treeHash () { | |
| 125 | return this._sessionBatch.hash() | |
| 126 | } | |
| 127 | ||
| 128 | setUserData (key, value, opts) { | |
| 129 | return this.session.setUserData(key, value, opts) | |
| 130 | } | |
| 131 | ||
| 132 | getUserData (key, opts) { | |
| 133 | return this.session.getUserData(key, opts) | |
| 134 | } | |
| 135 | ||
| 136 | async info (opts) { | |
| 137 | const session = this.session | |
| 138 | const info = await session.info(opts) | |
| 139 | ||
| 140 | info.length = this._sessionLength | |
| 141 | ||
| 142 | if (info.contiguousLength >= info.length) { | |
| 143 | info.contiguousLength = info.length += this._appends.length | |
| 144 | } else { | |
| 145 | info.length += this._appends.length | |
| 146 | } | |
| 147 | ||
| 148 | info.byteLength = this._sessionByteLength + this._byteLength | |
| 149 | ||
| 150 | return info | |
| 151 | } | |
| 152 | ||
| 153 | async seek (bytes, opts = {}) { | |
| 154 | if (this.opened === false) await this.opening | |
| 155 | if (this.closing) throw SESSION_CLOSED() | |
| 156 | ||
| 157 | if (bytes < this._sessionByteLength) return await this.session.seek(bytes, { ...opts, tree: this._sessionBatch }) | |
| 158 | ||
| 159 | bytes -= this._sessionByteLength | |
| 160 | ||
| 161 | let i = 0 | |
| 162 | ||
| 163 | for (const blk of this._appends) { | |
| 164 | if (bytes < blk.byteLength) return [this._sessionLength + i, bytes] | |
| 165 | i++ | |
| 166 | bytes -= blk.byteLength | |
| 167 | } | |
| 168 | ||
| 169 | if (bytes === 0) return [this._sessionLength + i, 0] | |
| 170 | ||
| 171 | throw BLOCK_NOT_AVAILABLE() | |
| 172 | } | |
| 173 | ||
| 174 | async get (index, opts = {}) { | |
| 175 | if (this.opened === false) await this.opening | |
| 176 | if (this.closing) throw SESSION_CLOSED() | |
| 177 | ||
| 178 | const length = this._sessionLength | |
| 179 | ||
| 180 | if (index < length) { | |
| 181 | return this.session.get(index, { ...opts, tree: this._sessionBatch }) | |
| 182 | } | |
| 183 | ||
| 184 | if (opts && opts.raw) { | |
| 185 | return this._appendsActual[index - length] || null | |
| 186 | } | |
| 187 | ||
| 188 | const buffer = this._appends[index - length] || null | |
| 189 | if (!buffer) throw BLOCK_NOT_AVAILABLE() | |
| 190 | ||
| 191 | const encoding = (opts && opts.valueEncoding && c.from(opts.valueEncoding)) || this.session.valueEncoding | |
| 192 | if (!encoding) return buffer | |
| 193 | ||
| 194 | return c.decode(encoding, buffer) | |
| 195 | } | |
| 196 | ||
| 197 | async _waitForFlush () { | |
| 198 | // wait for any pending flush... | |
| 199 | while (this._flushing) { | |
| 200 | await this._flushing | |
| 201 | await Promise.resolve() // yield in case a new flush is queued | |
| 202 | } | |
| 203 | } | |
| 204 | ||
| 205 | async restoreBatch (length, blocks) { | |
| 206 | if (this.opened === false) await this.opening | |
| 207 | if (length >= this._sessionLength) return this.createTreeBatch(length, blocks) | |
| 208 | return this.session.core.tree.restoreBatch(length) | |
| 209 | } | |
| 210 | ||
| 211 | _catchupBatch (clone) { | |
| 212 | if (this._cachedBatch === null) this._cachedBatch = this._sessionBatch.clone() | |
| 213 | ||
| 214 | if (this.length > this._cachedBatch.length) { | |
| 215 | const offset = this._cachedBatch.length - this._sessionBatch.length | |
| 216 | ||
| 217 | for (let i = offset; i < this._appendsActual.length; i++) { | |
| 218 | this._cachedBatch.append(this._appendsActual[i]) | |
| 219 | } | |
| 220 | } | |
| 221 | ||
| 222 | return clone ? this._cachedBatch.clone() : this._cachedBatch | |
| 223 | } | |
| 224 | ||
| 225 | createTreeBatch (length, opts = {}) { | |
| 226 | if (Array.isArray(opts)) opts = { blocks: opts } | |
| 227 | ||
| 228 | const { blocks = [], clone = true } = opts | |
| 229 | if (!length && length !== 0) length = this.length + blocks.length | |
| 230 | ||
| 231 | const maxLength = this.length + blocks.length | |
| 232 | const b = this._catchupBatch(clone || (blocks.length > 0 || length !== this.length)) | |
| 233 | const len = Math.min(length, this.length) | |
| 234 | ||
| 235 | if (len < this._sessionLength || length > maxLength) return null | |
| 236 | if (len < b.length) b.checkout(len, this._sessionBatch.roots) | |
| 237 | ||
| 238 | for (let i = 0; i < length - len; i++) { | |
| 239 | b.append(this._appendsActual === this._appends ? blocks[i] : this._encrypt(b.length, blocks[i])) | |
| 240 | } | |
| 241 | ||
| 242 | return b | |
| 243 | } | |
| 244 | ||
| 245 | async truncate (newLength = 0, opts = {}) { | |
| 246 | if (this.opened === false) await this.opening | |
| 247 | if (this.closing) throw SESSION_CLOSED() | |
| 248 | ||
| 249 | // wait for any pending flush... (prop needs a lock) | |
| 250 | await this._waitForFlush() | |
| 251 | ||
| 252 | if (typeof opts === 'number') opts = { fork: opts } | |
| 253 | const { fork = this.fork + 1, force = false } = opts | |
| 254 | ||
| 255 | this._cachedBatch = null | |
| 256 | ||
| 257 | const length = this._sessionLength | |
| 258 | if (newLength < length) { | |
| 259 | if (!force) throw new Error('Cannot truncate committed blocks') | |
| 260 | this._appends.length = 0 | |
| 261 | this._byteLength = 0 | |
| 262 | await this.session.truncate(newLength, { fork, force: true, ...opts }) | |
| 263 | this._sessionLength = this.session.length | |
| 264 | this._sessionByteLength = this.session.byteLength | |
| 265 | this._sessionBatch = this.session.createTreeBatch() | |
| 266 | } else { | |
| 267 | for (let i = newLength - length; i < this._appends.length; i++) this._byteLength -= this._appends[i].byteLength | |
| 268 | this._appends.length = newLength - length | |
| 269 | } | |
| 270 | ||
| 271 | this.fork = fork | |
| 272 | ||
| 273 | this.emit('truncate', newLength, this.fork) | |
| 274 | } | |
| 275 | ||
| 276 | async append (blocks) { | |
| 277 | const session = this.session | |
| 278 | ||
| 279 | if (this.opened === false) await this.opening | |
| 280 | if (this.closing) throw SESSION_CLOSED() | |
| 281 | ||
| 282 | // wait for any pending flush... (prop needs a lock) | |
| 283 | await this._waitForFlush() | |
| 284 | ||
| 285 | blocks = Array.isArray(blocks) ? blocks : [blocks] | |
| 286 | ||
| 287 | const buffers = session.encodeBatch !== null | |
| 288 | ? session.encodeBatch(blocks) | |
| 289 | : new Array(blocks.length) | |
| 290 | ||
| 291 | if (session.encodeBatch === null) { | |
| 292 | for (let i = 0; i < blocks.length; i++) { | |
| 293 | const buffer = this._encode(session.valueEncoding, blocks[i]) | |
| 294 | buffers[i] = buffer | |
| 295 | this._byteLength += buffer.byteLength | |
| 296 | } | |
| 297 | } | |
| 298 | if (this._appends !== this._appendsActual) { | |
| 299 | for (let i = 0; i < buffers.length; i++) { | |
| 300 | this._appendsActual.push(this._encrypt(this._sessionLength + this._appendsActual.length, buffers[i])) | |
| 301 | } | |
| 302 | } | |
| 303 | ||
| 304 | for (const b of buffers) this._appends.push(b) | |
| 305 | ||
| 306 | const info = { length: this.length, byteLength: this.byteLength } | |
| 307 | this.emit('append') | |
| 308 | ||
| 309 | return info | |
| 310 | } | |
| 311 | ||
| 312 | _encode (enc, val) { | |
| 313 | const state = { start: 0, end: 0, buffer: null } | |
| 314 | ||
| 315 | if (b4a.isBuffer(val)) { | |
| 316 | if (state.start === 0) return val | |
| 317 | state.end += val.byteLength | |
| 318 | } else if (enc) { | |
| 319 | enc.preencode(state, val) | |
| 320 | } else { | |
| 321 | val = b4a.from(val) | |
| 322 | if (state.start === 0) return val | |
| 323 | state.end += val.byteLength | |
| 324 | } | |
| 325 | ||
| 326 | state.buffer = b4a.allocUnsafe(state.end) | |
| 327 | ||
| 328 | if (enc) enc.encode(state, val) | |
| 329 | else state.buffer.set(val, state.start) | |
| 330 | ||
| 331 | return state.buffer | |
| 332 | } | |
| 333 | ||
| 334 | _encrypt (index, buffer) { | |
| 335 | const block = b4a.allocUnsafe(buffer.byteLength + 8) | |
| 336 | block.set(buffer, 8) | |
| 337 | this.session.encryption.encrypt(index, block, this.fork) | |
| 338 | return block | |
| 339 | } | |
| 340 | ||
| 341 | async flush (opts = {}) { | |
| 342 | if (this.opened === false) await this.opening | |
| 343 | if (this.closing) throw SESSION_CLOSED() | |
| 344 | ||
| 345 | const { length = this.length, keyPair = this.session.keyPair, signature = null, pending = !signature && !keyPair } = opts | |
| 346 | ||
| 347 | while (this._flushing) await this._flushing | |
| 348 | this._flushing = this._flush(length, keyPair, signature, pending) | |
| 349 | ||
| 350 | let flushed = false | |
| 351 | ||
| 352 | try { | |
| 353 | flushed = await this._flushing | |
| 354 | } finally { | |
| 355 | this._flushing = null | |
| 356 | } | |
| 357 | ||
| 358 | if (this.autoClose) await this.close() | |
| 359 | ||
| 360 | return flushed | |
| 361 | } | |
| 362 | ||
| 363 | async _flush (length, keyPair, signature, pending) { // TODO: make this safe to interact with a parallel truncate... | |
| 364 | if (this._sessionBatch.fork !== this.session.fork) return false // no truncs supported atm | |
| 365 | ||
| 366 | if (this.session.replicator._upgrade) { | |
| 367 | for (const req of this.session.replicator._upgrade.inflight) { | |
| 368 | // yield to the remote inflight upgrade, TODO: if the remote upgrade fails, retry flushing... | |
| 369 | if (req.upgrade && (req.upgrade.start + req.upgrade.length) > length) { | |
| 370 | return false | |
| 371 | } | |
| 372 | } | |
| 373 | } | |
| 374 | ||
| 375 | const flushingLength = Math.min(length - this._sessionLength, this._appends.length) | |
| 376 | if (flushingLength <= 0) { | |
| 377 | if (this._sessionLength > this.core.tree.length && length > this.core.tree.length && !pending) { | |
| 378 | const batch = await this.restoreBatch(length) | |
| 379 | const info = await this.core.insertBatch(batch, [], { keyPair, signature, pending, treeLength: length }) | |
| 380 | return info !== null | |
| 381 | } | |
| 382 | return true | |
| 383 | } | |
| 384 | ||
| 385 | const batch = this.createTreeBatch(this._sessionLength + flushingLength) | |
| 386 | if (batch === null) return false | |
| 387 | ||
| 388 | const info = await this.core.insertBatch(batch, this._appendsActual, { keyPair, signature, pending, treeLength: this._sessionLength }) | |
| 389 | if (info === null) return false | |
| 390 | ||
| 391 | const delta = info.byteLength - this._sessionByteLength | |
| 392 | const newBatch = info.length !== this.session.length ? await this.session.core.tree.restoreBatch(info.length) : this.session.createTreeBatch() | |
| 393 | ||
| 394 | this._sessionLength = info.length | |
| 395 | this._sessionByteLength = info.byteLength | |
| 396 | this._sessionBatch = newBatch | |
| 397 | ||
| 398 | if (this._cachedBatch !== null) this._cachedBatch.prune(info.length) | |
| 399 | ||
| 400 | const same = this._appends === this._appendsActual | |
| 401 | ||
| 402 | this._appends = this._appends.slice(flushingLength) | |
| 403 | this._appendsActual = same ? this._appends : this._appendsActual.slice(flushingLength) | |
| 404 | this._byteLength -= delta | |
| 405 | ||
| 406 | this.emit('flush') | |
| 407 | ||
| 408 | return true | |
| 409 | } | |
| 410 | ||
| 411 | close () { | |
| 412 | if (!this.closing) this.closing = this._close() | |
| 413 | return this.closing | |
| 414 | } | |
| 415 | ||
| 416 | async _close () { | |
| 417 | this._clearAppends() | |
| 418 | ||
| 419 | await this.session.close() | |
| 420 | ||
| 421 | this.closed = true | |
| 422 | this.emit('close') | |
| 423 | } | |
| 424 | ||
| 425 | _clearAppends () { | |
| 426 | this._appends = [] | |
| 427 | this._appendsActual = [] | |
| 428 | this._byteLength = 0 | |
| 429 | this.fork = 0 | |
| 430 | } | |
| 431 | } |
| … | … | @@ -0,0 +1,55 @@ |
| 1 | const c = require('compact-encoding') | |
| 2 | const { oplog } = require('./messages') | |
| 3 | ||
| 4 | module.exports = class BigHeader { | |
| 5 | constructor (storage) { | |
| 6 | this.storage = storage | |
| 7 | } | |
| 8 | ||
| 9 | async load (external) { | |
| 10 | const buf = await new Promise((resolve, reject) => { | |
| 11 | this.storage.read(external.start, external.length, (err, buf) => { | |
| 12 | if (err) return reject(err) | |
| 13 | resolve(buf) | |
| 14 | }) | |
| 15 | }) | |
| 16 | ||
| 17 | const header = c.decode(oplog.header, buf) | |
| 18 | header.external = external | |
| 19 | return header | |
| 20 | } | |
| 21 | ||
| 22 | async flush (header) { | |
| 23 | const external = header.external || { start: 0, length: 0 } | |
| 24 | header.external = null | |
| 25 | ||
| 26 | const buf = c.encode(oplog.header, header) | |
| 27 | ||
| 28 | let start = 0 | |
| 29 | if (buf.byteLength > external.start) { | |
| 30 | start = external.start + external.length | |
| 31 | const rem = start & 4095 | |
| 32 | if (rem > 0) start += (4096 - rem) | |
| 33 | } | |
| 34 | ||
| 35 | header.external = { start, length: buf.byteLength } | |
| 36 | ||
| 37 | await new Promise((resolve, reject) => { | |
| 38 | this.storage.write(start, buf, (err) => { | |
| 39 | if (err) return reject(err) | |
| 40 | resolve() | |
| 41 | }) | |
| 42 | }) | |
| 43 | ||
| 44 | return header | |
| 45 | } | |
| 46 | ||
| 47 | close () { | |
| 48 | return new Promise((resolve, reject) => { | |
| 49 | this.storage.close((err) => { | |
| 50 | if (err) return reject(err) | |
| 51 | resolve() | |
| 52 | }) | |
| 53 | }) | |
| 54 | } | |
| 55 | } |
| … | … | @@ -0,0 +1,496 @@ |
| 1 | const BigSparseArray = require('big-sparse-array') | |
| 2 | const b4a = require('b4a') | |
| 3 | const quickbit = require('./compat').quickbit | |
| 4 | ||
| 5 | const BITS_PER_PAGE = 32768 | |
| 6 | const BYTES_PER_PAGE = BITS_PER_PAGE / 8 | |
| 7 | const WORDS_PER_PAGE = BYTES_PER_PAGE / 4 | |
| 8 | const BITS_PER_SEGMENT = 2097152 | |
| 9 | const BYTES_PER_SEGMENT = BITS_PER_SEGMENT / 8 | |
| 10 | const WORDS_PER_SEGMENT = BYTES_PER_SEGMENT / 4 | |
| 11 | const INITIAL_WORDS_PER_SEGMENT = 1024 | |
| 12 | const PAGES_PER_SEGMENT = BITS_PER_SEGMENT / BITS_PER_PAGE | |
| 13 | const SEGMENT_GROWTH_FACTOR = 4 | |
| 14 | ||
| 15 | class BitfieldPage { | |
| 16 | constructor (index, segment) { | |
| 17 | this.dirty = false | |
| 18 | this.index = index | |
| 19 | this.offset = index * BYTES_PER_PAGE - segment.offset | |
| 20 | this.bitfield = null | |
| 21 | this.segment = segment | |
| 22 | ||
| 23 | segment.add(this) | |
| 24 | } | |
| 25 | ||
| 26 | get tree () { | |
| 27 | return this.segment.tree | |
| 28 | } | |
| 29 | ||
| 30 | get (index) { | |
| 31 | return quickbit.get(this.bitfield, index) | |
| 32 | } | |
| 33 | ||
| 34 | set (index, val) { | |
| 35 | if (quickbit.set(this.bitfield, index, val)) { | |
| 36 | this.tree.update(this.offset * 8 + index) | |
| 37 | } | |
| 38 | } | |
| 39 | ||
| 40 | setRange (start, length, val) { | |
| 41 | quickbit.fill(this.bitfield, val, start, start + length) | |
| 42 | ||
| 43 | let i = Math.floor(start / 128) | |
| 44 | const n = i + Math.ceil(length / 128) | |
| 45 | ||
| 46 | while (i <= n) this.tree.update(this.offset * 8 + i++ * 128) | |
| 47 | } | |
| 48 | ||
| 49 | findFirst (val, position) { | |
| 50 | return quickbit.findFirst(this.bitfield, val, position) | |
| 51 | } | |
| 52 | ||
| 53 | findLast (val, position) { | |
| 54 | return quickbit.findLast(this.bitfield, val, position) | |
| 55 | } | |
| 56 | ||
| 57 | count (start, length, val) { | |
| 58 | const end = start + length | |
| 59 | ||
| 60 | let i = start | |
| 61 | let c = 0 | |
| 62 | ||
| 63 | while (length > 0) { | |
| 64 | const l = this.findFirst(val, i) | |
| 65 | if (l === -1 || l >= end) return c | |
| 66 | ||
| 67 | const h = this.findFirst(!val, l + 1) | |
| 68 | if (h === -1 || h >= end) return c + end - l | |
| 69 | ||
| 70 | c += h - l | |
| 71 | length -= h - i | |
| 72 | i = h | |
| 73 | } | |
| 74 | ||
| 75 | return c | |
| 76 | } | |
| 77 | } | |
| 78 | ||
| 79 | class BitfieldSegment { | |
| 80 | constructor (index, bitfield) { | |
| 81 | this.index = index | |
| 82 | this.offset = index * BYTES_PER_SEGMENT | |
| 83 | this.tree = quickbit.Index.from(bitfield, BYTES_PER_SEGMENT) | |
| 84 | this.pages = new Array(PAGES_PER_SEGMENT) | |
| 85 | } | |
| 86 | ||
| 87 | get bitfield () { | |
| 88 | return this.tree.field | |
| 89 | } | |
| 90 | ||
| 91 | add (page) { | |
| 92 | const i = page.index - this.index * PAGES_PER_SEGMENT | |
| 93 | this.pages[i] = page | |
| 94 | ||
| 95 | const start = i * WORDS_PER_PAGE | |
| 96 | const end = start + WORDS_PER_PAGE | |
| 97 | ||
| 98 | if (end >= this.bitfield.length) this.reallocate(end) | |
| 99 | ||
| 100 | page.bitfield = this.bitfield.subarray(start, end) | |
| 101 | } | |
| 102 | ||
| 103 | reallocate (length) { | |
| 104 | let target = this.bitfield.length | |
| 105 | while (target < length) target *= SEGMENT_GROWTH_FACTOR | |
| 106 | ||
| 107 | const bitfield = new Uint32Array(target) | |
| 108 | bitfield.set(this.bitfield) | |
| 109 | ||
| 110 | this.tree = quickbit.Index.from(bitfield, BYTES_PER_SEGMENT) | |
| 111 | ||
| 112 | for (let i = 0; i < this.pages.length; i++) { | |
| 113 | const page = this.pages[i] | |
| 114 | if (!page) continue | |
| 115 | ||
| 116 | const start = i * WORDS_PER_PAGE | |
| 117 | const end = start + WORDS_PER_PAGE | |
| 118 | ||
| 119 | page.bitfield = bitfield.subarray(start, end) | |
| 120 | } | |
| 121 | } | |
| 122 | ||
| 123 | findFirst (val, position) { | |
| 124 | position = this.tree.skipFirst(!val, position) | |
| 125 | ||
| 126 | let j = position & (BITS_PER_PAGE - 1) | |
| 127 | let i = (position - j) / BITS_PER_PAGE | |
| 128 | ||
| 129 | if (i >= PAGES_PER_SEGMENT) return -1 | |
| 130 | ||
| 131 | while (i < this.pages.length) { | |
| 132 | const p = this.pages[i] | |
| 133 | ||
| 134 | let index = -1 | |
| 135 | ||
| 136 | if (p) index = p.findFirst(val, j) | |
| 137 | else if (!val) index = j | |
| 138 | ||
| 139 | if (index !== -1) return i * BITS_PER_PAGE + index | |
| 140 | ||
| 141 | j = 0 | |
| 142 | i++ | |
| 143 | } | |
| 144 | ||
| 145 | return -1 | |
| 146 | } | |
| 147 | ||
| 148 | findLast (val, position) { | |
| 149 | position = this.tree.skipLast(!val, position) | |
| 150 | ||
| 151 | let j = position & (BITS_PER_PAGE - 1) | |
| 152 | let i = (position - j) / BITS_PER_PAGE | |
| 153 | ||
| 154 | if (i >= PAGES_PER_SEGMENT) return -1 | |
| 155 | ||
| 156 | while (i >= 0) { | |
| 157 | const p = this.pages[i] | |
| 158 | ||
| 159 | let index = -1 | |
| 160 | ||
| 161 | if (p) index = p.findLast(val, j) | |
| 162 | else if (!val) index = j | |
| 163 | ||
| 164 | if (index !== -1) return i * BITS_PER_PAGE + index | |
| 165 | ||
| 166 | j = BITS_PER_PAGE - 1 | |
| 167 | i-- | |
| 168 | } | |
| 169 | ||
| 170 | return -1 | |
| 171 | } | |
| 172 | } | |
| 173 | ||
| 174 | module.exports = class Bitfield { | |
| 175 | constructor (storage, buffer) { | |
| 176 | this.unflushed = [] | |
| 177 | this.storage = storage | |
| 178 | this.resumed = !!(buffer && buffer.byteLength >= 4) | |
| 179 | ||
| 180 | this._pages = new BigSparseArray() | |
| 181 | this._segments = new BigSparseArray() | |
| 182 | ||
| 183 | const view = this.resumed | |
| 184 | ? new Uint32Array( | |
| 185 | buffer.buffer, | |
| 186 | buffer.byteOffset, | |
| 187 | Math.floor(buffer.byteLength / 4) | |
| 188 | ) | |
| 189 | : new Uint32Array(INITIAL_WORDS_PER_SEGMENT) | |
| 190 | ||
| 191 | for (let i = 0; i < view.length; i += WORDS_PER_SEGMENT) { | |
| 192 | let bitfield = view.subarray(i, i + (WORDS_PER_SEGMENT)) | |
| 193 | let length = WORDS_PER_SEGMENT | |
| 194 | ||
| 195 | if (i === 0) { | |
| 196 | length = INITIAL_WORDS_PER_SEGMENT | |
| 197 | while (length < bitfield.length) length *= SEGMENT_GROWTH_FACTOR | |
| 198 | } | |
| 199 | ||
| 200 | if (bitfield.length !== length) { | |
| 201 | const copy = new Uint32Array(length) | |
| 202 | copy.set(bitfield, 0) | |
| 203 | bitfield = copy | |
| 204 | } | |
| 205 | ||
| 206 | const segment = new BitfieldSegment(i / (WORDS_PER_SEGMENT), bitfield) | |
| 207 | this._segments.set(segment.index, segment) | |
| 208 | ||
| 209 | for (let j = 0; j < bitfield.length; j += WORDS_PER_PAGE) { | |
| 210 | const page = new BitfieldPage((i + j) / WORDS_PER_PAGE, segment) | |
| 211 | this._pages.set(page.index, page) | |
| 212 | } | |
| 213 | } | |
| 214 | } | |
| 215 | ||
| 216 | toBuffer (length) { | |
| 217 | const pages = Math.ceil(length / BITS_PER_PAGE) | |
| 218 | const buffer = b4a.allocUnsafe(pages * BYTES_PER_PAGE) | |
| 219 | ||
| 220 | for (let i = 0; i < pages; i++) { | |
| 221 | const page = this._pages.get(i) | |
| 222 | const offset = i * BYTES_PER_PAGE | |
| 223 | ||
| 224 | if (page) { | |
| 225 | const buf = b4a.from( | |
| 226 | page.bitfield.buffer, | |
| 227 | page.bitfield.byteOffset, | |
| 228 | page.bitfield.byteLength | |
| 229 | ) | |
| 230 | ||
| 231 | buffer.set(buf, offset) | |
| 232 | } else { | |
| 233 | buffer.fill(0, offset, offset + BYTES_PER_PAGE) | |
| 234 | } | |
| 235 | } | |
| 236 | ||
| 237 | return buffer | |
| 238 | } | |
| 239 | ||
| 240 | getBitfield (index) { | |
| 241 | const j = index & (BITS_PER_PAGE - 1) | |
| 242 | const i = (index - j) / BITS_PER_PAGE | |
| 243 | ||
| 244 | const p = this._pages.get(i) | |
| 245 | return p || null | |
| 246 | } | |
| 247 | ||
| 248 | get (index) { | |
| 249 | const j = index & (BITS_PER_PAGE - 1) | |
| 250 | const i = (index - j) / BITS_PER_PAGE | |
| 251 | ||
| 252 | const p = this._pages.get(i) | |
| 253 | ||
| 254 | return p ? p.get(j) : false | |
| 255 | } | |
| 256 | ||
| 257 | set (index, val) { | |
| 258 | const j = index & (BITS_PER_PAGE - 1) | |
| 259 | const i = (index - j) / BITS_PER_PAGE | |
| 260 | ||
| 261 | let p = this._pages.get(i) | |
| 262 | ||
| 263 | if (!p && val) { | |
| 264 | const k = Math.floor(i / PAGES_PER_SEGMENT) | |
| 265 | const s = this._segments.get(k) || this._segments.set(k, new BitfieldSegment(k, new Uint32Array(k === 0 ? INITIAL_WORDS_PER_SEGMENT : WORDS_PER_SEGMENT))) | |
| 266 | ||
| 267 | p = this._pages.set(i, new BitfieldPage(i, s)) | |
| 268 | } | |
| 269 | ||
| 270 | if (p) { | |
| 271 | p.set(j, val) | |
| 272 | ||
| 273 | if (!p.dirty) { | |
| 274 | p.dirty = true | |
| 275 | this.unflushed.push(p) | |
| 276 | } | |
| 277 | } | |
| 278 | } | |
| 279 | ||
| 280 | setRange (start, length, val) { | |
| 281 | let j = start & (BITS_PER_PAGE - 1) | |
| 282 | let i = (start - j) / BITS_PER_PAGE | |
| 283 | ||
| 284 | while (length > 0) { | |
| 285 | let p = this._pages.get(i) | |
| 286 | ||
| 287 | if (!p && val) { | |
| 288 | const k = Math.floor(i / PAGES_PER_SEGMENT) | |
| 289 | const s = this._segments.get(k) || this._segments.set(k, new BitfieldSegment(k, new Uint32Array(k === 0 ? INITIAL_WORDS_PER_SEGMENT : WORDS_PER_SEGMENT))) | |
| 290 | ||
| 291 | p = this._pages.set(i, new BitfieldPage(i, s)) | |
| 292 | } | |
| 293 | ||
| 294 | const end = Math.min(j + length, BITS_PER_PAGE) | |
| 295 | const range = end - j | |
| 296 | ||
| 297 | if (p) { | |
| 298 | p.setRange(j, range, val) | |
| 299 | ||
| 300 | if (!p.dirty) { | |
| 301 | p.dirty = true | |
| 302 | this.unflushed.push(p) | |
| 303 | } | |
| 304 | } | |
| 305 | ||
| 306 | j = 0 | |
| 307 | i++ | |
| 308 | length -= range | |
| 309 | } | |
| 310 | } | |
| 311 | ||
| 312 | findFirst (val, position) { | |
| 313 | let j = position & (BITS_PER_SEGMENT - 1) | |
| 314 | let i = (position - j) / BITS_PER_SEGMENT | |
| 315 | ||
| 316 | while (i < this._segments.maxLength) { | |
| 317 | const s = this._segments.get(i) | |
| 318 | ||
| 319 | let index = -1 | |
| 320 | ||
| 321 | if (s) index = s.findFirst(val, j) | |
| 322 | else if (!val) index = j | |
| 323 | ||
| 324 | if (index !== -1) return i * BITS_PER_SEGMENT + index | |
| 325 | ||
| 326 | j = 0 | |
| 327 | i++ | |
| 328 | } | |
| 329 | ||
| 330 | return val ? -1 : this._segments.maxLength * BITS_PER_SEGMENT | |
| 331 | } | |
| 332 | ||
| 333 | firstSet (position) { | |
| 334 | return this.findFirst(true, position) | |
| 335 | } | |
| 336 | ||
| 337 | firstUnset (position) { | |
| 338 | return this.findFirst(false, position) | |
| 339 | } | |
| 340 | ||
| 341 | findLast (val, position) { | |
| 342 | let j = position & (BITS_PER_SEGMENT - 1) | |
| 343 | let i = (position - j) / BITS_PER_SEGMENT | |
| 344 | ||
| 345 | while (i >= 0) { | |
| 346 | const s = this._segments.get(i) | |
| 347 | ||
| 348 | let index = -1 | |
| 349 | ||
| 350 | if (s) index = s.findLast(val, j) | |
| 351 | else if (!val) index = j | |
| 352 | ||
| 353 | if (index !== -1) return i * BITS_PER_SEGMENT + index | |
| 354 | ||
| 355 | j = BITS_PER_SEGMENT - 1 | |
| 356 | i-- | |
| 357 | } | |
| 358 | ||
| 359 | return -1 | |
| 360 | } | |
| 361 | ||
| 362 | lastSet (position) { | |
| 363 | return this.findLast(true, position) | |
| 364 | } | |
| 365 | ||
| 366 | lastUnset (position) { | |
| 367 | return this.findLast(false, position) | |
| 368 | } | |
| 369 | ||
| 370 | count (start, length, val) { | |
| 371 | let j = start & (BITS_PER_PAGE - 1) | |
| 372 | let i = (start - j) / BITS_PER_PAGE | |
| 373 | let c = 0 | |
| 374 | ||
| 375 | while (length > 0) { | |
| 376 | const p = this._pages.get(i) | |
| 377 | ||
| 378 | const end = Math.min(j + length, BITS_PER_PAGE) | |
| 379 | const range = end - j | |
| 380 | ||
| 381 | if (p) c += p.count(j, range, val) | |
| 382 | else if (!val) c += range | |
| 383 | ||
| 384 | j = 0 | |
| 385 | i++ | |
| 386 | length -= range | |
| 387 | } | |
| 388 | ||
| 389 | return c | |
| 390 | } | |
| 391 | ||
| 392 | countSet (start, length) { | |
| 393 | return this.count(start, length, true) | |
| 394 | } | |
| 395 | ||
| 396 | countUnset (start, length) { | |
| 397 | return this.count(start, length, false) | |
| 398 | } | |
| 399 | ||
| 400 | * want (start, length) { | |
| 401 | const j = start & (BITS_PER_SEGMENT - 1) | |
| 402 | let i = (start - j) / BITS_PER_SEGMENT | |
| 403 | ||
| 404 | while (length > 0) { | |
| 405 | const s = this._segments.get(i) | |
| 406 | ||
| 407 | if (s) { | |
| 408 | // We always send at least 4 KiB worth of bitfield in a want, rounding | |
| 409 | // to the nearest 4 KiB. | |
| 410 | const end = ceilTo(clamp(length / 8, 4096, BYTES_PER_SEGMENT), 4096) | |
| 411 | ||
| 412 | yield { | |
| 413 | start: i * BITS_PER_SEGMENT, | |
| 414 | bitfield: s.bitfield.subarray(0, end / 4) | |
| 415 | } | |
| 416 | } | |
| 417 | ||
| 418 | i++ | |
| 419 | length -= BITS_PER_SEGMENT | |
| 420 | } | |
| 421 | } | |
| 422 | ||
| 423 | clear () { | |
| 424 | return new Promise((resolve, reject) => { | |
| 425 | this.storage.truncate(0, (err) => { | |
| 426 | if (err) return reject(err) | |
| 427 | this._pages = new BigSparseArray() | |
| 428 | this.unflushed = [] | |
| 429 | resolve() | |
| 430 | }) | |
| 431 | }) | |
| 432 | } | |
| 433 | ||
| 434 | close () { | |
| 435 | return new Promise((resolve, reject) => { | |
| 436 | this.storage.close((err) => { | |
| 437 | if (err) reject(err) | |
| 438 | else resolve() | |
| 439 | }) | |
| 440 | }) | |
| 441 | } | |
| 442 | ||
| 443 | flush () { | |
| 444 | return new Promise((resolve, reject) => { | |
| 445 | if (!this.unflushed.length) return resolve() | |
| 446 | ||
| 447 | const self = this | |
| 448 | let missing = this.unflushed.length | |
| 449 | let error = null | |
| 450 | ||
| 451 | for (const page of this.unflushed) { | |
| 452 | const buf = b4a.from( | |
| 453 | page.bitfield.buffer, | |
| 454 | page.bitfield.byteOffset, | |
| 455 | page.bitfield.byteLength | |
| 456 | ) | |
| 457 | ||
| 458 | page.dirty = false | |
| 459 | this.storage.write(page.index * BYTES_PER_PAGE, buf, done) | |
| 460 | } | |
| 461 | ||
| 462 | function done (err) { | |
| 463 | if (err) error = err | |
| 464 | if (--missing) return | |
| 465 | if (error) return reject(error) | |
| 466 | self.unflushed = [] | |
| 467 | resolve() | |
| 468 | } | |
| 469 | }) | |
| 470 | } | |
| 471 | ||
| 472 | static open (storage, tree = null) { | |
| 473 | return new Promise((resolve, reject) => { | |
| 474 | storage.stat((err, st) => { | |
| 475 | if (err) return resolve(new Bitfield(storage, null)) | |
| 476 | let size = st.size - (st.size & 3) | |
| 477 | if (!size) return resolve(new Bitfield(storage, null)) | |
| 478 | if (tree) size = Math.min(size, ceilTo(tree.length / 8, 4096)) | |
| 479 | storage.read(0, size, (err, data) => { | |
| 480 | if (err) return reject(err) | |
| 481 | resolve(new Bitfield(storage, data)) | |
| 482 | }) | |
| 483 | }) | |
| 484 | }) | |
| 485 | } | |
| 486 | } | |
| 487 | ||
| 488 | function clamp (n, min, max) { | |
| 489 | return Math.min(Math.max(n, min), max) | |
| 490 | } | |
| 491 | ||
| 492 | function ceilTo (n, multiple = 1) { | |
| 493 | const remainder = n % multiple | |
| 494 | if (remainder === 0) return n | |
| 495 | return n + multiple - remainder | |
| 496 | } |
| … | … | @@ -0,0 +1,75 @@ |
| 1 | const sodium = require('sodium-universal') | |
| 2 | const c = require('compact-encoding') | |
| 3 | const b4a = require('b4a') | |
| 4 | const { BLOCK_ENCRYPTION } = require('./caps') | |
| 5 | ||
| 6 | const nonce = b4a.alloc(sodium.crypto_stream_NONCEBYTES) | |
| 7 | ||
| 8 | module.exports = class BlockEncryption { | |
| 9 | constructor (encryptionKey, hypercoreKey, { isBlockKey = false, compat = true } = {}) { | |
| 10 | const subKeys = b4a.alloc(2 * sodium.crypto_stream_KEYBYTES) | |
| 11 | ||
| 12 | this.key = encryptionKey | |
| 13 | this.blockKey = isBlockKey ? encryptionKey : subKeys.subarray(0, sodium.crypto_stream_KEYBYTES) | |
| 14 | this.blindingKey = subKeys.subarray(sodium.crypto_stream_KEYBYTES) | |
| 15 | this.padding = 8 | |
| 16 | this.compat = compat | |
| 17 | this.isBlockKey = isBlockKey | |
| 18 | ||
| 19 | if (!isBlockKey) { | |
| 20 | if (compat) sodium.crypto_generichash_batch(this.blockKey, [encryptionKey], hypercoreKey) | |
| 21 | else sodium.crypto_generichash_batch(this.blockKey, [BLOCK_ENCRYPTION, hypercoreKey, encryptionKey]) | |
| 22 | } | |
| 23 | ||
| 24 | sodium.crypto_generichash(this.blindingKey, this.blockKey) | |
| 25 | } | |
| 26 | ||
| 27 | encrypt (index, block, fork) { | |
| 28 | const padding = block.subarray(0, this.padding) | |
| 29 | block = block.subarray(this.padding) | |
| 30 | ||
| 31 | c.uint64.encode({ start: 0, end: 8, buffer: padding }, fork) | |
| 32 | c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index) | |
| 33 | ||
| 34 | // Zero out any previous padding. | |
| 35 | nonce.fill(0, 8, 8 + padding.byteLength) | |
| 36 | ||
| 37 | // Blind the fork ID, possibly risking reusing the nonce on a reorg of the | |
| 38 | // Hypercore. This is fine as the blinding is best-effort and the latest | |
| 39 | // fork ID shared on replication anyway. | |
| 40 | sodium.crypto_stream_xor( | |
| 41 | padding, | |
| 42 | padding, | |
| 43 | nonce, | |
| 44 | this.blindingKey | |
| 45 | ) | |
| 46 | ||
| 47 | nonce.set(padding, 8) | |
| 48 | ||
| 49 | // The combination of a (blinded) fork ID and a block index is unique for a | |
| 50 | // given Hypercore and is therefore a valid nonce for encrypting the block. | |
| 51 | sodium.crypto_stream_xor( | |
| 52 | block, | |
| 53 | block, | |
| 54 | nonce, | |
| 55 | this.blockKey | |
| 56 | ) | |
| 57 | } | |
| 58 | ||
| 59 | decrypt (index, block) { | |
| 60 | const padding = block.subarray(0, this.padding) | |
| 61 | block = block.subarray(this.padding) | |
| 62 | ||
| 63 | c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index) | |
| 64 | ||
| 65 | nonce.set(padding, 8) | |
| 66 | ||
| 67 | // Decrypt the block using the blinded fork ID. | |
| 68 | sodium.crypto_stream_xor( | |
| 69 | block, | |
| 70 | block, | |
| 71 | nonce, | |
| 72 | this.blockKey | |
| 73 | ) | |
| 74 | } | |
| 75 | } |
| … | … | @@ -0,0 +1,63 @@ |
| 1 | const b4a = require('b4a') | |
| 2 | const { WRITE_FAILED } = require('hypercore-errors') | |
| 3 | ||
| 4 | module.exports = class BlockStore { | |
| 5 | constructor (storage, tree) { | |
| 6 | this.storage = storage | |
| 7 | this.tree = tree | |
| 8 | } | |
| 9 | ||
| 10 | async get (i, tree) { | |
| 11 | if (!tree) tree = this.tree | |
| 12 | const [offset, size] = await tree.byteRange(2 * i) | |
| 13 | return this._read(offset, size) | |
| 14 | } | |
| 15 | ||
| 16 | async put (i, data, offset) { | |
| 17 | return this._write(offset, data) | |
| 18 | } | |
| 19 | ||
| 20 | putBatch (i, batch, offset) { | |
| 21 | if (batch.length === 0) return Promise.resolve() | |
| 22 | return this.put(i, batch.length === 1 ? batch[0] : b4a.concat(batch), offset) | |
| 23 | } | |
| 24 | ||
| 25 | clear (offset = 0, length = -1) { | |
| 26 | return new Promise((resolve, reject) => { | |
| 27 | if (length === -1) this.storage.truncate(offset, done) | |
| 28 | else this.storage.del(offset, length, done) | |
| 29 | ||
| 30 | function done (err) { | |
| 31 | if (err) reject(err) | |
| 32 | else resolve() | |
| 33 | } | |
| 34 | }) | |
| 35 | } | |
| 36 | ||
| 37 | close () { | |
| 38 | return new Promise((resolve, reject) => { | |
| 39 | this.storage.close((err) => { | |
| 40 | if (err) reject(err) | |
| 41 | else resolve() | |
| 42 | }) | |
| 43 | }) | |
| 44 | } | |
| 45 | ||
| 46 | _read (offset, size) { | |
| 47 | return new Promise((resolve, reject) => { | |
| 48 | this.storage.read(offset, size, (err, data) => { | |
| 49 | if (err) reject(err) | |
| 50 | else resolve(data) | |
| 51 | }) | |
| 52 | }) | |
| 53 | } | |
| 54 | ||
| 55 | _write (offset, data) { | |
| 56 | return new Promise((resolve, reject) => { | |
| 57 | this.storage.write(offset, data, (err) => { | |
| 58 | if (err) reject(WRITE_FAILED(err.message)) | |
| 59 | else resolve(offset + data.byteLength) | |
| 60 | }) | |
| 61 | }) | |
| 62 | } | |
| 63 | } |
| … | … | @@ -0,0 +1,46 @@ |
| 1 | const crypto = require('hypercore-crypto') | |
| 2 | const sodium = require('sodium-universal') | |
| 3 | const b4a = require('b4a') | |
| 4 | const c = require('compact-encoding') | |
| 5 | ||
| 6 | // TODO: rename this to "crypto" and move everything hashing related etc in here | |
| 7 | // Also lets move the tree stuff from hypercore-crypto here | |
| 8 | ||
| 9 | const [ | |
| 10 | TREE, | |
| 11 | REPLICATE_INITIATOR, | |
| 12 | REPLICATE_RESPONDER, | |
| 13 | MANIFEST, | |
| 14 | DEFAULT_NAMESPACE, | |
| 15 | BLOCK_ENCRYPTION | |
| 16 | ] = crypto.namespace('hypercore', 6) | |
| 17 | ||
| 18 | exports.MANIFEST = MANIFEST | |
| 19 | exports.DEFAULT_NAMESPACE = DEFAULT_NAMESPACE | |
| 20 | exports.BLOCK_ENCRYPTION = BLOCK_ENCRYPTION | |
| 21 | ||
| 22 | exports.replicate = function (isInitiator, key, handshakeHash) { | |
| 23 | const out = b4a.allocUnsafe(32) | |
| 24 | sodium.crypto_generichash_batch(out, [isInitiator ? REPLICATE_INITIATOR : REPLICATE_RESPONDER, key], handshakeHash) | |
| 25 | return out | |
| 26 | } | |
| 27 | ||
| 28 | exports.treeSignable = function (manifestHash, treeHash, length, fork) { | |
| 29 | const state = { start: 0, end: 112, buffer: b4a.allocUnsafe(112) } | |
| 30 | c.fixed32.encode(state, TREE) | |
| 31 | c.fixed32.encode(state, manifestHash) | |
| 32 | c.fixed32.encode(state, treeHash) | |
| 33 | c.uint64.encode(state, length) | |
| 34 | c.uint64.encode(state, fork) | |
| 35 | return state.buffer | |
| 36 | } | |
| 37 | ||
| 38 | exports.treeSignableCompat = function (hash, length, fork, noHeader) { | |
| 39 | const end = noHeader ? 48 : 80 | |
| 40 | const state = { start: 0, end, buffer: b4a.allocUnsafe(end) } | |
| 41 | if (!noHeader) c.fixed32.encode(state, TREE) // ultra legacy mode, kill in future major | |
| 42 | c.fixed32.encode(state, hash) | |
| 43 | c.uint64.encode(state, length) | |
| 44 | c.uint64.encode(state, fork) | |
| 45 | return state.buffer | |
| 46 | } |
| … | … | @@ -0,0 +1,12 @@ |
| 1 | // Export the appropriate version of `quickbit-universal` as the plain import | |
| 2 | // may resolve to an older version in some environments | |
| 3 | let quickbit = require('quickbit-universal') | |
| 4 | if ( | |
| 5 | typeof quickbit.findFirst !== 'function' || | |
| 6 | typeof quickbit.findLast !== 'function' || | |
| 7 | typeof quickbit.clear !== 'function' | |
| 8 | ) { | |
| 9 | // This should always load the fallback from the locally installed version | |
| 10 | quickbit = require('quickbit-universal/fallback') | |
| 11 | } | |
| 12 | exports.quickbit = quickbit |
| … | … | @@ -0,0 +1,1106 @@ |
| 1 | const hypercoreCrypto = require('hypercore-crypto') | |
| 2 | const b4a = require('b4a') | |
| 3 | const unslab = require('unslab') | |
| 4 | const Oplog = require('./oplog') | |
| 5 | const BigHeader = require('./big-header') | |
| 6 | const Mutex = require('./mutex') | |
| 7 | const MerkleTree = require('./merkle-tree') | |
| 8 | const BlockStore = require('./block-store') | |
| 9 | const Bitfield = require('./bitfield') | |
| 10 | const RemoteBitfield = require('./remote-bitfield') | |
| 11 | const Info = require('./info') | |
| 12 | const { BAD_ARGUMENT, STORAGE_EMPTY, STORAGE_CONFLICT, INVALID_OPERATION, INVALID_SIGNATURE, INVALID_CHECKSUM } = require('hypercore-errors') | |
| 13 | const m = require('./messages') | |
| 14 | const Verifier = require('./verifier') | |
| 15 | const audit = require('./audit') | |
| 16 | ||
| 17 | module.exports = class Core { | |
| 18 | constructor (header, compat, crypto, oplog, bigHeader, tree, blocks, bitfield, verifier, sessions, legacy, globalCache, onupdate, onconflict) { | |
| 19 | this.onupdate = onupdate | |
| 20 | this.onconflict = onconflict | |
| 21 | this.preupdate = null | |
| 22 | this.header = header | |
| 23 | this.compat = compat | |
| 24 | this.crypto = crypto | |
| 25 | this.oplog = oplog | |
| 26 | this.bigHeader = bigHeader | |
| 27 | this.tree = tree | |
| 28 | this.blocks = blocks | |
| 29 | this.bitfield = bitfield | |
| 30 | this.verifier = verifier | |
| 31 | this.truncating = 0 | |
| 32 | this.updating = false | |
| 33 | this.closed = false | |
| 34 | this.skipBitfield = null | |
| 35 | this.active = sessions.length | |
| 36 | this.sessions = sessions | |
| 37 | this.globalCache = globalCache | |
| 38 | ||
| 39 | this._manifestFlushed = !!header.manifest | |
| 40 | this._maxOplogSize = 65536 | |
| 41 | this._autoFlush = 1 | |
| 42 | this._verifies = null | |
| 43 | this._verifiesFlushed = null | |
| 44 | this._mutex = new Mutex() | |
| 45 | this._legacy = legacy | |
| 46 | } | |
| 47 | ||
| 48 | static async open (storage, opts = {}) { | |
| 49 | const oplogFile = storage('oplog') | |
| 50 | const treeFile = storage('tree') | |
| 51 | const bitfieldFile = storage('bitfield') | |
| 52 | const dataFile = storage('data') | |
| 53 | const headerFile = storage('header') | |
| 54 | ||
| 55 | try { | |
| 56 | return await this.resume(oplogFile, treeFile, bitfieldFile, dataFile, headerFile, opts) | |
| 57 | } catch (err) { | |
| 58 | await closeAll(oplogFile, treeFile, bitfieldFile, dataFile, headerFile) | |
| 59 | throw err | |
| 60 | } | |
| 61 | } | |
| 62 | ||
| 63 | static async resume (oplogFile, treeFile, bitfieldFile, dataFile, headerFile, opts) { | |
| 64 | let overwrite = opts.overwrite === true | |
| 65 | ||
| 66 | const force = opts.force === true | |
| 67 | const createIfMissing = opts.createIfMissing !== false | |
| 68 | const crypto = opts.crypto || hypercoreCrypto | |
| 69 | // kill this flag soon | |
| 70 | const legacy = !!opts.legacy | |
| 71 | ||
| 72 | const oplog = new Oplog(oplogFile, { | |
| 73 | headerEncoding: m.oplog.header, | |
| 74 | entryEncoding: m.oplog.entry, | |
| 75 | readonly: opts.readonly | |
| 76 | }) | |
| 77 | ||
| 78 | // default to true for now if no manifest is provided | |
| 79 | let compat = opts.compat === true || (opts.compat !== false && !opts.manifest) | |
| 80 | ||
| 81 | let { header, entries } = await oplog.open() | |
| 82 | ||
| 83 | if (force && opts.key && header && !b4a.equals(header.key, opts.key)) { | |
| 84 | overwrite = true | |
| 85 | } | |
| 86 | ||
| 87 | const bigHeader = new BigHeader(headerFile) | |
| 88 | ||
| 89 | if (!header || overwrite) { | |
| 90 | if (!createIfMissing) { | |
| 91 | throw STORAGE_EMPTY('No Hypercore is stored here') | |
| 92 | } | |
| 93 | ||
| 94 | if (compat) { | |
| 95 | if (opts.key && opts.keyPair && !b4a.equals(opts.key, opts.keyPair.publicKey)) { | |
| 96 | throw BAD_ARGUMENT('Key must match publicKey when in compat mode') | |
| 97 | } | |
| 98 | } | |
| 99 | ||
| 100 | const keyPair = opts.keyPair || (opts.key ? null : crypto.keyPair()) | |
| 101 | const defaultManifest = !opts.manifest && (!!opts.compat || !opts.key || !!(keyPair && b4a.equals(opts.key, keyPair.publicKey))) | |
| 102 | const manifest = defaultManifest ? Verifier.defaultSignerManifest(opts.key || keyPair.publicKey) : Verifier.createManifest(opts.manifest) | |
| 103 | ||
| 104 | header = { | |
| 105 | external: null, | |
| 106 | key: opts.key || (compat ? manifest.signers[0].publicKey : Verifier.manifestHash(manifest)), | |
| 107 | manifest, | |
| 108 | keyPair: keyPair ? { publicKey: keyPair.publicKey, secretKey: keyPair.secretKey || null } : null, | |
| 109 | userData: [], | |
| 110 | tree: { | |
| 111 | fork: 0, | |
| 112 | length: 0, | |
| 113 | rootHash: null, | |
| 114 | signature: null | |
| 115 | }, | |
| 116 | hints: { | |
| 117 | reorgs: [], | |
| 118 | contiguousLength: 0 | |
| 119 | } | |
| 120 | } | |
| 121 | ||
| 122 | await flushHeader(oplog, bigHeader, header) | |
| 123 | } else if (header.external) { | |
| 124 | header = await bigHeader.load(header.external) | |
| 125 | } | |
| 126 | ||
| 127 | // unslab the long lived buffers to avoid keeping the slab alive | |
| 128 | header.key = unslab(header.key) | |
| 129 | header.tree.rootHash = unslab(header.tree.rootHash) | |
| 130 | header.tree.signature = unslab(header.tree.signature) | |
| 131 | ||
| 132 | if (header.keyPair) { | |
| 133 | header.keyPair.publicKey = unslab(header.keyPair.publicKey) | |
| 134 | header.keyPair.secretKey = unslab(header.keyPair.secretKey) | |
| 135 | } | |
| 136 | ||
| 137 | if (opts.manifest) { | |
| 138 | // if we provide a manifest and no key, verify that the stored key is the same | |
| 139 | if (!opts.key && !Verifier.isValidManifest(header.key, Verifier.createManifest(opts.manifest))) { | |
| 140 | throw STORAGE_CONFLICT('Manifest does not hash to provided key') | |
| 141 | } | |
| 142 | ||
| 143 | if (!header.manifest) header.manifest = opts.manifest | |
| 144 | } | |
| 145 | ||
| 146 | if (opts.key && !b4a.equals(header.key, opts.key)) { | |
| 147 | throw STORAGE_CONFLICT('Another Hypercore is stored here') | |
| 148 | } | |
| 149 | ||
| 150 | // if we signalled compat, but already now this core isn't disable it | |
| 151 | if (compat && header.manifest && !Verifier.isCompat(header.key, header.manifest)) { | |
| 152 | compat = false | |
| 153 | } else if (!compat && header.manifest && Verifier.isCompat(header.key, header.manifest)) { | |
| 154 | compat = true | |
| 155 | } | |
| 156 | ||
| 157 | const prologue = header.manifest ? header.manifest.prologue : null | |
| 158 | ||
| 159 | const tree = await MerkleTree.open(treeFile, { crypto, prologue, ...header.tree }) | |
| 160 | const bitfield = await Bitfield.open(bitfieldFile) | |
| 161 | const blocks = new BlockStore(dataFile, tree) | |
| 162 | ||
| 163 | if (overwrite) { | |
| 164 | await tree.clear() | |
| 165 | await blocks.clear() | |
| 166 | await bitfield.clear() | |
| 167 | entries = [] | |
| 168 | } | |
| 169 | ||
| 170 | // compat from earlier version that do not store contig length | |
| 171 | if (header.hints.contiguousLength === 0) { | |
| 172 | while (bitfield.get(header.hints.contiguousLength)) header.hints.contiguousLength++ | |
| 173 | } | |
| 174 | ||
| 175 | // to unslab | |
| 176 | if (header.manifest) header.manifest = Verifier.createManifest(header.manifest) | |
| 177 | ||
| 178 | const verifier = header.manifest ? new Verifier(header.key, header.manifest, { crypto, legacy }) : null | |
| 179 | ||
| 180 | for (const e of entries) { | |
| 181 | if (e.userData) { | |
| 182 | updateUserData(header.userData, e.userData.key, e.userData.value) | |
| 183 | } | |
| 184 | ||
| 185 | if (e.treeNodes) { | |
| 186 | for (const node of e.treeNodes) { | |
| 187 | tree.addNode(node) | |
| 188 | } | |
| 189 | } | |
| 190 | ||
| 191 | if (e.bitfield) { | |
| 192 | bitfield.setRange(e.bitfield.start, e.bitfield.length, !e.bitfield.drop) | |
| 193 | updateContig(header, e.bitfield, bitfield) | |
| 194 | } | |
| 195 | ||
| 196 | if (e.treeUpgrade) { | |
| 197 | const batch = await tree.truncate(e.treeUpgrade.length, e.treeUpgrade.fork) | |
| 198 | batch.ancestors = e.treeUpgrade.ancestors | |
| 199 | batch.signature = unslab(e.treeUpgrade.signature) | |
| 200 | addReorgHint(header.hints.reorgs, tree, batch) | |
| 201 | batch.commit() | |
| 202 | ||
| 203 | header.tree.length = tree.length | |
| 204 | header.tree.fork = tree.fork | |
| 205 | header.tree.rootHash = tree.hash() | |
| 206 | header.tree.signature = tree.signature | |
| 207 | } | |
| 208 | } | |
| 209 | ||
| 210 | for (const entry of header.userData) { | |
| 211 | entry.value = unslab(entry.value) | |
| 212 | } | |
| 213 | ||
| 214 | return new this(header, compat, crypto, oplog, bigHeader, tree, blocks, bitfield, verifier, opts.sessions || [], legacy, opts.globalCache || null, opts.onupdate || noop, opts.onconflict || noop) | |
| 215 | } | |
| 216 | ||
| 217 | async audit () { | |
| 218 | await this._mutex.lock() | |
| 219 | ||
| 220 | try { | |
| 221 | await this._flushOplog() | |
| 222 | const corrections = await audit(this) | |
| 223 | if (corrections.blocks || corrections.tree) await this._flushOplog() | |
| 224 | return corrections | |
| 225 | } finally { | |
| 226 | await this._mutex.unlock() | |
| 227 | } | |
| 228 | } | |
| 229 | ||
| 230 | async setManifest (manifest) { | |
| 231 | await this._mutex.lock() | |
| 232 | ||
| 233 | try { | |
| 234 | if (manifest && this.header.manifest === null) { | |
| 235 | if (!Verifier.isValidManifest(this.header.key, manifest)) throw INVALID_CHECKSUM('Manifest hash does not match') | |
| 236 | this._setManifest(Verifier.createManifest(manifest), null) | |
| 237 | await this._flushOplog() | |
| 238 | } | |
| 239 | } finally { | |
| 240 | this._mutex.unlock() | |
| 241 | } | |
| 242 | } | |
| 243 | ||
| 244 | _setManifest (manifest, keyPair) { | |
| 245 | if (!manifest && b4a.equals(keyPair.publicKey, this.header.key)) manifest = Verifier.defaultSignerManifest(this.header.key) | |
| 246 | if (!manifest) return | |
| 247 | ||
| 248 | const verifier = new Verifier(this.header.key, manifest, { crypto: this.crypto, legacy: this._legacy }) | |
| 249 | ||
| 250 | if (verifier.prologue) this.tree.setPrologue(verifier.prologue) | |
| 251 | ||
| 252 | this.header.manifest = manifest | |
| 253 | this.compat = verifier.compat | |
| 254 | this.verifier = verifier | |
| 255 | this._manifestFlushed = false | |
| 256 | ||
| 257 | this.onupdate(0b10000, null, null, null) | |
| 258 | } | |
| 259 | ||
| 260 | _shouldFlush () { | |
| 261 | // TODO: make something more fancy for auto flush mode (like fibonacci etc) | |
| 262 | if (--this._autoFlush <= 0 || this.oplog.byteLength >= this._maxOplogSize) { | |
| 263 | this._autoFlush = 4 | |
| 264 | return true | |
| 265 | } | |
| 266 | ||
| 267 | if (!this._manifestFlushed && this.header.manifest) { | |
| 268 | this._manifestFlushed = true | |
| 269 | return true | |
| 270 | } | |
| 271 | ||
| 272 | return false | |
| 273 | } | |
| 274 | ||
| 275 | async copyPrologue (src, { additional = [] } = {}) { | |
| 276 | await this._mutex.lock() | |
| 277 | ||
| 278 | try { | |
| 279 | await src._mutex.lock() | |
| 280 | } catch (err) { | |
| 281 | this._mutex.unlock() | |
| 282 | throw err | |
| 283 | } | |
| 284 | ||
| 285 | try { | |
| 286 | const prologue = this.header.manifest && this.header.manifest.prologue | |
| 287 | if (!prologue) throw INVALID_OPERATION('No prologue present') | |
| 288 | ||
| 289 | const srcLength = prologue.length - additional.length | |
| 290 | const srcBatch = srcLength !== src.tree.length ? await src.tree.truncate(srcLength) : src.tree.batch() | |
| 291 | const srcRoots = srcBatch.roots.slice(0) | |
| 292 | const srcByteLength = srcBatch.byteLength | |
| 293 | ||
| 294 | for (const blk of additional) srcBatch.append(blk) | |
| 295 | ||
| 296 | if (!b4a.equals(srcBatch.hash(), prologue.hash)) throw INVALID_OPERATION('Source tree is conflicting') | |
| 297 | ||
| 298 | // all hashes are correct, lets copy | |
| 299 | ||
| 300 | const entry = { | |
| 301 | userData: null, | |
| 302 | treeNodes: srcRoots, | |
| 303 | treeUpgrade: null, | |
| 304 | bitfield: null | |
| 305 | } | |
| 306 | ||
| 307 | if (additional.length) { | |
| 308 | await this.blocks.putBatch(srcLength, additional, srcByteLength) | |
| 309 | entry.treeNodes = entry.treeNodes.concat(srcBatch.nodes) | |
| 310 | entry.bitfield = { | |
| 311 | drop: false, | |
| 312 | start: srcLength, | |
| 313 | length: additional.length | |
| 314 | } | |
| 315 | } | |
| 316 | ||
| 317 | await this.oplog.append([entry], false) | |
| 318 | this.tree.addNodes(entry.treeNodes) | |
| 319 | ||
| 320 | if (this.header.tree.length < srcBatch.length) { | |
| 321 | this.header.tree.length = srcBatch.length | |
| 322 | this.header.tree.rootHash = srcBatch.hash() | |
| 323 | ||
| 324 | this.tree.length = srcBatch.length | |
| 325 | this.tree.byteLength = srcBatch.byteLength | |
| 326 | this.tree.roots = srcBatch.roots | |
| 327 | this.onupdate(0b0001, null, null, null) | |
| 328 | } | |
| 329 | ||
| 330 | if (entry.bitfield) { | |
| 331 | this._setBitfieldRange(entry.bitfield.start, entry.bitfield.length, true) | |
| 332 | this.onupdate(0, entry.bitfield, null, null) | |
| 333 | } | |
| 334 | ||
| 335 | await this._flushOplog() | |
| 336 | ||
| 337 | // no more additional blocks now and we should be consistant on disk | |
| 338 | // copy over all existing segments... | |
| 339 | ||
| 340 | let segmentEnd = 0 | |
| 341 | ||
| 342 | while (segmentEnd < srcLength) { | |
| 343 | const segmentStart = maximumSegmentStart(segmentEnd, src.bitfield, this.bitfield) | |
| 344 | if (segmentStart >= srcLength || segmentStart < 0) break | |
| 345 | ||
| 346 | // max segment is 65536 to avoid running out of memory | |
| 347 | segmentEnd = Math.min(segmentStart + 65536, srcLength, minimumSegmentEnd(segmentStart, src.bitfield, this.bitfield)) | |
| 348 | ||
| 349 | const treeNodes = await src.tree.getNeededNodes(srcLength, segmentStart, segmentEnd) | |
| 350 | const bitfield = { | |
| 351 | drop: false, | |
| 352 | start: segmentStart, | |
| 353 | length: segmentEnd - segmentStart | |
| 354 | } | |
| 355 | ||
| 356 | const segment = [] | |
| 357 | for (let i = segmentStart; i < segmentEnd; i++) { | |
| 358 | const blk = await src.blocks.get(i) | |
| 359 | segment.push(blk) | |
| 360 | } | |
| 361 | ||
| 362 | const offset = await src.tree.byteOffset(2 * segmentStart) | |
| 363 | await this.blocks.putBatch(segmentStart, segment, offset) | |
| 364 | ||
| 365 | const entry = { | |
| 366 | userData: null, | |
| 367 | treeNodes, | |
| 368 | treeUpgrade: null, | |
| 369 | bitfield | |
| 370 | } | |
| 371 | ||
| 372 | await this.oplog.append([entry], false) | |
| 373 | this.tree.addNodes(treeNodes) | |
| 374 | this._setBitfieldRange(bitfield.start, bitfield.length, true) | |
| 375 | this.onupdate(0, bitfield, null, null) | |
| 376 | await this._flushOplog() | |
| 377 | } | |
| 378 | ||
| 379 | this.header.userData = src.header.userData.slice(0) | |
| 380 | const contig = Math.min(src.header.hints.contiguousLength, srcBatch.length) | |
| 381 | if (this.header.hints.contiguousLength < contig) this.header.hints.contiguousLength = contig | |
| 382 | ||
| 383 | await this._flushOplog() | |
| 384 | } finally { | |
| 385 | src._mutex.unlock() | |
| 386 | this._mutex.unlock() | |
| 387 | } | |
| 388 | } | |
| 389 | ||
| 390 | async flush () { | |
| 391 | await this._mutex.lock() | |
| 392 | try { | |
| 393 | this._manifestFlushed = true | |
| 394 | this._autoFlush = 4 | |
| 395 | await this._flushOplog() | |
| 396 | } finally { | |
| 397 | this._mutex.unlock() | |
| 398 | } | |
| 399 | } | |
| 400 | ||
| 401 | async _flushOplog () { | |
| 402 | // TODO: the apis using this, actually do not need to wait for the bitfields, tree etc to flush | |
| 403 | // as their mutations are already stored in the oplog. We could potentially just run this in the | |
| 404 | // background. Might be easier to impl that where it is called instead and keep this one simple. | |
| 405 | await this.bitfield.flush() | |
| 406 | await this.tree.flush() | |
| 407 | ||
| 408 | return flushHeader(this.oplog, this.bigHeader, this.header) | |
| 409 | } | |
| 410 | ||
| 411 | _appendBlocks (values) { | |
| 412 | return this.blocks.putBatch(this.tree.length, values, this.tree.byteLength) | |
| 413 | } | |
| 414 | ||
| 415 | async _writeBlock (batch, index, value) { | |
| 416 | const byteOffset = await batch.byteOffset(index * 2) | |
| 417 | await this.blocks.put(index, value, byteOffset) | |
| 418 | } | |
| 419 | ||
| 420 | async userData (key, value, flush) { | |
| 421 | // TODO: each oplog append can set user data, so we should have a way | |
| 422 | // to just hitch a ride on one of the other ongoing appends? | |
| 423 | await this._mutex.lock() | |
| 424 | ||
| 425 | try { | |
| 426 | let empty = true | |
| 427 | ||
| 428 | for (const u of this.header.userData) { | |
| 429 | if (u.key !== key) continue | |
| 430 | if (value && b4a.equals(u.value, value)) return | |
| 431 | empty = false | |
| 432 | break | |
| 433 | } | |
| 434 | ||
| 435 | if (empty && !value) return | |
| 436 | ||
| 437 | const entry = { | |
| 438 | userData: { key, value }, | |
| 439 | treeNodes: null, | |
| 440 | treeUpgrade: null, | |
| 441 | bitfield: null | |
| 442 | } | |
| 443 | ||
| 444 | await this.oplog.append([entry], false) | |
| 445 | ||
| 446 | updateUserData(this.header.userData, key, value) | |
| 447 | ||
| 448 | if (this._shouldFlush() || flush) await this._flushOplog() | |
| 449 | } finally { | |
| 450 | this._mutex.unlock() | |
| 451 | } | |
| 452 | } | |
| 453 | ||
| 454 | async truncate (length, fork, { signature, keyPair = this.header.keyPair } = {}) { | |
| 455 | if (this.tree.prologue && length < this.tree.prologue.length) { | |
| 456 | throw INVALID_OPERATION('Truncation breaks prologue') | |
| 457 | } | |
| 458 | ||
| 459 | this.truncating++ | |
| 460 | await this._mutex.lock() | |
| 461 | ||
| 462 | // upsert compat manifest | |
| 463 | if (this.verifier === null && keyPair) this._setManifest(null, keyPair) | |
| 464 | ||
| 465 | try { | |
| 466 | const batch = await this.tree.truncate(length, fork) | |
| 467 | if (length > 0) batch.signature = signature || this.verifier.sign(batch, keyPair) | |
| 468 | await this._truncate(batch, null) | |
| 469 | } finally { | |
| 470 | this.truncating-- | |
| 471 | this._mutex.unlock() | |
| 472 | } | |
| 473 | } | |
| 474 | ||
| 475 | async clearBatch () { | |
| 476 | await this._mutex.lock() | |
| 477 | ||
| 478 | try { | |
| 479 | const len = this.bitfield.findFirst(false, this.tree.length) | |
| 480 | if (len <= this.tree.length) return | |
| 481 | ||
| 482 | const batch = await this.tree.truncate(this.tree.length, this.tree.fork) | |
| 483 | ||
| 484 | batch.signature = this.tree.signature // same sig | |
| 485 | ||
| 486 | const entry = { | |
| 487 | userData: null, | |
| 488 | treeNodes: batch.nodes, | |
| 489 | treeUpgrade: batch, | |
| 490 | bitfield: { | |
| 491 | drop: true, | |
| 492 | start: batch.ancestors, | |
| 493 | length: len - batch.ancestors | |
| 494 | } | |
| 495 | } | |
| 496 | ||
| 497 | await this.oplog.append([entry], false) | |
| 498 | ||
| 499 | this._setBitfieldRange(batch.ancestors, len - batch.ancestors, false) | |
| 500 | batch.commit() | |
| 501 | ||
| 502 | // TODO: (see below todo) | |
| 503 | await this._flushOplog() | |
| 504 | } finally { | |
| 505 | this._mutex.unlock() | |
| 506 | } | |
| 507 | } | |
| 508 | ||
| 509 | async clear (start, end, cleared) { | |
| 510 | await this._mutex.lock() | |
| 511 | ||
| 512 | try { | |
| 513 | const entry = { | |
| 514 | userData: null, | |
| 515 | treeNodes: null, | |
| 516 | treeUpgrade: null, | |
| 517 | bitfield: { | |
| 518 | start, | |
| 519 | length: end - start, | |
| 520 | drop: true | |
| 521 | } | |
| 522 | } | |
| 523 | ||
| 524 | await this.oplog.append([entry], false) | |
| 525 | ||
| 526 | this._setBitfieldRange(start, end - start, false) | |
| 527 | ||
| 528 | if (start < this.header.hints.contiguousLength) { | |
| 529 | this.header.hints.contiguousLength = start | |
| 530 | } | |
| 531 | ||
| 532 | start = this.bitfield.lastSet(start) + 1 | |
| 533 | end = this.bitfield.firstSet(end) | |
| 534 | ||
| 535 | if (end === -1) end = this.tree.length | |
| 536 | if (start >= end || start >= this.tree.length) return | |
| 537 | ||
| 538 | const offset = await this.tree.byteOffset(start * 2) | |
| 539 | const endOffset = await this.tree.byteOffset(end * 2) | |
| 540 | const length = endOffset - offset | |
| 541 | ||
| 542 | const before = cleared ? await Info.bytesUsed(this.blocks.storage) : null | |
| 543 | ||
| 544 | await this.blocks.clear(offset, length) | |
| 545 | ||
| 546 | const after = cleared ? await Info.bytesUsed(this.blocks.storage) : null | |
| 547 | ||
| 548 | if (cleared) cleared.blocks = Math.max(before - after, 0) | |
| 549 | ||
| 550 | this.onupdate(0, entry.bitfield, null, null) | |
| 551 | ||
| 552 | if (this._shouldFlush()) await this._flushOplog() | |
| 553 | } finally { | |
| 554 | this._mutex.unlock() | |
| 555 | } | |
| 556 | } | |
| 557 | ||
| 558 | async purge () { | |
| 559 | return new Promise((resolve, reject) => { | |
| 560 | let missing = 4 | |
| 561 | let error = null | |
| 562 | ||
| 563 | this.oplog.storage.unlink(done) | |
| 564 | this.tree.storage.unlink(done) | |
| 565 | this.bitfield.storage.unlink(done) | |
| 566 | this.blocks.storage.unlink(done) | |
| 567 | ||
| 568 | function done (err) { | |
| 569 | if (err) error = err | |
| 570 | if (--missing) return | |
| 571 | if (error) reject(error) | |
| 572 | else resolve() | |
| 573 | } | |
| 574 | }) | |
| 575 | } | |
| 576 | ||
| 577 | async insertBatch (batch, values, { signature, keyPair = this.header.keyPair, pending = false, treeLength = batch.treeLength } = {}) { | |
| 578 | await this._mutex.lock() | |
| 579 | ||
| 580 | try { | |
| 581 | // upsert compat manifest | |
| 582 | if (this.verifier === null && keyPair) this._setManifest(null, keyPair) | |
| 583 | ||
| 584 | if (this.tree.fork !== batch.fork) return null | |
| 585 | ||
| 586 | if (this.tree.length > batch.treeLength) { | |
| 587 | if (this.tree.length > batch.length) return null // TODO: partial commit in the future if possible | |
| 588 | ||
| 589 | for (const root of this.tree.roots) { | |
| 590 | const batchRoot = await batch.get(root.index) | |
| 591 | if (batchRoot.size !== root.size || !b4a.equals(batchRoot.hash, root.hash)) { | |
| 592 | return null | |
| 593 | } | |
| 594 | } | |
| 595 | } | |
| 596 | ||
| 597 | const adding = batch.length - treeLength | |
| 598 | ||
| 599 | batch.upgraded = !pending && batch.length > this.tree.length | |
| 600 | batch.treeLength = this.tree.length | |
| 601 | batch.ancestors = this.tree.length | |
| 602 | if (batch.upgraded && !pending) batch.signature = signature || this.verifier.sign(batch, keyPair) | |
| 603 | ||
| 604 | let byteOffset = batch.byteLength | |
| 605 | for (let i = 0; i < adding; i++) byteOffset -= values[i].byteLength | |
| 606 | ||
| 607 | if (pending === true) batch.upgraded = false | |
| 608 | ||
| 609 | const entry = { | |
| 610 | userData: null, | |
| 611 | treeNodes: batch.nodes, | |
| 612 | treeUpgrade: batch.upgraded ? batch : null, | |
| 613 | bitfield: { | |
| 614 | drop: false, | |
| 615 | start: treeLength, | |
| 616 | length: adding | |
| 617 | } | |
| 618 | } | |
| 619 | ||
| 620 | await this.blocks.putBatch(treeLength, adding < values.length ? values.slice(0, adding) : values, byteOffset) | |
| 621 | await this.oplog.append([entry], false) | |
| 622 | ||
| 623 | this._setBitfieldRange(entry.bitfield.start, entry.bitfield.length, true) | |
| 624 | batch.commit() | |
| 625 | ||
| 626 | if (batch.upgraded) { | |
| 627 | this.header.tree.length = batch.length | |
| 628 | this.header.tree.rootHash = batch.hash() | |
| 629 | this.header.tree.signature = batch.signature | |
| 630 | } | |
| 631 | ||
| 632 | const status = (batch.upgraded ? 0b0001 : 0) | updateContig(this.header, entry.bitfield, this.bitfield) | |
| 633 | if (!pending) { | |
| 634 | // we already commit this, and now we signed it, so tell others | |
| 635 | if (entry.treeUpgrade && treeLength > batch.treeLength) { | |
| 636 | entry.bitfield.start = batch.treeLength | |
| 637 | entry.bitfield.length = treeLength - batch.treeLength | |
| 638 | } | |
| 639 | ||
| 640 | this.onupdate(status, entry.bitfield, null, null) | |
| 641 | } | |
| 642 | ||
| 643 | if (this._shouldFlush()) await this._flushOplog() | |
| 644 | } finally { | |
| 645 | this._mutex.unlock() | |
| 646 | } | |
| 647 | ||
| 648 | return { length: batch.length, byteLength: batch.byteLength } | |
| 649 | } | |
| 650 | ||
| 651 | async append (values, { signature, keyPair = this.header.keyPair, preappend } = {}) { | |
| 652 | await this._mutex.lock() | |
| 653 | ||
| 654 | try { | |
| 655 | // upsert compat manifest | |
| 656 | if (this.verifier === null && keyPair) this._setManifest(null, keyPair) | |
| 657 | ||
| 658 | if (preappend) await preappend(values) | |
| 659 | ||
| 660 | if (!values.length) { | |
| 661 | return { length: this.tree.length, byteLength: this.tree.byteLength } | |
| 662 | } | |
| 663 | ||
| 664 | const batch = this.tree.batch() | |
| 665 | for (const val of values) batch.append(val) | |
| 666 | ||
| 667 | // only multisig can have prologue so signature is always present | |
| 668 | if (this.tree.prologue && batch.length < this.tree.prologue.length) { | |
| 669 | throw INVALID_OPERATION('Append is not consistent with prologue') | |
| 670 | } | |
| 671 | ||
| 672 | batch.signature = signature || this.verifier.sign(batch, keyPair) | |
| 673 | ||
| 674 | const entry = { | |
| 675 | userData: null, | |
| 676 | treeNodes: batch.nodes, | |
| 677 | treeUpgrade: batch, | |
| 678 | bitfield: { | |
| 679 | drop: false, | |
| 680 | start: batch.ancestors, | |
| 681 | length: values.length | |
| 682 | } | |
| 683 | } | |
| 684 | ||
| 685 | const byteLength = await this._appendBlocks(values) | |
| 686 | ||
| 687 | await this.oplog.append([entry], false) | |
| 688 | ||
| 689 | this._setBitfieldRange(batch.ancestors, batch.length - batch.ancestors, true) | |
| 690 | batch.commit() | |
| 691 | ||
| 692 | this.header.tree.length = batch.length | |
| 693 | this.header.tree.rootHash = batch.hash() | |
| 694 | this.header.tree.signature = batch.signature | |
| 695 | ||
| 696 | const status = 0b0001 | updateContig(this.header, entry.bitfield, this.bitfield) | |
| 697 | this.onupdate(status, entry.bitfield, null, null) | |
| 698 | ||
| 699 | if (this._shouldFlush()) await this._flushOplog() | |
| 700 | ||
| 701 | return { length: batch.length, byteLength } | |
| 702 | } finally { | |
| 703 | this._mutex.unlock() | |
| 704 | } | |
| 705 | } | |
| 706 | ||
| 707 | _verifyBatchUpgrade (batch, manifest) { | |
| 708 | if (!this.header.manifest) { | |
| 709 | if (!manifest && this.compat) manifest = Verifier.defaultSignerManifest(this.header.key) | |
| 710 | ||
| 711 | if (!manifest || !(Verifier.isValidManifest(this.header.key, manifest) || (this.compat && Verifier.isCompat(this.header.key, manifest)))) { | |
| 712 | throw INVALID_SIGNATURE('Proof contains an invalid manifest') // TODO: proper error type | |
| 713 | } | |
| 714 | } | |
| 715 | ||
| 716 | manifest = Verifier.createManifest(manifest) // To unslab | |
| 717 | ||
| 718 | const verifier = this.verifier || new Verifier(this.header.key, manifest, { crypto: this.crypto, legacy: this._legacy }) | |
| 719 | ||
| 720 | if (!verifier.verify(batch, batch.signature)) { | |
| 721 | throw INVALID_SIGNATURE('Proof contains an invalid signature') | |
| 722 | } | |
| 723 | ||
| 724 | if (!this.header.manifest) { | |
| 725 | this.header.manifest = manifest | |
| 726 | this.compat = verifier.compat | |
| 727 | this.verifier = verifier | |
| 728 | this.onupdate(0b10000, null, null, null) | |
| 729 | } | |
| 730 | } | |
| 731 | ||
| 732 | async _verifyExclusive ({ batch, bitfield, value, manifest, from }) { | |
| 733 | this._verifyBatchUpgrade(batch, manifest) | |
| 734 | ||
| 735 | await this._mutex.lock() | |
| 736 | ||
| 737 | try { | |
| 738 | if (!batch.commitable()) return false | |
| 739 | this.updating = true | |
| 740 | ||
| 741 | const entry = { | |
| 742 | userData: null, | |
| 743 | treeNodes: batch.nodes, | |
| 744 | treeUpgrade: batch, | |
| 745 | bitfield | |
| 746 | } | |
| 747 | ||
| 748 | if (this.preupdate !== null) await this.preupdate(batch, this.header.key) | |
| 749 | if (bitfield) await this._writeBlock(batch, bitfield.start, value) | |
| 750 | ||
| 751 | await this.oplog.append([entry], false) | |
| 752 | ||
| 753 | let status = 0b0001 | |
| 754 | ||
| 755 | if (bitfield) { | |
| 756 | this._setBitfield(bitfield.start, true) | |
| 757 | status |= updateContig(this.header, bitfield, this.bitfield) | |
| 758 | } | |
| 759 | ||
| 760 | batch.commit() | |
| 761 | ||
| 762 | this.header.tree.fork = batch.fork | |
| 763 | this.header.tree.length = batch.length | |
| 764 | this.header.tree.rootHash = batch.hash() | |
| 765 | this.header.tree.signature = batch.signature | |
| 766 | ||
| 767 | this.onupdate(status, bitfield, value, from) | |
| 768 | ||
| 769 | if (this._shouldFlush()) await this._flushOplog() | |
| 770 | } finally { | |
| 771 | this.updating = false | |
| 772 | this._mutex.unlock() | |
| 773 | } | |
| 774 | ||
| 775 | return true | |
| 776 | } | |
| 777 | ||
| 778 | async _verifyShared () { | |
| 779 | if (!this._verifies.length) return false | |
| 780 | ||
| 781 | await this._mutex.lock() | |
| 782 | ||
| 783 | const verifies = this._verifies | |
| 784 | this._verifies = null | |
| 785 | this._verified = null | |
| 786 | ||
| 787 | try { | |
| 788 | const entries = [] | |
| 789 | ||
| 790 | for (const { batch, bitfield, value } of verifies) { | |
| 791 | if (!batch.commitable()) continue | |
| 792 | ||
| 793 | if (bitfield) { | |
| 794 | await this._writeBlock(batch, bitfield.start, value) | |
| 795 | } | |
| 796 | ||
| 797 | entries.push({ | |
| 798 | userData: null, | |
| 799 | treeNodes: batch.nodes, | |
| 800 | treeUpgrade: null, | |
| 801 | bitfield | |
| 802 | }) | |
| 803 | } | |
| 804 | ||
| 805 | await this.oplog.append(entries, false) | |
| 806 | ||
| 807 | for (let i = 0; i < verifies.length; i++) { | |
| 808 | const { batch, bitfield, value, manifest, from } = verifies[i] | |
| 809 | ||
| 810 | if (!batch.commitable()) { | |
| 811 | verifies[i] = null // signal that we cannot commit this one | |
| 812 | continue | |
| 813 | } | |
| 814 | ||
| 815 | let status = 0 | |
| 816 | ||
| 817 | if (bitfield) { | |
| 818 | this._setBitfield(bitfield.start, true) | |
| 819 | status = updateContig(this.header, bitfield, this.bitfield) | |
| 820 | } | |
| 821 | ||
| 822 | // if we got a manifest AND its strictly a non compat one, lets store it | |
| 823 | if (manifest && this.header.manifest === null) { | |
| 824 | if (!Verifier.isValidManifest(this.header.key, manifest)) throw INVALID_CHECKSUM('Manifest hash does not match') | |
| 825 | this._setManifest(manifest, null) | |
| 826 | } | |
| 827 | ||
| 828 | batch.commit() | |
| 829 | ||
| 830 | this.onupdate(status, bitfield, value, from) | |
| 831 | } | |
| 832 | ||
| 833 | if (this._shouldFlush()) await this._flushOplog() | |
| 834 | } finally { | |
| 835 | this._mutex.unlock() | |
| 836 | } | |
| 837 | ||
| 838 | return verifies[0] !== null | |
| 839 | } | |
| 840 | ||
| 841 | async checkConflict (proof, from) { | |
| 842 | if (this.tree.length < proof.upgrade.length || proof.fork !== this.tree.fork) { | |
| 843 | // out of date this proof - ignore for now | |
| 844 | return false | |
| 845 | } | |
| 846 | ||
| 847 | const batch = this.tree.verifyFullyRemote(proof) | |
| 848 | ||
| 849 | try { | |
| 850 | this._verifyBatchUpgrade(batch, proof.manifest) | |
| 851 | } catch { | |
| 852 | return true | |
| 853 | } | |
| 854 | ||
| 855 | const remoteTreeHash = this.crypto.tree(proof.upgrade.nodes) | |
| 856 | const localTreeHash = this.crypto.tree(await this.tree.getRoots(proof.upgrade.length)) | |
| 857 | ||
| 858 | if (b4a.equals(localTreeHash, remoteTreeHash)) return false | |
| 859 | ||
| 860 | await this.onconflict(proof) | |
| 861 | return true | |
| 862 | } | |
| 863 | ||
| 864 | async verifyReorg (proof) { | |
| 865 | const batch = await this.tree.reorg(proof) | |
| 866 | ||
| 867 | this._verifyBatchUpgrade(batch, proof.manifest) | |
| 868 | ||
| 869 | return batch | |
| 870 | } | |
| 871 | ||
| 872 | async verify (proof, from) { | |
| 873 | // We cannot apply "other forks" atm. | |
| 874 | // We should probably still try and they are likely super similar for non upgrades | |
| 875 | // but this is easy atm (and the above layer will just retry) | |
| 876 | if (proof.fork !== this.tree.fork) return false | |
| 877 | ||
| 878 | const batch = await this.tree.verify(proof) | |
| 879 | if (!batch.commitable()) return false | |
| 880 | ||
| 881 | const value = (proof.block && proof.block.value) || null | |
| 882 | const op = { | |
| 883 | batch, | |
| 884 | bitfield: value && { drop: false, start: proof.block.index, length: 1 }, | |
| 885 | value, | |
| 886 | manifest: proof.manifest, | |
| 887 | from | |
| 888 | } | |
| 889 | ||
| 890 | if (batch.upgraded) return this._verifyExclusive(op) | |
| 891 | ||
| 892 | if (this._verifies !== null) { | |
| 893 | const verifies = this._verifies | |
| 894 | const i = verifies.push(op) | |
| 895 | await this._verified | |
| 896 | return verifies[i] !== null | |
| 897 | } | |
| 898 | ||
| 899 | this._verifies = [op] | |
| 900 | this._verified = this._verifyShared() | |
| 901 | return this._verified | |
| 902 | } | |
| 903 | ||
| 904 | async reorg (batch, from) { | |
| 905 | if (!batch.commitable()) return false | |
| 906 | ||
| 907 | this.truncating++ | |
| 908 | await this._mutex.lock() | |
| 909 | ||
| 910 | try { | |
| 911 | if (!batch.commitable()) return false | |
| 912 | await this._truncate(batch, from) | |
| 913 | } finally { | |
| 914 | this.truncating-- | |
| 915 | this._mutex.unlock() | |
| 916 | } | |
| 917 | ||
| 918 | return true | |
| 919 | } | |
| 920 | ||
| 921 | async _truncate (batch, from) { | |
| 922 | const entry = { | |
| 923 | userData: null, | |
| 924 | treeNodes: batch.nodes, | |
| 925 | treeUpgrade: batch, | |
| 926 | bitfield: { | |
| 927 | drop: true, | |
| 928 | start: batch.ancestors, | |
| 929 | length: this.tree.length - batch.ancestors | |
| 930 | } | |
| 931 | } | |
| 932 | ||
| 933 | await this.oplog.append([entry], false) | |
| 934 | ||
| 935 | this._setBitfieldRange(batch.ancestors, this.tree.length - batch.ancestors, false) | |
| 936 | addReorgHint(this.header.hints.reorgs, this.tree, batch) | |
| 937 | batch.commit() | |
| 938 | ||
| 939 | const contigStatus = updateContig(this.header, entry.bitfield, this.bitfield) | |
| 940 | const status = ((batch.length > batch.ancestors) ? 0b0011 : 0b0010) | contigStatus | |
| 941 | ||
| 942 | this.header.tree.fork = batch.fork | |
| 943 | this.header.tree.length = batch.length | |
| 944 | this.header.tree.rootHash = batch.hash() | |
| 945 | this.header.tree.signature = batch.signature | |
| 946 | ||
| 947 | this.onupdate(status, entry.bitfield, null, from) | |
| 948 | ||
| 949 | // TODO: there is a bug in the merkle tree atm where it cannot handle unflushed | |
| 950 | // truncates if we append or download anything after the truncation point later on | |
| 951 | // This is because tree.get checks the truncated flag. We should fix this so we can do | |
| 952 | // the later flush here as well | |
| 953 | // if (this._shouldFlush()) await this._flushOplog() | |
| 954 | await this._flushOplog() | |
| 955 | } | |
| 956 | ||
| 957 | openSkipBitfield () { | |
| 958 | if (this.skipBitfield !== null) return this.skipBitfield | |
| 959 | this.skipBitfield = new RemoteBitfield() | |
| 960 | const buf = this.bitfield.toBuffer(this.tree.length) | |
| 961 | const bitfield = new Uint32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4) | |
| 962 | this.skipBitfield.insert(0, bitfield) | |
| 963 | return this.skipBitfield | |
| 964 | } | |
| 965 | ||
| 966 | _setBitfield (index, value) { | |
| 967 | this.bitfield.set(index, value) | |
| 968 | if (this.skipBitfield !== null) this.skipBitfield.set(index, value) | |
| 969 | } | |
| 970 | ||
| 971 | _setBitfieldRange (start, length, value) { | |
| 972 | this.bitfield.setRange(start, length, value) | |
| 973 | if (this.skipBitfield !== null) this.skipBitfield.setRange(start, length, value) | |
| 974 | } | |
| 975 | ||
| 976 | async close () { | |
| 977 | this.closed = true | |
| 978 | await this._mutex.destroy() | |
| 979 | await Promise.allSettled([ | |
| 980 | this.oplog.close(), | |
| 981 | this.bitfield.close(), | |
| 982 | this.tree.close(), | |
| 983 | this.blocks.close(), | |
| 984 | this.bigHeader.close() | |
| 985 | ]) | |
| 986 | } | |
| 987 | } | |
| 988 | ||
| 989 | function updateContig (header, upd, bitfield) { | |
| 990 | const end = upd.start + upd.length | |
| 991 | ||
| 992 | let c = header.hints.contiguousLength | |
| 993 | ||
| 994 | if (upd.drop) { | |
| 995 | // If we dropped a block in the current contig range, "downgrade" it | |
| 996 | if (c <= end && c > upd.start) { | |
| 997 | c = upd.start | |
| 998 | } | |
| 999 | } else { | |
| 1000 | if (c <= end && c >= upd.start) { | |
| 1001 | c = end | |
| 1002 | while (bitfield.get(c)) c++ | |
| 1003 | } | |
| 1004 | } | |
| 1005 | ||
| 1006 | if (c === header.hints.contiguousLength) { | |
| 1007 | return 0b0000 | |
| 1008 | } | |
| 1009 | ||
| 1010 | if (c > header.hints.contiguousLength) { | |
| 1011 | header.hints.contiguousLength = c | |
| 1012 | return 0b0100 | |
| 1013 | } | |
| 1014 | ||
| 1015 | header.hints.contiguousLength = c | |
| 1016 | return 0b1000 | |
| 1017 | } | |
| 1018 | ||
| 1019 | function addReorgHint (list, tree, batch) { | |
| 1020 | if (tree.length === 0 || tree.fork === batch.fork) return | |
| 1021 | ||
| 1022 | while (list.length >= 4) list.shift() // 4 here is arbitrary, just want it to be small (hints only) | |
| 1023 | while (list.length > 0) { | |
| 1024 | if (list[list.length - 1].ancestors > batch.ancestors) list.pop() | |
| 1025 | else break | |
| 1026 | } | |
| 1027 | ||
| 1028 | list.push({ from: tree.fork, to: batch.fork, ancestors: batch.ancestors }) | |
| 1029 | } | |
| 1030 | ||
| 1031 | function updateUserData (list, key, value) { | |
| 1032 | value = unslab(value) | |
| 1033 | ||
| 1034 | for (let i = 0; i < list.length; i++) { | |
| 1035 | if (list[i].key === key) { | |
| 1036 | if (value) list[i].value = value | |
| 1037 | else list.splice(i, 1) | |
| 1038 | return | |
| 1039 | } | |
| 1040 | } | |
| 1041 | if (value) list.push({ key, value }) | |
| 1042 | } | |
| 1043 | ||
| 1044 | function closeAll (...storages) { | |
| 1045 | let missing = 1 | |
| 1046 | let error = null | |
| 1047 | ||
| 1048 | return new Promise((resolve, reject) => { | |
| 1049 | for (const s of storages) { | |
| 1050 | missing++ | |
| 1051 | s.close(done) | |
| 1052 | } | |
| 1053 | ||
| 1054 | done(null) | |
| 1055 | ||
| 1056 | function done (err) { | |
| 1057 | if (err) error = err | |
| 1058 | if (--missing) return | |
| 1059 | if (error) reject(error) | |
| 1060 | else resolve() | |
| 1061 | } | |
| 1062 | }) | |
| 1063 | } | |
| 1064 | ||
| 1065 | async function flushHeader (oplog, bigHeader, header) { | |
| 1066 | if (header.external) { | |
| 1067 | await bigHeader.flush(header) | |
| 1068 | } | |
| 1069 | ||
| 1070 | try { | |
| 1071 | await oplog.flush(header) | |
| 1072 | } catch (err) { | |
| 1073 | if (err.code !== 'OPLOG_HEADER_OVERFLOW') throw err | |
| 1074 | await bigHeader.flush(header) | |
| 1075 | await oplog.flush(header) | |
| 1076 | } | |
| 1077 | } | |
| 1078 | ||
| 1079 | function noop () {} | |
| 1080 | ||
| 1081 | function maximumSegmentStart (start, src, dst) { | |
| 1082 | while (true) { | |
| 1083 | const a = src.firstSet(start) | |
| 1084 | const b = dst.firstUnset(start) | |
| 1085 | ||
| 1086 | if (a === -1) return -1 | |
| 1087 | if (b === -1) return a | |
| 1088 | ||
| 1089 | // if dst has the segment, restart | |
| 1090 | if (a < b) { | |
| 1091 | start = b | |
| 1092 | continue | |
| 1093 | } | |
| 1094 | ||
| 1095 | return a | |
| 1096 | } | |
| 1097 | } | |
| 1098 | ||
| 1099 | function minimumSegmentEnd (start, src, dst) { | |
| 1100 | const a = src.firstUnset(start) | |
| 1101 | const b = dst.firstSet(start) | |
| 1102 | ||
| 1103 | if (a === -1) return -1 | |
| 1104 | if (b === -1) return a | |
| 1105 | return a < b ? a : b | |
| 1106 | } |
| … | … | @@ -0,0 +1,22 @@ |
| 1 | module.exports = class Download { | |
| 2 | constructor (req) { | |
| 3 | this.req = req | |
| 4 | } | |
| 5 | ||
| 6 | async done () { | |
| 7 | return (await this.req).promise | |
| 8 | } | |
| 9 | ||
| 10 | /** | |
| 11 | * Deprecated. Use `range.done()`. | |
| 12 | */ | |
| 13 | downloaded () { | |
| 14 | return this.done() | |
| 15 | } | |
| 16 | ||
| 17 | destroy () { | |
| 18 | this.req.then(req => req.context && req.context.detach(req), noop) | |
| 19 | } | |
| 20 | } | |
| 21 | ||
| 22 | function noop () {} |
| … | … | @@ -0,0 +1,60 @@ |
| 1 | const TICKS = 16 | |
| 2 | ||
| 3 | module.exports = class HotswapQueue { | |
| 4 | constructor () { | |
| 5 | this.priorities = [[], [], []] | |
| 6 | } | |
| 7 | ||
| 8 | * pick (peer) { | |
| 9 | for (let i = 0; i < this.priorities.length; i++) { | |
| 10 | // try first one more than second one etc etc | |
| 11 | let ticks = (this.priorities.length - i) * TICKS | |
| 12 | const queue = this.priorities[i] | |
| 13 | ||
| 14 | for (let j = 0; j < queue.length; j++) { | |
| 15 | const r = j + Math.floor(Math.random() * queue.length - j) | |
| 16 | const a = queue[j] | |
| 17 | const b = queue[r] | |
| 18 | ||
| 19 | if (r !== j) { | |
| 20 | queue[(b.hotswap.index = j)] = b | |
| 21 | queue[(a.hotswap.index = r)] = a | |
| 22 | } | |
| 23 | ||
| 24 | if (hasInflight(b, peer)) continue | |
| 25 | ||
| 26 | yield b | |
| 27 | ||
| 28 | if (--ticks <= 0) break | |
| 29 | } | |
| 30 | } | |
| 31 | } | |
| 32 | ||
| 33 | add (block) { | |
| 34 | if (block.hotswap !== null) this.remove(block) | |
| 35 | if (block.inflight.length === 0 || block.inflight.length >= 3) return | |
| 36 | ||
| 37 | // TODO: also use other stuff to determine queue prio | |
| 38 | const queue = this.priorities[block.inflight.length - 1] | |
| 39 | ||
| 40 | const index = queue.push(block) - 1 | |
| 41 | block.hotswap = { ref: this, queue, index } | |
| 42 | } | |
| 43 | ||
| 44 | remove (block) { | |
| 45 | const hotswap = block.hotswap | |
| 46 | if (hotswap === null) return | |
| 47 | ||
| 48 | block.hotswap = null | |
| 49 | const head = hotswap.queue.pop() | |
| 50 | if (head === block) return | |
| 51 | hotswap.queue[(head.hotswap.index = hotswap.index)] = head | |
| 52 | } | |
| 53 | } | |
| 54 | ||
| 55 | function hasInflight (block, peer) { | |
| 56 | for (let j = 0; j < block.inflight.length; j++) { | |
| 57 | if (block.inflight[j].peer === peer) return true | |
| 58 | } | |
| 59 | return false | |
| 60 | } |
| … | … | @@ -0,0 +1,53 @@ |
| 1 | module.exports = class Info { | |
| 2 | constructor (opts = {}) { | |
| 3 | this.key = opts.key | |
| 4 | this.discoveryKey = opts.discoveryKey | |
| 5 | this.length = opts.length || 0 | |
| 6 | this.contiguousLength = opts.contiguousLength || 0 | |
| 7 | this.byteLength = opts.byteLength || 0 | |
| 8 | this.fork = opts.fork || 0 | |
| 9 | this.padding = opts.padding || 0 | |
| 10 | this.storage = opts.storage || null | |
| 11 | } | |
| 12 | ||
| 13 | static async from (session, opts = {}) { | |
| 14 | return new Info({ | |
| 15 | key: session.key, | |
| 16 | discoveryKey: session.discoveryKey, | |
| 17 | length: session.length, | |
| 18 | contiguousLength: session.contiguousLength, | |
| 19 | byteLength: session.byteLength, | |
| 20 | fork: session.fork, | |
| 21 | padding: session.padding, | |
| 22 | storage: opts.storage ? await this.storage(session) : null | |
| 23 | }) | |
| 24 | } | |
| 25 | ||
| 26 | static async storage (session) { | |
| 27 | const { oplog, tree, blocks, bitfield } = session.core | |
| 28 | try { | |
| 29 | return { | |
| 30 | oplog: await Info.bytesUsed(oplog.storage), | |
| 31 | tree: await Info.bytesUsed(tree.storage), | |
| 32 | blocks: await Info.bytesUsed(blocks.storage), | |
| 33 | bitfield: await Info.bytesUsed(bitfield.storage) | |
| 34 | } | |
| 35 | } catch { | |
| 36 | return null | |
| 37 | } | |
| 38 | } | |
| 39 | ||
| 40 | static bytesUsed (file) { | |
| 41 | return new Promise((resolve, reject) => { | |
| 42 | file.stat((err, st) => { | |
| 43 | if (err) { | |
| 44 | resolve(0) // prob just file not found (TODO, improve) | |
| 45 | } else if (typeof st.blocks !== 'number') { | |
| 46 | reject(new Error('cannot determine bytes used')) | |
| 47 | } else { | |
| 48 | resolve(st.blocks * 512) | |
| 49 | } | |
| 50 | }) | |
| 51 | }) | |
| 52 | } | |
| 53 | } |
| … | … | @@ -0,0 +1,1402 @@ |
| 1 | const flat = require('flat-tree') | |
| 2 | const crypto = require('hypercore-crypto') | |
| 3 | const c = require('compact-encoding') | |
| 4 | const Xache = require('xache') | |
| 5 | const b4a = require('b4a') | |
| 6 | const unslab = require('unslab') | |
| 7 | const caps = require('./caps') | |
| 8 | const { INVALID_PROOF, INVALID_CHECKSUM, INVALID_OPERATION, BAD_ARGUMENT, ASSERTION } = require('hypercore-errors') | |
| 9 | ||
| 10 | const BLANK_HASH = b4a.alloc(32) | |
| 11 | const OLD_TREE = b4a.from([5, 2, 87, 2, 0, 0, 40, 7, 66, 76, 65, 75, 69, 50, 98]) | |
| 12 | const TREE_CACHE = 128 // speeds up linear scans by A LOT | |
| 13 | ||
| 14 | class NodeQueue { | |
| 15 | constructor (nodes, extra = null) { | |
| 16 | this.i = 0 | |
| 17 | this.nodes = nodes | |
| 18 | this.extra = extra | |
| 19 | this.length = nodes.length + (this.extra === null ? 0 : 1) | |
| 20 | } | |
| 21 | ||
| 22 | shift (index) { | |
| 23 | if (this.extra !== null && this.extra.index === index) { | |
| 24 | const node = this.extra | |
| 25 | this.extra = null | |
| 26 | this.length-- | |
| 27 | return node | |
| 28 | } | |
| 29 | ||
| 30 | if (this.i >= this.nodes.length) { | |
| 31 | throw INVALID_OPERATION('Expected node ' + index + ', got (nil)') | |
| 32 | } | |
| 33 | ||
| 34 | const node = this.nodes[this.i++] | |
| 35 | if (node.index !== index) { | |
| 36 | throw INVALID_OPERATION('Expected node ' + index + ', got node ' + node.index) | |
| 37 | } | |
| 38 | ||
| 39 | this.length-- | |
| 40 | return node | |
| 41 | } | |
| 42 | } | |
| 43 | ||
| 44 | class MerkleTreeBatch { | |
| 45 | constructor (tree) { | |
| 46 | this.fork = tree.fork | |
| 47 | this.roots = [...tree.roots] | |
| 48 | this.length = tree.length | |
| 49 | this.ancestors = tree.length | |
| 50 | this.byteLength = tree.byteLength | |
| 51 | this.signature = null | |
| 52 | this.hashCached = null | |
| 53 | ||
| 54 | this.treeLength = tree.length | |
| 55 | this.treeFork = tree.fork | |
| 56 | this.tree = tree | |
| 57 | this.nodes = [] | |
| 58 | this.upgraded = false | |
| 59 | } | |
| 60 | ||
| 61 | checkout (length, additionalRoots) { | |
| 62 | const roots = [] | |
| 63 | let r = 0 | |
| 64 | ||
| 65 | const head = 2 * length - 2 | |
| 66 | const gaps = new Set() | |
| 67 | const all = new Map() | |
| 68 | ||
| 69 | // additional roots is so the original roots can be passed (we mutate the array in appendRoot) | |
| 70 | if (additionalRoots) { | |
| 71 | for (const node of additionalRoots) all.set(node.index, node) | |
| 72 | } | |
| 73 | ||
| 74 | for (const node of this.nodes) all.set(node.index, node) | |
| 75 | ||
| 76 | for (const index of flat.fullRoots(head + 2)) { | |
| 77 | const left = flat.leftSpan(index) | |
| 78 | if (left !== 0) gaps.add(left - 1) | |
| 79 | ||
| 80 | if (r < this.roots.length && this.roots[r].index === index) { | |
| 81 | roots.push(this.roots[r++]) | |
| 82 | continue | |
| 83 | } | |
| 84 | const node = all.get(index) | |
| 85 | if (!node) throw new BAD_ARGUMENT('root missing for given length') | |
| 86 | roots.push(node) | |
| 87 | } | |
| 88 | ||
| 89 | this.roots = roots | |
| 90 | this.length = length | |
| 91 | this.byteLength = totalSize(roots) | |
| 92 | this.hashCached = null | |
| 93 | this.signature = null | |
| 94 | ||
| 95 | for (let i = 0; i < this.nodes.length; i++) { | |
| 96 | const index = this.nodes[i].index | |
| 97 | if (index <= head && !gaps.has(index)) continue | |
| 98 | const last = this.nodes.pop() | |
| 99 | if (i < this.nodes.length) this.nodes[i--] = last | |
| 100 | } | |
| 101 | } | |
| 102 | ||
| 103 | prune (length) { | |
| 104 | if (length === 0) return | |
| 105 | ||
| 106 | const head = 2 * length - 2 | |
| 107 | const gaps = new Set() | |
| 108 | ||
| 109 | // TODO: make a function for this in flat-tree | |
| 110 | for (const index of flat.fullRoots(head + 2)) { | |
| 111 | const left = flat.leftSpan(index) | |
| 112 | if (left !== 0) gaps.add(left - 1) | |
| 113 | } | |
| 114 | ||
| 115 | for (let i = 0; i < this.nodes.length; i++) { | |
| 116 | const index = this.nodes[i].index | |
| 117 | if (index > head || gaps.has(index)) continue | |
| 118 | const last = this.nodes.pop() | |
| 119 | if (i < this.nodes.length) this.nodes[i--] = last | |
| 120 | } | |
| 121 | } | |
| 122 | ||
| 123 | clone () { | |
| 124 | const b = new MerkleTreeBatch(this.tree) | |
| 125 | ||
| 126 | b.fork = this.fork | |
| 127 | b.roots = [...this.roots] | |
| 128 | b.length = this.length | |
| 129 | b.byteLength = this.byteLength | |
| 130 | b.signature = this.signature | |
| 131 | b.treeLength = this.treeLength | |
| 132 | b.treeFork = this.treeFork | |
| 133 | b.tree = this.tree | |
| 134 | b.nodes = [...this.nodes] | |
| 135 | b.upgraded = this.upgraded | |
| 136 | ||
| 137 | return b | |
| 138 | } | |
| 139 | ||
| 140 | hash () { | |
| 141 | if (this.hashCached === null) this.hashCached = unslab(this.tree.crypto.tree(this.roots)) | |
| 142 | return this.hashCached | |
| 143 | } | |
| 144 | ||
| 145 | signable (manifestHash) { | |
| 146 | return caps.treeSignable(manifestHash, this.hash(), this.length, this.fork) | |
| 147 | } | |
| 148 | ||
| 149 | signableCompat (noHeader) { | |
| 150 | return caps.treeSignableCompat(this.hash(), this.length, this.fork, noHeader) | |
| 151 | } | |
| 152 | ||
| 153 | get (index, error) { | |
| 154 | if (index >= this.length * 2) { | |
| 155 | return null | |
| 156 | } | |
| 157 | ||
| 158 | for (const n of this.nodes) { | |
| 159 | if (n.index === index) return n | |
| 160 | } | |
| 161 | ||
| 162 | return this.tree.get(index, error) | |
| 163 | } | |
| 164 | ||
| 165 | proof ({ block, hash, seek, upgrade }) { | |
| 166 | return generateProof(this, block, hash, seek, upgrade) | |
| 167 | } | |
| 168 | ||
| 169 | verifyUpgrade (proof) { | |
| 170 | const unverified = verifyTree(proof, this.tree.crypto, this.nodes) | |
| 171 | ||
| 172 | if (!proof.upgrade) throw INVALID_OPERATION('Expected upgrade proof') | |
| 173 | ||
| 174 | return verifyUpgrade(proof, unverified, this) | |
| 175 | } | |
| 176 | ||
| 177 | append (buf) { | |
| 178 | const head = this.length * 2 | |
| 179 | const ite = flat.iterator(head) | |
| 180 | const node = blockNode(this.tree.crypto, head, buf) | |
| 181 | ||
| 182 | this.appendRoot(node, ite) | |
| 183 | } | |
| 184 | ||
| 185 | appendRoot (node, ite) { | |
| 186 | node = unslabNode(node) | |
| 187 | this.hashCached = null | |
| 188 | this.upgraded = true | |
| 189 | this.length += ite.factor / 2 | |
| 190 | this.byteLength += node.size | |
| 191 | this.roots.push(node) | |
| 192 | this.nodes.push(node) | |
| 193 | ||
| 194 | while (this.roots.length > 1) { | |
| 195 | const a = this.roots[this.roots.length - 1] | |
| 196 | const b = this.roots[this.roots.length - 2] | |
| 197 | ||
| 198 | // TODO: just have a peek sibling instead? (pretty sure it's always the left sib as well) | |
| 199 | if (ite.sibling() !== b.index) { | |
| 200 | ite.sibling() // unset so it always points to last root | |
| 201 | break | |
| 202 | } | |
| 203 | ||
| 204 | const node = unslabNode(parentNode(this.tree.crypto, ite.parent(), a, b)) | |
| 205 | this.nodes.push(node) | |
| 206 | this.roots.pop() | |
| 207 | this.roots.pop() | |
| 208 | this.roots.push(node) | |
| 209 | } | |
| 210 | } | |
| 211 | ||
| 212 | commitable () { | |
| 213 | return this.treeFork === this.tree.fork && ( | |
| 214 | this.upgraded | |
| 215 | ? this.treeLength === this.tree.length | |
| 216 | : this.treeLength <= this.tree.length | |
| 217 | ) | |
| 218 | } | |
| 219 | ||
| 220 | commit () { | |
| 221 | if (!this.commitable()) throw INVALID_OPERATION('Tree was modified during batch, refusing to commit') | |
| 222 | ||
| 223 | if (this.upgraded) this._commitUpgrade() | |
| 224 | ||
| 225 | for (let i = 0; i < this.nodes.length; i++) { | |
| 226 | const node = this.nodes[i] | |
| 227 | this.tree.unflushed.set(node.index, node) | |
| 228 | } | |
| 229 | } | |
| 230 | ||
| 231 | _commitUpgrade () { | |
| 232 | // TODO: If easy to detect, we should refuse an trunc+append here without a fork id | |
| 233 | // change. Will only happen on user error so mostly to prevent that. | |
| 234 | ||
| 235 | if (this.ancestors < this.treeLength) { | |
| 236 | if (this.ancestors > 0) { | |
| 237 | const head = 2 * this.ancestors | |
| 238 | const ite = flat.iterator(head - 2) | |
| 239 | ||
| 240 | while (true) { | |
| 241 | if (ite.contains(head) && ite.index < head) { | |
| 242 | this.tree.unflushed.set(ite.index, blankNode(ite.index)) | |
| 243 | } | |
| 244 | if (ite.offset === 0) break | |
| 245 | ite.parent() | |
| 246 | } | |
| 247 | } | |
| 248 | ||
| 249 | this.tree.truncateTo = this.tree.truncated | |
| 250 | ? Math.min(this.tree.truncateTo, this.ancestors) | |
| 251 | : this.ancestors | |
| 252 | ||
| 253 | this.tree.truncated = true | |
| 254 | this.tree.cache = new Xache({ maxSize: this.tree.cache.maxSize }) | |
| 255 | truncateMap(this.tree.unflushed, this.ancestors) | |
| 256 | if (this.tree.flushing !== null) truncateMap(this.tree.flushing, this.ancestors) | |
| 257 | } | |
| 258 | ||
| 259 | this.tree.roots = this.roots | |
| 260 | this.tree.length = this.length | |
| 261 | this.tree.byteLength = this.byteLength | |
| 262 | this.tree.fork = this.fork | |
| 263 | this.tree.signature = this.signature | |
| 264 | } | |
| 265 | ||
| 266 | seek (bytes, padding) { | |
| 267 | return new ByteSeeker(this, bytes, padding) | |
| 268 | } | |
| 269 | ||
| 270 | byteRange (index) { | |
| 271 | return getByteRange(this, index) | |
| 272 | } | |
| 273 | ||
| 274 | byteOffset (index) { | |
| 275 | if (index === 2 * this.tree.length) return this.tree.byteLength | |
| 276 | return getByteOffset(this, index) | |
| 277 | } | |
| 278 | } | |
| 279 | ||
| 280 | class ReorgBatch extends MerkleTreeBatch { | |
| 281 | constructor (tree) { | |
| 282 | super(tree) | |
| 283 | this.roots = [] | |
| 284 | this.length = 0 | |
| 285 | this.byteLength = 0 | |
| 286 | this.diff = null | |
| 287 | this.ancestors = 0 | |
| 288 | // We set upgraded because reorgs are signed so hit will | |
| 289 | // hit the same code paths (like the treeLength check in commit) | |
| 290 | this.upgraded = true | |
| 291 | this.want = { | |
| 292 | nodes: 0, | |
| 293 | start: 0, | |
| 294 | end: 0 | |
| 295 | } | |
| 296 | } | |
| 297 | ||
| 298 | get finished () { | |
| 299 | return this.want === null | |
| 300 | } | |
| 301 | ||
| 302 | update (proof) { | |
| 303 | if (this.want === null) return true | |
| 304 | ||
| 305 | const nodes = [] | |
| 306 | const root = verifyTree(proof, this.tree.crypto, nodes) | |
| 307 | ||
| 308 | if (root === null || !b4a.equals(root.hash, this.diff.hash)) return false | |
| 309 | ||
| 310 | this.nodes.push(...nodes) | |
| 311 | return this._update(nodes) | |
| 312 | } | |
| 313 | ||
| 314 | async _update (nodes) { | |
| 315 | const n = new Map() | |
| 316 | for (const node of nodes) n.set(node.index, node) | |
| 317 | ||
| 318 | let diff = null | |
| 319 | const ite = flat.iterator(this.diff.index) | |
| 320 | const startingDiff = this.diff | |
| 321 | ||
| 322 | while ((ite.index & 1) !== 0) { | |
| 323 | const left = n.get(ite.leftChild()) | |
| 324 | if (!left) break | |
| 325 | ||
| 326 | const existing = await this.tree.get(left.index, false) | |
| 327 | if (!existing || !b4a.equals(existing.hash, left.hash)) { | |
| 328 | diff = left | |
| 329 | } else { | |
| 330 | diff = n.get(ite.sibling()) | |
| 331 | } | |
| 332 | } | |
| 333 | ||
| 334 | if ((this.diff.index & 1) === 0) return true | |
| 335 | if (diff === null) return false | |
| 336 | if (startingDiff !== this.diff) return false | |
| 337 | ||
| 338 | return this._updateDiffRoot(diff) | |
| 339 | } | |
| 340 | ||
| 341 | _updateDiffRoot (diff) { | |
| 342 | if (this.want === null) return true | |
| 343 | ||
| 344 | const spans = flat.spans(diff.index) | |
| 345 | const start = spans[0] / 2 | |
| 346 | const end = Math.min(this.treeLength, spans[1] / 2 + 1) | |
| 347 | const len = end - start | |
| 348 | ||
| 349 | this.ancestors = start | |
| 350 | this.diff = diff | |
| 351 | ||
| 352 | if ((diff.index & 1) === 0 || this.want.start >= this.treeLength || len <= 0) { | |
| 353 | this.want = null | |
| 354 | return true | |
| 355 | } | |
| 356 | ||
| 357 | this.want.start = start | |
| 358 | this.want.end = end | |
| 359 | this.want.nodes = log2(spans[1] - spans[0] + 2) - 1 | |
| 360 | ||
| 361 | return false | |
| 362 | } | |
| 363 | } | |
| 364 | ||
| 365 | class ByteSeeker { | |
| 366 | constructor (tree, bytes, padding = 0) { | |
| 367 | this.tree = tree | |
| 368 | this.bytes = bytes | |
| 369 | this.padding = padding | |
| 370 | ||
| 371 | const size = tree.byteLength - (tree.length * padding) | |
| 372 | ||
| 373 | this.start = bytes >= size ? tree.length : 0 | |
| 374 | this.end = bytes < size ? tree.length : 0 | |
| 375 | } | |
| 376 | ||
| 377 | async _seek (bytes) { | |
| 378 | if (!bytes) return [0, 0] | |
| 379 | ||
| 380 | for (const node of this.tree.roots) { // all async ticks happen once we find the root so safe | |
| 381 | const size = getUnpaddedSize(node, this.padding, null) | |
| 382 | ||
| 383 | if (bytes === size) return [flat.rightSpan(node.index) + 2, 0] | |
| 384 | if (bytes > size) { | |
| 385 | bytes -= size | |
| 386 | continue | |
| 387 | } | |
| 388 | ||
| 389 | const ite = flat.iterator(node.index) | |
| 390 | ||
| 391 | while ((ite.index & 1) !== 0) { | |
| 392 | const l = await this.tree.get(ite.leftChild(), false) | |
| 393 | ||
| 394 | if (l) { | |
| 395 | const size = getUnpaddedSize(l, this.padding, ite) | |
| 396 | ||
| 397 | if (size === bytes) return [ite.rightSpan() + 2, 0] | |
| 398 | if (size > bytes) continue | |
| 399 | bytes -= size | |
| 400 | ite.sibling() | |
| 401 | } else { | |
| 402 | ite.parent() | |
| 403 | return [ite.index, bytes] | |
| 404 | } | |
| 405 | } | |
| 406 | ||
| 407 | return [ite.index, bytes] | |
| 408 | } | |
| 409 | ||
| 410 | return null | |
| 411 | } | |
| 412 | ||
| 413 | async update () { // TODO: combine _seek and this, much simpler | |
| 414 | const res = await this._seek(this.bytes) | |
| 415 | if (!res) return null | |
| 416 | if ((res[0] & 1) === 0) return [res[0] / 2, res[1]] | |
| 417 | ||
| 418 | const span = flat.spans(res[0]) | |
| 419 | this.start = span[0] / 2 | |
| 420 | this.end = span[1] / 2 + 1 | |
| 421 | ||
| 422 | return null | |
| 423 | } | |
| 424 | } | |
| 425 | ||
| 426 | module.exports = class MerkleTree { | |
| 427 | constructor (storage, roots, fork, signature, prologue) { | |
| 428 | this.crypto = crypto | |
| 429 | this.fork = fork | |
| 430 | this.roots = roots | |
| 431 | this.length = roots.length ? totalSpan(roots) / 2 : 0 | |
| 432 | this.byteLength = totalSize(roots) | |
| 433 | this.signature = signature | |
| 434 | this.prologue = prologue | |
| 435 | ||
| 436 | this.storage = storage | |
| 437 | this.unflushed = new Map() | |
| 438 | this.cache = new Xache({ maxSize: TREE_CACHE }) | |
| 439 | this.flushing = null | |
| 440 | this.truncated = false | |
| 441 | this.truncateTo = 0 | |
| 442 | } | |
| 443 | ||
| 444 | addNode (node) { | |
| 445 | if (node.size === 0 && b4a.equals(node.hash, BLANK_HASH)) node = blankNode(node.index) | |
| 446 | this.unflushed.set(node.index, node) | |
| 447 | } | |
| 448 | ||
| 449 | batch () { | |
| 450 | return new MerkleTreeBatch(this) | |
| 451 | } | |
| 452 | ||
| 453 | async restoreBatch (length) { | |
| 454 | const batch = new MerkleTreeBatch(this) | |
| 455 | if (length === this.length) return batch | |
| 456 | ||
| 457 | const roots = unslabNodes(await this.getRoots(length)) | |
| 458 | ||
| 459 | batch.roots = roots | |
| 460 | batch.length = length | |
| 461 | batch.byteLength = 0 | |
| 462 | batch.ancestors = length | |
| 463 | ||
| 464 | for (const node of roots) batch.byteLength += node.size | |
| 465 | ||
| 466 | return batch | |
| 467 | } | |
| 468 | ||
| 469 | seek (bytes, padding) { | |
| 470 | return new ByteSeeker(this, bytes, padding) | |
| 471 | } | |
| 472 | ||
| 473 | hash () { | |
| 474 | return unslab(this.crypto.tree(this.roots)) | |
| 475 | } | |
| 476 | ||
| 477 | signable (namespace) { | |
| 478 | return caps.treeSignable(namespace, this.hash(), this.length, this.fork) | |
| 479 | } | |
| 480 | ||
| 481 | getRoots (length) { | |
| 482 | const indexes = flat.fullRoots(2 * length) | |
| 483 | const roots = new Array(indexes.length) | |
| 484 | ||
| 485 | for (let i = 0; i < indexes.length; i++) { | |
| 486 | roots[i] = this.get(indexes[i], true) | |
| 487 | } | |
| 488 | ||
| 489 | return Promise.all(roots) | |
| 490 | } | |
| 491 | ||
| 492 | setPrologue ({ hash, length }) { | |
| 493 | this.prologue = { hash, length } | |
| 494 | } | |
| 495 | ||
| 496 | addNodes (nodes) { | |
| 497 | for (let i = 0; i < nodes.length; i++) { | |
| 498 | const node = nodes[i] | |
| 499 | this.unflushed.set(node.index, node) | |
| 500 | } | |
| 501 | } | |
| 502 | ||
| 503 | getNeededNodes (length, start, end) { | |
| 504 | const nodes = new Map() | |
| 505 | const head = length * 2 | |
| 506 | ||
| 507 | for (let i = start; i < end; i++) { | |
| 508 | const ite = flat.iterator(i * 2) | |
| 509 | ||
| 510 | while (true) { | |
| 511 | if (nodes.has(ite.index)) break | |
| 512 | nodes.set(ite.index, this.get(ite.index, true)) | |
| 513 | ||
| 514 | const sibling = ite.sibling() | |
| 515 | ||
| 516 | ite.parent() | |
| 517 | if (ite.contains(head)) break | |
| 518 | ||
| 519 | if (nodes.has(sibling)) break | |
| 520 | nodes.set(sibling, this.get(sibling, true)) | |
| 521 | } | |
| 522 | } | |
| 523 | ||
| 524 | return Promise.all([...nodes.values()]) | |
| 525 | } | |
| 526 | ||
| 527 | async upgradeable (length) { | |
| 528 | const indexes = flat.fullRoots(2 * length) | |
| 529 | const roots = new Array(indexes.length) | |
| 530 | ||
| 531 | for (let i = 0; i < indexes.length; i++) { | |
| 532 | roots[i] = this.get(indexes[i], false) | |
| 533 | } | |
| 534 | ||
| 535 | for (const node of await Promise.all(roots)) { | |
| 536 | if (node === null) return false | |
| 537 | } | |
| 538 | ||
| 539 | return true | |
| 540 | } | |
| 541 | ||
| 542 | blankNode (index) { | |
| 543 | return blankNode(index) | |
| 544 | } | |
| 545 | ||
| 546 | get (index, error = true) { | |
| 547 | const c = this.cache.get(index) | |
| 548 | if (c) return c | |
| 549 | ||
| 550 | let node = this.unflushed.get(index) | |
| 551 | ||
| 552 | if (this.flushing !== null && node === undefined) { | |
| 553 | node = this.flushing.get(index) | |
| 554 | } | |
| 555 | ||
| 556 | // TODO: test this | |
| 557 | if (this.truncated && node !== undefined && node.index >= 2 * this.truncateTo) { | |
| 558 | node = blankNode(index) | |
| 559 | } | |
| 560 | ||
| 561 | if (node !== undefined) { | |
| 562 | if (node.hash === BLANK_HASH) { | |
| 563 | if (error) throw INVALID_OPERATION('Could not load node: ' + index) | |
| 564 | return Promise.resolve(null) | |
| 565 | } | |
| 566 | return Promise.resolve(node) | |
| 567 | } | |
| 568 | ||
| 569 | return getStoredNode(this.storage, index, this.cache, error) | |
| 570 | } | |
| 571 | ||
| 572 | async flush () { | |
| 573 | this.flushing = this.unflushed | |
| 574 | this.unflushed = new Map() | |
| 575 | ||
| 576 | try { | |
| 577 | if (this.truncated) await this._flushTruncation() | |
| 578 | await this._flushNodes() | |
| 579 | } catch (err) { | |
| 580 | for (const node of this.flushing.values()) { | |
| 581 | if (!this.unflushed.has(node.index)) this.unflushed.set(node.index, node) | |
| 582 | } | |
| 583 | throw err | |
| 584 | } finally { | |
| 585 | this.flushing = null | |
| 586 | } | |
| 587 | } | |
| 588 | ||
| 589 | _flushTruncation () { | |
| 590 | return new Promise((resolve, reject) => { | |
| 591 | const t = this.truncateTo | |
| 592 | const offset = t === 0 ? 0 : (t - 1) * 80 + 40 | |
| 593 | ||
| 594 | this.storage.truncate(offset, (err) => { | |
| 595 | if (err) return reject(err) | |
| 596 | ||
| 597 | if (this.truncateTo === t) { | |
| 598 | this.truncateTo = 0 | |
| 599 | this.truncated = false | |
| 600 | } | |
| 601 | ||
| 602 | resolve() | |
| 603 | }) | |
| 604 | }) | |
| 605 | } | |
| 606 | ||
| 607 | _flushNodes () { | |
| 608 | // TODO: write neighbors together etc etc | |
| 609 | // TODO: bench loading a full disk page and copy to that instead | |
| 610 | return new Promise((resolve, reject) => { | |
| 611 | const slab = b4a.allocUnsafe(40 * this.flushing.size) | |
| 612 | ||
| 613 | let error = null | |
| 614 | let missing = this.flushing.size + 1 | |
| 615 | let offset = 0 | |
| 616 | ||
| 617 | for (const node of this.flushing.values()) { | |
| 618 | const state = { | |
| 619 | start: 0, | |
| 620 | end: 40, | |
| 621 | buffer: slab.subarray(offset, offset += 40) | |
| 622 | } | |
| 623 | ||
| 624 | c.uint64.encode(state, node.size) | |
| 625 | c.raw.encode(state, node.hash) | |
| 626 | ||
| 627 | this.storage.write(node.index * 40, state.buffer, done) | |
| 628 | } | |
| 629 | ||
| 630 | done(null) | |
| 631 | ||
| 632 | function done (err) { | |
| 633 | if (err) error = err | |
| 634 | if (--missing > 0) return | |
| 635 | if (error) reject(error) | |
| 636 | else resolve() | |
| 637 | } | |
| 638 | }) | |
| 639 | } | |
| 640 | ||
| 641 | clear () { | |
| 642 | this.cache = new Xache({ maxSize: this.cache.maxSize }) | |
| 643 | this.truncated = true | |
| 644 | this.truncateTo = 0 | |
| 645 | this.roots = [] | |
| 646 | this.length = 0 | |
| 647 | this.byteLength = 0 | |
| 648 | this.fork = 0 | |
| 649 | this.signature = null | |
| 650 | if (this.flushing !== null) this.flushing.clear() | |
| 651 | this.unflushed.clear() | |
| 652 | return this.flush() | |
| 653 | } | |
| 654 | ||
| 655 | close () { | |
| 656 | return new Promise((resolve, reject) => { | |
| 657 | this.storage.close(err => { | |
| 658 | if (err) reject(err) | |
| 659 | else resolve() | |
| 660 | }) | |
| 661 | }) | |
| 662 | } | |
| 663 | ||
| 664 | async truncate (length, fork = this.fork) { | |
| 665 | const head = length * 2 | |
| 666 | const batch = new MerkleTreeBatch(this) | |
| 667 | const fullRoots = flat.fullRoots(head) | |
| 668 | ||
| 669 | for (let i = 0; i < fullRoots.length; i++) { | |
| 670 | const root = fullRoots[i] | |
| 671 | if (i < batch.roots.length && batch.roots[i].index === root) continue | |
| 672 | ||
| 673 | while (batch.roots.length > i) batch.roots.pop() | |
| 674 | batch.roots.push(unslabNode(await this.get(root))) | |
| 675 | } | |
| 676 | ||
| 677 | while (batch.roots.length > fullRoots.length) { | |
| 678 | batch.roots.pop() | |
| 679 | } | |
| 680 | ||
| 681 | batch.fork = fork | |
| 682 | batch.length = length | |
| 683 | batch.ancestors = length | |
| 684 | batch.byteLength = totalSize(batch.roots) | |
| 685 | batch.upgraded = true | |
| 686 | ||
| 687 | return batch | |
| 688 | } | |
| 689 | ||
| 690 | async reorg (proof) { | |
| 691 | const batch = new ReorgBatch(this) | |
| 692 | ||
| 693 | let unverified = null | |
| 694 | ||
| 695 | if (proof.block || proof.hash || proof.seek) { | |
| 696 | unverified = verifyTree(proof, this.crypto, batch.nodes) | |
| 697 | } | |
| 698 | ||
| 699 | if (!verifyUpgrade(proof, unverified, batch)) { | |
| 700 | throw INVALID_PROOF('Fork proof not verifiable') | |
| 701 | } | |
| 702 | ||
| 703 | for (const root of batch.roots) { | |
| 704 | const existing = await this.get(root.index, false) | |
| 705 | if (existing && b4a.equals(existing.hash, root.hash)) continue | |
| 706 | batch._updateDiffRoot(root) | |
| 707 | break | |
| 708 | } | |
| 709 | ||
| 710 | if (batch.diff !== null) { | |
| 711 | await batch._update(batch.nodes) | |
| 712 | } else { | |
| 713 | batch.want = null | |
| 714 | batch.ancestors = batch.length | |
| 715 | } | |
| 716 | ||
| 717 | return batch | |
| 718 | } | |
| 719 | ||
| 720 | verifyFullyRemote (proof) { | |
| 721 | // TODO: impl this less hackishly | |
| 722 | const batch = new MerkleTreeBatch(this) | |
| 723 | ||
| 724 | batch.fork = proof.fork | |
| 725 | batch.roots = [] | |
| 726 | batch.length = 0 | |
| 727 | batch.ancestors = 0 | |
| 728 | batch.byteLength = 0 | |
| 729 | ||
| 730 | let unverified = verifyTree(proof, this.crypto, batch.nodes) | |
| 731 | ||
| 732 | if (proof.upgrade) { | |
| 733 | if (verifyUpgrade(proof, unverified, batch)) { | |
| 734 | unverified = null | |
| 735 | } | |
| 736 | } | |
| 737 | ||
| 738 | return batch | |
| 739 | } | |
| 740 | ||
| 741 | async verify (proof) { | |
| 742 | const batch = new MerkleTreeBatch(this) | |
| 743 | ||
| 744 | let unverified = verifyTree(proof, this.crypto, batch.nodes) | |
| 745 | ||
| 746 | if (proof.upgrade) { | |
| 747 | if (verifyUpgrade(proof, unverified, batch)) { | |
| 748 | unverified = null | |
| 749 | } | |
| 750 | } | |
| 751 | ||
| 752 | if (unverified) { | |
| 753 | const verified = await this.get(unverified.index) | |
| 754 | if (!b4a.equals(verified.hash, unverified.hash)) { | |
| 755 | throw INVALID_CHECKSUM('Invalid checksum at node ' + unverified.index) | |
| 756 | } | |
| 757 | } | |
| 758 | ||
| 759 | return batch | |
| 760 | } | |
| 761 | ||
| 762 | proof ({ block, hash, seek, upgrade }) { | |
| 763 | return generateProof(this, block, hash, seek, upgrade) | |
| 764 | } | |
| 765 | ||
| 766 | // Successor to .nodes() | |
| 767 | async missingNodes (index) { | |
| 768 | const head = 2 * this.length | |
| 769 | const ite = flat.iterator(index) | |
| 770 | ||
| 771 | // See iterator.rightSpan() | |
| 772 | const iteRightSpan = ite.index + ite.factor / 2 - 1 | |
| 773 | // If the index is not in the current tree, we do not know how many missing nodes there are... | |
| 774 | if (iteRightSpan >= head) return 0 | |
| 775 | ||
| 776 | let cnt = 0 | |
| 777 | while (!ite.contains(head) && (await this.get(ite.index, false)) === null) { | |
| 778 | cnt++ | |
| 779 | ite.parent() | |
| 780 | } | |
| 781 | ||
| 782 | return cnt | |
| 783 | } | |
| 784 | ||
| 785 | // Deprecated | |
| 786 | async nodes (index) { | |
| 787 | const head = 2 * this.length | |
| 788 | const ite = flat.iterator(index) | |
| 789 | ||
| 790 | let cnt = 0 | |
| 791 | while (!ite.contains(head) && (await this.get(ite.index, false)) === null) { | |
| 792 | cnt++ | |
| 793 | ite.parent() | |
| 794 | } | |
| 795 | ||
| 796 | return cnt | |
| 797 | } | |
| 798 | ||
| 799 | byteRange (index) { | |
| 800 | return getByteRange(this, index) | |
| 801 | } | |
| 802 | ||
| 803 | byteOffset (index) { | |
| 804 | return getByteOffset(this, index) | |
| 805 | } | |
| 806 | ||
| 807 | static async open (storage, opts = {}) { | |
| 808 | await new Promise((resolve, reject) => { | |
| 809 | storage.read(0, OLD_TREE.length, (err, buf) => { | |
| 810 | if (err) return resolve() | |
| 811 | if (b4a.equals(buf, OLD_TREE)) return reject(new Error('Storage contains an incompatible merkle tree')) | |
| 812 | resolve() | |
| 813 | }) | |
| 814 | }) | |
| 815 | ||
| 816 | const length = typeof opts.length === 'number' | |
| 817 | ? opts.length | |
| 818 | : await autoLength(storage) | |
| 819 | ||
| 820 | const roots = [] | |
| 821 | for (const index of flat.fullRoots(2 * length)) { | |
| 822 | roots.push(unslabNode(await getStoredNode(storage, index, null, true))) | |
| 823 | } | |
| 824 | ||
| 825 | return new MerkleTree(storage, roots, opts.fork || 0, opts.signature || null, opts.prologue || null) | |
| 826 | } | |
| 827 | } | |
| 828 | ||
| 829 | async function getByteRange (tree, index) { | |
| 830 | const head = 2 * tree.length | |
| 831 | if (((index & 1) === 0 ? index : flat.rightSpan(index)) >= head) { | |
| 832 | throw BAD_ARGUMENT('Index is out of bounds') | |
| 833 | } | |
| 834 | return [await tree.byteOffset(index), (await tree.get(index)).size] | |
| 835 | } | |
| 836 | ||
| 837 | async function getByteOffset (tree, index) { | |
| 838 | if (index === 2 * tree.length) return tree.byteLength | |
| 839 | if ((index & 1) === 1) index = flat.leftSpan(index) | |
| 840 | ||
| 841 | let head = 0 | |
| 842 | let offset = 0 | |
| 843 | ||
| 844 | for (const node of tree.roots) { // all async ticks happen once we find the root so safe | |
| 845 | head += 2 * ((node.index - head) + 1) | |
| 846 | ||
| 847 | if (index >= head) { | |
| 848 | offset += node.size | |
| 849 | continue | |
| 850 | } | |
| 851 | ||
| 852 | const ite = flat.iterator(node.index) | |
| 853 | ||
| 854 | while (ite.index !== index) { | |
| 855 | if (index < ite.index) { | |
| 856 | ite.leftChild() | |
| 857 | } else { | |
| 858 | offset += (await tree.get(ite.leftChild())).size | |
| 859 | ite.sibling() | |
| 860 | } | |
| 861 | } | |
| 862 | ||
| 863 | return offset | |
| 864 | } | |
| 865 | ||
| 866 | throw ASSERTION('Failed to find offset') | |
| 867 | } | |
| 868 | ||
| 869 | // All the methods needed for proof verification | |
| 870 | ||
| 871 | function verifyTree ({ block, hash, seek }, crypto, nodes) { | |
| 872 | const untrustedNode = block | |
| 873 | ? { index: 2 * block.index, value: block.value, nodes: block.nodes } | |
| 874 | : hash | |
| 875 | ? { index: hash.index, value: null, nodes: hash.nodes } | |
| 876 | : null | |
| 877 | ||
| 878 | if (untrustedNode === null && (!seek || !seek.nodes.length)) return null | |
| 879 | ||
| 880 | let root = null | |
| 881 | ||
| 882 | if (seek && seek.nodes.length) { | |
| 883 | const ite = flat.iterator(seek.nodes[0].index) | |
| 884 | const q = new NodeQueue(seek.nodes) | |
| 885 | ||
| 886 | root = q.shift(ite.index) | |
| 887 | nodes.push(root) | |
| 888 | ||
| 889 | while (q.length > 0) { | |
| 890 | const node = q.shift(ite.sibling()) | |
| 891 | ||
| 892 | root = parentNode(crypto, ite.parent(), root, node) | |
| 893 | nodes.push(node) | |
| 894 | nodes.push(root) | |
| 895 | } | |
| 896 | } | |
| 897 | ||
| 898 | if (untrustedNode === null) return root | |
| 899 | ||
| 900 | const ite = flat.iterator(untrustedNode.index) | |
| 901 | const blockHash = untrustedNode.value && blockNode(crypto, ite.index, untrustedNode.value) | |
| 902 | ||
| 903 | const q = new NodeQueue(untrustedNode.nodes, root) | |
| 904 | ||
| 905 | root = blockHash || q.shift(ite.index) | |
| 906 | nodes.push(root) | |
| 907 | ||
| 908 | while (q.length > 0) { | |
| 909 | const node = q.shift(ite.sibling()) | |
| 910 | ||
| 911 | root = parentNode(crypto, ite.parent(), root, node) | |
| 912 | nodes.push(node) | |
| 913 | nodes.push(root) | |
| 914 | } | |
| 915 | ||
| 916 | return root | |
| 917 | } | |
| 918 | ||
| 919 | function verifyUpgrade ({ fork, upgrade }, blockRoot, batch) { | |
| 920 | const prologue = batch.tree.prologue | |
| 921 | ||
| 922 | if (prologue) { | |
| 923 | const { start, length } = upgrade | |
| 924 | if (start < prologue.length && (start !== 0 || length < prologue.length)) { | |
| 925 | throw INVALID_PROOF('Upgrade does not satisfy prologue') | |
| 926 | } | |
| 927 | } | |
| 928 | ||
| 929 | const q = new NodeQueue(upgrade.nodes, blockRoot) | |
| 930 | ||
| 931 | let grow = batch.roots.length > 0 | |
| 932 | let i = 0 | |
| 933 | ||
| 934 | const to = 2 * (upgrade.start + upgrade.length) | |
| 935 | const ite = flat.iterator(0) | |
| 936 | ||
| 937 | for (; ite.fullRoot(to); ite.nextTree()) { | |
| 938 | if (i < batch.roots.length && batch.roots[i].index === ite.index) { | |
| 939 | i++ | |
| 940 | continue | |
| 941 | } | |
| 942 | ||
| 943 | if (grow) { | |
| 944 | grow = false | |
| 945 | const root = ite.index | |
| 946 | if (i < batch.roots.length) { | |
| 947 | ite.seek(batch.roots[batch.roots.length - 1].index) | |
| 948 | while (ite.index !== root) { | |
| 949 | batch.appendRoot(q.shift(ite.sibling()), ite) | |
| 950 | } | |
| 951 | continue | |
| 952 | } | |
| 953 | } | |
| 954 | ||
| 955 | batch.appendRoot(q.shift(ite.index), ite) | |
| 956 | } | |
| 957 | ||
| 958 | if (prologue && batch.length === prologue.length) { | |
| 959 | if (!b4a.equals(prologue.hash, batch.hash())) { | |
| 960 | throw INVALID_PROOF('Invalid hash') | |
| 961 | } | |
| 962 | } | |
| 963 | ||
| 964 | const extra = upgrade.additionalNodes | |
| 965 | ||
| 966 | ite.seek(batch.roots[batch.roots.length - 1].index) | |
| 967 | i = 0 | |
| 968 | ||
| 969 | while (i < extra.length && extra[i].index === ite.sibling()) { | |
| 970 | batch.appendRoot(extra[i++], ite) | |
| 971 | } | |
| 972 | ||
| 973 | while (i < extra.length) { | |
| 974 | const node = extra[i++] | |
| 975 | ||
| 976 | while (node.index !== ite.index) { | |
| 977 | if (ite.factor === 2) throw INVALID_OPERATION('Unexpected node: ' + node.index) | |
| 978 | ite.leftChild() | |
| 979 | } | |
| 980 | ||
| 981 | batch.appendRoot(node, ite) | |
| 982 | ite.sibling() | |
| 983 | } | |
| 984 | ||
| 985 | batch.signature = unslab(upgrade.signature) | |
| 986 | batch.fork = fork | |
| 987 | ||
| 988 | return q.extra === null | |
| 989 | } | |
| 990 | ||
| 991 | async function seekFromHead (tree, head, bytes, padding) { | |
| 992 | const roots = flat.fullRoots(head) | |
| 993 | ||
| 994 | for (let i = 0; i < roots.length; i++) { | |
| 995 | const root = roots[i] | |
| 996 | const node = await tree.get(root) | |
| 997 | const size = getUnpaddedSize(node, padding, null) | |
| 998 | ||
| 999 | if (bytes === size) return root | |
| 1000 | if (bytes > size) { | |
| 1001 | bytes -= size | |
| 1002 | continue | |
| 1003 | } | |
| 1004 | ||
| 1005 | return seekTrustedTree(tree, root, bytes, padding) | |
| 1006 | } | |
| 1007 | ||
| 1008 | return head | |
| 1009 | } | |
| 1010 | ||
| 1011 | // trust that bytes are within the root tree and find the block at bytes | |
| 1012 | ||
| 1013 | async function seekTrustedTree (tree, root, bytes, padding) { | |
| 1014 | if (!bytes) return root | |
| 1015 | ||
| 1016 | const ite = flat.iterator(root) | |
| 1017 | ||
| 1018 | while ((ite.index & 1) !== 0) { | |
| 1019 | const l = await tree.get(ite.leftChild(), false) | |
| 1020 | if (l) { | |
| 1021 | const size = getUnpaddedSize(l, padding, ite) | |
| 1022 | if (size === bytes) return ite.index | |
| 1023 | if (size > bytes) continue | |
| 1024 | bytes -= size | |
| 1025 | ite.sibling() | |
| 1026 | } else { | |
| 1027 | ite.parent() | |
| 1028 | return ite.index | |
| 1029 | } | |
| 1030 | } | |
| 1031 | ||
| 1032 | return ite.index | |
| 1033 | } | |
| 1034 | ||
| 1035 | // try to find the block at bytes without trusting that is *is* within the root passed | |
| 1036 | ||
| 1037 | async function seekUntrustedTree (tree, root, bytes, padding) { | |
| 1038 | const offset = await tree.byteOffset(root) - (padding ? padding * flat.leftSpan(root) / 2 : 0) | |
| 1039 | ||
| 1040 | if (offset > bytes) throw INVALID_OPERATION('Invalid seek') | |
| 1041 | if (offset === bytes) return root | |
| 1042 | ||
| 1043 | bytes -= offset | |
| 1044 | ||
| 1045 | const node = await tree.get(root) | |
| 1046 | ||
| 1047 | if (getUnpaddedSize(node, padding, null) <= bytes) throw INVALID_OPERATION('Invalid seek') | |
| 1048 | ||
| 1049 | return seekTrustedTree(tree, root, bytes, padding) | |
| 1050 | } | |
| 1051 | ||
| 1052 | // Below is proof production, ie, construct proofs to verify a request | |
| 1053 | // Note, that all these methods are sync as we can statically infer which nodes | |
| 1054 | // are needed for the remote to verify given they arguments they passed us | |
| 1055 | ||
| 1056 | function seekProof (tree, seekRoot, root, p) { | |
| 1057 | const ite = flat.iterator(seekRoot) | |
| 1058 | ||
| 1059 | p.seek = [] | |
| 1060 | p.seek.push(tree.get(ite.index)) | |
| 1061 | ||
| 1062 | while (ite.index !== root) { | |
| 1063 | ite.sibling() | |
| 1064 | p.seek.push(tree.get(ite.index)) | |
| 1065 | ite.parent() | |
| 1066 | } | |
| 1067 | } | |
| 1068 | ||
| 1069 | function blockAndSeekProof (tree, node, seek, seekRoot, root, p) { | |
| 1070 | if (!node) return seekProof(tree, seekRoot, root, p) | |
| 1071 | ||
| 1072 | const ite = flat.iterator(node.index) | |
| 1073 | ||
| 1074 | p.node = [] | |
| 1075 | if (!node.value) p.node.push(tree.get(ite.index)) | |
| 1076 | ||
| 1077 | while (ite.index !== root) { | |
| 1078 | ite.sibling() | |
| 1079 | ||
| 1080 | if (seek && ite.contains(seekRoot) && ite.index !== seekRoot) { | |
| 1081 | seekProof(tree, seekRoot, ite.index, p) | |
| 1082 | } else { | |
| 1083 | p.node.push(tree.get(ite.index)) | |
| 1084 | } | |
| 1085 | ||
| 1086 | ite.parent() | |
| 1087 | } | |
| 1088 | } | |
| 1089 | ||
| 1090 | function upgradeProof (tree, node, seek, from, to, subTree, p) { | |
| 1091 | if (from === 0) p.upgrade = [] | |
| 1092 | ||
| 1093 | for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) { | |
| 1094 | // check if they already have the node | |
| 1095 | if (ite.index + ite.factor / 2 < from) continue | |
| 1096 | ||
| 1097 | // connect existing tree | |
| 1098 | if (p.upgrade === null && ite.contains(from - 2)) { | |
| 1099 | p.upgrade = [] | |
| 1100 | ||
| 1101 | const root = ite.index | |
| 1102 | const target = from - 2 | |
| 1103 | ||
| 1104 | ite.seek(target) | |
| 1105 | ||
| 1106 | while (ite.index !== root) { | |
| 1107 | ite.sibling() | |
| 1108 | if (ite.index > target) { | |
| 1109 | if (p.node === null && p.seek === null && ite.contains(subTree)) { | |
| 1110 | blockAndSeekProof(tree, node, seek, subTree, ite.index, p) | |
| 1111 | } else { | |
| 1112 | p.upgrade.push(tree.get(ite.index)) | |
| 1113 | } | |
| 1114 | } | |
| 1115 | ite.parent() | |
| 1116 | } | |
| 1117 | ||
| 1118 | continue | |
| 1119 | } | |
| 1120 | ||
| 1121 | if (p.upgrade === null) { | |
| 1122 | p.upgrade = [] | |
| 1123 | } | |
| 1124 | ||
| 1125 | // if the subtree included is a child of this tree, include that one | |
| 1126 | // instead of a dup node | |
| 1127 | if (p.node === null && p.seek === null && ite.contains(subTree)) { | |
| 1128 | blockAndSeekProof(tree, node, seek, subTree, ite.index, p) | |
| 1129 | continue | |
| 1130 | } | |
| 1131 | ||
| 1132 | // add root (can be optimised since the root might be in tree.roots) | |
| 1133 | p.upgrade.push(tree.get(ite.index)) | |
| 1134 | } | |
| 1135 | } | |
| 1136 | ||
| 1137 | function additionalUpgradeProof (tree, from, to, p) { | |
| 1138 | if (from === 0) p.additionalUpgrade = [] | |
| 1139 | ||
| 1140 | for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) { | |
| 1141 | // check if they already have the node | |
| 1142 | if (ite.index + ite.factor / 2 < from) continue | |
| 1143 | ||
| 1144 | // connect existing tree | |
| 1145 | if (p.additionalUpgrade === null && ite.contains(from - 2)) { | |
| 1146 | p.additionalUpgrade = [] | |
| 1147 | ||
| 1148 | const root = ite.index | |
| 1149 | const target = from - 2 | |
| 1150 | ||
| 1151 | ite.seek(target) | |
| 1152 | ||
| 1153 | while (ite.index !== root) { | |
| 1154 | ite.sibling() | |
| 1155 | if (ite.index > target) { | |
| 1156 | p.additionalUpgrade.push(tree.get(ite.index)) | |
| 1157 | } | |
| 1158 | ite.parent() | |
| 1159 | } | |
| 1160 | ||
| 1161 | continue | |
| 1162 | } | |
| 1163 | ||
| 1164 | if (p.additionalUpgrade === null) { | |
| 1165 | p.additionalUpgrade = [] | |
| 1166 | } | |
| 1167 | ||
| 1168 | // add root (can be optimised since the root is in tree.roots) | |
| 1169 | p.additionalUpgrade.push(tree.get(ite.index)) | |
| 1170 | } | |
| 1171 | } | |
| 1172 | ||
| 1173 | function nodesToRoot (index, nodes, head) { | |
| 1174 | const ite = flat.iterator(index) | |
| 1175 | ||
| 1176 | for (let i = 0; i < nodes; i++) { | |
| 1177 | ite.parent() | |
| 1178 | if (ite.contains(head)) throw BAD_ARGUMENT('Nodes is out of bounds') | |
| 1179 | } | |
| 1180 | ||
| 1181 | return ite.index | |
| 1182 | } | |
| 1183 | ||
| 1184 | function totalSize (nodes) { | |
| 1185 | let s = 0 | |
| 1186 | for (const node of nodes) s += node.size | |
| 1187 | return s | |
| 1188 | } | |
| 1189 | ||
| 1190 | function totalSpan (nodes) { | |
| 1191 | let s = 0 | |
| 1192 | for (const node of nodes) s += 2 * ((node.index - s) + 1) | |
| 1193 | return s | |
| 1194 | } | |
| 1195 | ||
| 1196 | function blockNode (crypto, index, value) { | |
| 1197 | return { index, size: value.byteLength, hash: crypto.data(value) } | |
| 1198 | } | |
| 1199 | ||
| 1200 | function parentNode (crypto, index, a, b) { | |
| 1201 | return { index, size: a.size + b.size, hash: crypto.parent(a, b) } | |
| 1202 | } | |
| 1203 | ||
| 1204 | function blankNode (index) { | |
| 1205 | return { index, size: 0, hash: BLANK_HASH } | |
| 1206 | } | |
| 1207 | ||
| 1208 | // Storage methods | |
| 1209 | ||
| 1210 | function getStoredNode (storage, index, cache, error) { | |
| 1211 | return new Promise((resolve, reject) => { | |
| 1212 | storage.read(40 * index, 40, (err, data) => { | |
| 1213 | if (err) { | |
| 1214 | if (error) return reject(err) | |
| 1215 | else resolve(null) | |
| 1216 | return | |
| 1217 | } | |
| 1218 | ||
| 1219 | const hash = data.subarray(8) | |
| 1220 | const size = c.decode(c.uint64, data) | |
| 1221 | ||
| 1222 | if (size === 0 && b4a.compare(hash, BLANK_HASH) === 0) { | |
| 1223 | if (error) reject(new Error('Could not load node: ' + index)) | |
| 1224 | else resolve(null) | |
| 1225 | return | |
| 1226 | } | |
| 1227 | ||
| 1228 | const node = { index, size, hash } | |
| 1229 | ||
| 1230 | if (cache !== null) { | |
| 1231 | // Copy hash to a new buffer to avoid blocking gc of its original slab | |
| 1232 | node.hash = unslab(hash) | |
| 1233 | cache.set(index, node) | |
| 1234 | } | |
| 1235 | ||
| 1236 | resolve(node) | |
| 1237 | }) | |
| 1238 | }) | |
| 1239 | } | |
| 1240 | ||
| 1241 | function storedNodes (storage) { | |
| 1242 | return new Promise((resolve) => { | |
| 1243 | storage.stat((_, st) => { | |
| 1244 | if (!st) return resolve(0) | |
| 1245 | resolve((st.size - (st.size % 40)) / 40) | |
| 1246 | }) | |
| 1247 | }) | |
| 1248 | } | |
| 1249 | ||
| 1250 | async function autoLength (storage) { | |
| 1251 | const nodes = await storedNodes(storage) | |
| 1252 | if (!nodes) return 0 | |
| 1253 | const ite = flat.iterator(nodes - 1) | |
| 1254 | let index = nodes - 1 | |
| 1255 | while (await getStoredNode(storage, ite.parent(), null, false)) index = ite.index | |
| 1256 | return flat.rightSpan(index) / 2 + 1 | |
| 1257 | } | |
| 1258 | ||
| 1259 | function truncateMap (map, len) { | |
| 1260 | for (const node of map.values()) { | |
| 1261 | if (node.index >= 2 * len) map.delete(node.index) | |
| 1262 | } | |
| 1263 | } | |
| 1264 | ||
| 1265 | function log2 (n) { | |
| 1266 | let res = 1 | |
| 1267 | ||
| 1268 | while (n > 2) { | |
| 1269 | n /= 2 | |
| 1270 | res++ | |
| 1271 | } | |
| 1272 | ||
| 1273 | return res | |
| 1274 | } | |
| 1275 | ||
| 1276 | function normalizeIndexed (block, hash) { | |
| 1277 | if (block) return { value: true, index: block.index * 2, nodes: block.nodes, lastIndex: block.index } | |
| 1278 | if (hash) return { value: false, index: hash.index, nodes: hash.nodes, lastIndex: flat.rightSpan(hash.index) / 2 } | |
| 1279 | return null | |
| 1280 | } | |
| 1281 | ||
| 1282 | async function settleProof (p) { | |
| 1283 | const result = [ | |
| 1284 | p.node && Promise.all(p.node), | |
| 1285 | p.seek && Promise.all(p.seek), | |
| 1286 | p.upgrade && Promise.all(p.upgrade), | |
| 1287 | p.additionalUpgrade && Promise.all(p.additionalUpgrade) | |
| 1288 | ] | |
| 1289 | ||
| 1290 | try { | |
| 1291 | return await Promise.all(result) | |
| 1292 | } catch (err) { | |
| 1293 | if (p.node) await Promise.allSettled(p.node) | |
| 1294 | if (p.seek) await Promise.allSettled(p.seek) | |
| 1295 | if (p.upgrade) await Promise.allSettled(p.upgrade) | |
| 1296 | if (p.additionalUpgrade) await Promise.allSettled(p.additionalUpgrade) | |
| 1297 | throw err | |
| 1298 | } | |
| 1299 | } | |
| 1300 | ||
| 1301 | // tree can be either the merkle tree or a merkle tree batch | |
| 1302 | async function generateProof (tree, block, hash, seek, upgrade) { | |
| 1303 | // Important that this does not throw inbetween making the promise arrays | |
| 1304 | // and finalise being called, otherwise there will be lingering promises in the background | |
| 1305 | ||
| 1306 | if (tree.prologue && upgrade) { | |
| 1307 | upgrade.start = upgrade.start < tree.prologue.length ? 0 : upgrade.start | |
| 1308 | upgrade.length = upgrade.start < tree.prologue.length ? tree.prologue.length : upgrade.length | |
| 1309 | } | |
| 1310 | ||
| 1311 | const fork = tree.fork | |
| 1312 | const signature = tree.signature | |
| 1313 | const head = 2 * tree.length | |
| 1314 | const from = upgrade ? upgrade.start * 2 : 0 | |
| 1315 | const to = upgrade ? from + upgrade.length * 2 : head | |
| 1316 | const node = normalizeIndexed(block, hash) | |
| 1317 | ||
| 1318 | const result = { fork, block: null, hash: null, seek: null, upgrade: null, manifest: null } | |
| 1319 | ||
| 1320 | // can't do anything as we have no data... | |
| 1321 | if (head === 0) return result | |
| 1322 | ||
| 1323 | if (from >= to || to > head) { | |
| 1324 | throw INVALID_OPERATION('Invalid upgrade') | |
| 1325 | } | |
| 1326 | if (seek && upgrade && node !== null && node.index >= from) { | |
| 1327 | throw INVALID_OPERATION('Cannot both do a seek and block/hash request when upgrading') | |
| 1328 | } | |
| 1329 | ||
| 1330 | let subTree = head | |
| 1331 | ||
| 1332 | const p = { | |
| 1333 | node: null, | |
| 1334 | seek: null, | |
| 1335 | upgrade: null, | |
| 1336 | additionalUpgrade: null | |
| 1337 | } | |
| 1338 | ||
| 1339 | if (node !== null && (!upgrade || node.lastIndex < upgrade.start)) { | |
| 1340 | subTree = nodesToRoot(node.index, node.nodes, to) | |
| 1341 | const seekRoot = seek ? await seekUntrustedTree(tree, subTree, seek.bytes, seek.padding) : head | |
| 1342 | blockAndSeekProof(tree, node, seek, seekRoot, subTree, p) | |
| 1343 | } else if ((node || seek) && upgrade) { | |
| 1344 | subTree = seek ? await seekFromHead(tree, to, seek.bytes, seek.padding) : node.index | |
| 1345 | } | |
| 1346 | ||
| 1347 | if (upgrade) { | |
| 1348 | upgradeProof(tree, node, seek, from, to, subTree, p) | |
| 1349 | if (head > to) additionalUpgradeProof(tree, to, head, p) | |
| 1350 | } | |
| 1351 | ||
| 1352 | const [pNode, pSeek, pUpgrade, pAdditional] = await settleProof(p) | |
| 1353 | ||
| 1354 | if (block) { | |
| 1355 | if (pNode === null) throw INVALID_OPERATION('Invalid block request') | |
| 1356 | result.block = { | |
| 1357 | index: block.index, | |
| 1358 | value: null, // populated upstream, alloc it here for simplicity | |
| 1359 | nodes: pNode | |
| 1360 | } | |
| 1361 | } else if (hash) { | |
| 1362 | if (pNode === null) throw INVALID_OPERATION('Invalid hash request') | |
| 1363 | result.hash = { | |
| 1364 | index: hash.index, | |
| 1365 | nodes: pNode | |
| 1366 | } | |
| 1367 | } | |
| 1368 | ||
| 1369 | if (seek && pSeek !== null) { | |
| 1370 | result.seek = { | |
| 1371 | bytes: seek.bytes, | |
| 1372 | nodes: pSeek | |
| 1373 | } | |
| 1374 | } | |
| 1375 | ||
| 1376 | if (upgrade) { | |
| 1377 | result.upgrade = { | |
| 1378 | start: upgrade.start, | |
| 1379 | length: upgrade.length, | |
| 1380 | nodes: pUpgrade, | |
| 1381 | additionalNodes: pAdditional || [], | |
| 1382 | signature | |
| 1383 | } | |
| 1384 | } | |
| 1385 | ||
| 1386 | return result | |
| 1387 | } | |
| 1388 | ||
| 1389 | function getUnpaddedSize (node, padding, ite) { | |
| 1390 | return padding === 0 ? node.size : node.size - padding * (ite ? ite.countLeaves() : flat.countLeaves(node.index)) | |
| 1391 | } | |
| 1392 | ||
| 1393 | function unslabNodes (nodes) { | |
| 1394 | for (const node of nodes) unslabNode(node) | |
| 1395 | return nodes | |
| 1396 | } | |
| 1397 | ||
| 1398 | function unslabNode (node) { | |
| 1399 | if (node === null) return node | |
| 1400 | node.hash = unslab(node.hash) | |
| 1401 | return node | |
| 1402 | } |
| … | … | @@ -0,0 +1,1069 @@ |
| 1 | const c = require('compact-encoding') | |
| 2 | const b4a = require('b4a') | |
| 3 | const { DEFAULT_NAMESPACE } = require('./caps') | |
| 4 | const { INVALID_OPLOG_VERSION } = require('hypercore-errors') | |
| 5 | const unslab = require('unslab') | |
| 6 | ||
| 7 | const EMPTY = b4a.alloc(0) | |
| 8 | ||
| 9 | const hashes = { | |
| 10 | preencode (state, m) { | |
| 11 | state.end++ // small uint | |
| 12 | }, | |
| 13 | encode (state, m) { | |
| 14 | if (m === 'blake2b') { | |
| 15 | c.uint.encode(state, 0) | |
| 16 | return | |
| 17 | } | |
| 18 | ||
| 19 | throw new Error('Unknown hash: ' + m) | |
| 20 | }, | |
| 21 | decode (state) { | |
| 22 | const n = c.uint.decode(state) | |
| 23 | if (n === 0) return 'blake2b' | |
| 24 | throw new Error('Unknown hash id: ' + n) | |
| 25 | } | |
| 26 | } | |
| 27 | ||
| 28 | const signatures = { | |
| 29 | preencode (state, m) { | |
| 30 | state.end++ // small uint | |
| 31 | }, | |
| 32 | encode (state, m) { | |
| 33 | if (m === 'ed25519') { | |
| 34 | c.uint.encode(state, 0) | |
| 35 | return | |
| 36 | } | |
| 37 | ||
| 38 | throw new Error('Unknown signature: ' + m) | |
| 39 | }, | |
| 40 | decode (state) { | |
| 41 | const n = c.uint.decode(state) | |
| 42 | if (n === 0) return 'ed25519' | |
| 43 | throw new Error('Unknown signature id: ' + n) | |
| 44 | } | |
| 45 | } | |
| 46 | ||
| 47 | const signer = { | |
| 48 | preencode (state, m) { | |
| 49 | signatures.preencode(state, m.signature) | |
| 50 | c.fixed32.preencode(state, m.namespace) | |
| 51 | c.fixed32.preencode(state, m.publicKey) | |
| 52 | }, | |
| 53 | encode (state, m) { | |
| 54 | signatures.encode(state, m.signature) | |
| 55 | c.fixed32.encode(state, m.namespace) | |
| 56 | c.fixed32.encode(state, m.publicKey) | |
| 57 | }, | |
| 58 | decode (state) { | |
| 59 | return { | |
| 60 | signature: signatures.decode(state), | |
| 61 | namespace: c.fixed32.decode(state), | |
| 62 | publicKey: c.fixed32.decode(state) | |
| 63 | } | |
| 64 | } | |
| 65 | } | |
| 66 | ||
| 67 | const signerArray = c.array(signer) | |
| 68 | ||
| 69 | const prologue = { | |
| 70 | preencode (state, p) { | |
| 71 | c.fixed32.preencode(state, p.hash) | |
| 72 | c.uint.preencode(state, p.length) | |
| 73 | }, | |
| 74 | encode (state, p) { | |
| 75 | c.fixed32.encode(state, p.hash) | |
| 76 | c.uint.encode(state, p.length) | |
| 77 | }, | |
| 78 | decode (state) { | |
| 79 | return { | |
| 80 | hash: c.fixed32.decode(state), | |
| 81 | length: c.uint.decode(state) | |
| 82 | } | |
| 83 | } | |
| 84 | } | |
| 85 | ||
| 86 | const manifestv0 = { | |
| 87 | preencode (state, m) { | |
| 88 | hashes.preencode(state, m.hash) | |
| 89 | state.end++ // type | |
| 90 | ||
| 91 | if (m.prologue && m.signers.length === 0) { | |
| 92 | c.fixed32.preencode(state, m.prologue.hash) | |
| 93 | return | |
| 94 | } | |
| 95 | ||
| 96 | if (m.quorum === 1 && m.signers.length === 1 && !m.allowPatch) { | |
| 97 | signer.preencode(state, m.signers[0]) | |
| 98 | } else { | |
| 99 | state.end++ // flags | |
| 100 | c.uint.preencode(state, m.quorum) | |
| 101 | signerArray.preencode(state, m.signers) | |
| 102 | } | |
| 103 | }, | |
| 104 | encode (state, m) { | |
| 105 | hashes.encode(state, m.hash) | |
| 106 | ||
| 107 | if (m.prologue && m.signers.length === 0) { | |
| 108 | c.uint.encode(state, 0) | |
| 109 | c.fixed32.encode(state, m.prologue.hash) | |
| 110 | return | |
| 111 | } | |
| 112 | ||
| 113 | if (m.quorum === 1 && m.signers.length === 1 && !m.allowPatch) { | |
| 114 | c.uint.encode(state, 1) | |
| 115 | signer.encode(state, m.signers[0]) | |
| 116 | } else { | |
| 117 | c.uint.encode(state, 2) | |
| 118 | c.uint.encode(state, m.allowPatch ? 1 : 0) | |
| 119 | c.uint.encode(state, m.quorum) | |
| 120 | signerArray.encode(state, m.signers) | |
| 121 | } | |
| 122 | }, | |
| 123 | decode (state) { | |
| 124 | const hash = hashes.decode(state) | |
| 125 | const type = c.uint.decode(state) | |
| 126 | ||
| 127 | if (type > 2) throw new Error('Unknown type: ' + type) | |
| 128 | ||
| 129 | if (type === 0) { | |
| 130 | return { | |
| 131 | version: 0, | |
| 132 | hash, | |
| 133 | allowPatch: false, | |
| 134 | quorum: 0, | |
| 135 | signers: [], | |
| 136 | prologue: { | |
| 137 | hash: c.fixed32.decode(state), | |
| 138 | length: 0 | |
| 139 | } | |
| 140 | } | |
| 141 | } | |
| 142 | ||
| 143 | if (type === 1) { | |
| 144 | return { | |
| 145 | version: 0, | |
| 146 | hash, | |
| 147 | allowPatch: false, | |
| 148 | quorum: 1, | |
| 149 | signers: [signer.decode(state)], | |
| 150 | prologue: null | |
| 151 | } | |
| 152 | } | |
| 153 | ||
| 154 | const flags = c.uint.decode(state) | |
| 155 | ||
| 156 | return { | |
| 157 | version: 0, | |
| 158 | hash, | |
| 159 | allowPatch: (flags & 1) !== 0, | |
| 160 | quorum: c.uint.decode(state), | |
| 161 | signers: signerArray.decode(state), | |
| 162 | prologue: null | |
| 163 | } | |
| 164 | } | |
| 165 | } | |
| 166 | ||
| 167 | const manifest = exports.manifest = { | |
| 168 | preencode (state, m) { | |
| 169 | state.end++ // version | |
| 170 | if (m.version === 0) return manifestv0.preencode(state, m) | |
| 171 | ||
| 172 | state.end++ // flags | |
| 173 | hashes.preencode(state, m.hash) | |
| 174 | ||
| 175 | c.uint.preencode(state, m.quorum) | |
| 176 | signerArray.preencode(state, m.signers) | |
| 177 | if (m.prologue) prologue.preencode(state, m.prologue) | |
| 178 | }, | |
| 179 | encode (state, m) { | |
| 180 | c.uint.encode(state, m.version) | |
| 181 | if (m.version === 0) return manifestv0.encode(state, m) | |
| 182 | ||
| 183 | c.uint.encode(state, (m.allowPatch ? 1 : 0) | (m.prologue ? 2 : 0)) | |
| 184 | hashes.encode(state, m.hash) | |
| 185 | ||
| 186 | c.uint.encode(state, m.quorum) | |
| 187 | signerArray.encode(state, m.signers) | |
| 188 | if (m.prologue) prologue.encode(state, m.prologue) | |
| 189 | }, | |
| 190 | decode (state) { | |
| 191 | const v = c.uint.decode(state) | |
| 192 | if (v === 0) return manifestv0.decode(state) | |
| 193 | if (v !== 1) throw new Error('Unknown version: ' + v) | |
| 194 | ||
| 195 | const flags = c.uint.decode(state) | |
| 196 | const hash = hashes.decode(state) | |
| 197 | const quorum = c.uint.decode(state) | |
| 198 | const signers = signerArray.decode(state) | |
| 199 | ||
| 200 | return { | |
| 201 | version: 1, | |
| 202 | hash, | |
| 203 | allowPatch: (flags & 1) !== 0, | |
| 204 | quorum, | |
| 205 | signers, | |
| 206 | prologue: (flags & 2) === 0 ? null : prologue.decode(state) | |
| 207 | } | |
| 208 | } | |
| 209 | } | |
| 210 | ||
| 211 | const node = { | |
| 212 | preencode (state, n) { | |
| 213 | c.uint.preencode(state, n.index) | |
| 214 | c.uint.preencode(state, n.size) | |
| 215 | c.fixed32.preencode(state, n.hash) | |
| 216 | }, | |
| 217 | encode (state, n) { | |
| 218 | c.uint.encode(state, n.index) | |
| 219 | c.uint.encode(state, n.size) | |
| 220 | c.fixed32.encode(state, n.hash) | |
| 221 | }, | |
| 222 | decode (state) { | |
| 223 | return { | |
| 224 | index: c.uint.decode(state), | |
| 225 | size: c.uint.decode(state), | |
| 226 | hash: c.fixed32.decode(state) | |
| 227 | } | |
| 228 | } | |
| 229 | } | |
| 230 | ||
| 231 | const nodeArray = c.array(node) | |
| 232 | ||
| 233 | const wire = exports.wire = {} | |
| 234 | ||
| 235 | wire.handshake = { | |
| 236 | preencode (state, m) { | |
| 237 | c.uint.preencode(state, 1) | |
| 238 | c.fixed32.preencode(state, m.capability) | |
| 239 | }, | |
| 240 | encode (state, m) { | |
| 241 | c.uint.encode(state, m.seeks ? 1 : 0) | |
| 242 | c.fixed32.encode(state, m.capability) | |
| 243 | }, | |
| 244 | decode (state) { | |
| 245 | const flags = c.uint.decode(state) | |
| 246 | return { | |
| 247 | seeks: (flags & 1) !== 0, | |
| 248 | capability: unslab(c.fixed32.decode(state)) | |
| 249 | } | |
| 250 | } | |
| 251 | } | |
| 252 | ||
| 253 | const requestBlock = { | |
| 254 | preencode (state, b) { | |
| 255 | c.uint.preencode(state, b.index) | |
| 256 | c.uint.preencode(state, b.nodes) | |
| 257 | }, | |
| 258 | encode (state, b) { | |
| 259 | c.uint.encode(state, b.index) | |
| 260 | c.uint.encode(state, b.nodes) | |
| 261 | }, | |
| 262 | decode (state) { | |
| 263 | return { | |
| 264 | index: c.uint.decode(state), | |
| 265 | nodes: c.uint.decode(state) | |
| 266 | } | |
| 267 | } | |
| 268 | } | |
| 269 | ||
| 270 | const requestSeek = { | |
| 271 | preencode (state, s) { | |
| 272 | c.uint.preencode(state, s.bytes) | |
| 273 | c.uint.preencode(state, s.padding) | |
| 274 | }, | |
| 275 | encode (state, s) { | |
| 276 | c.uint.encode(state, s.bytes) | |
| 277 | c.uint.encode(state, s.padding) | |
| 278 | }, | |
| 279 | decode (state) { | |
| 280 | return { | |
| 281 | bytes: c.uint.decode(state), | |
| 282 | padding: c.uint.decode(state) | |
| 283 | } | |
| 284 | } | |
| 285 | } | |
| 286 | ||
| 287 | const requestUpgrade = { | |
| 288 | preencode (state, u) { | |
| 289 | c.uint.preencode(state, u.start) | |
| 290 | c.uint.preencode(state, u.length) | |
| 291 | }, | |
| 292 | encode (state, u) { | |
| 293 | c.uint.encode(state, u.start) | |
| 294 | c.uint.encode(state, u.length) | |
| 295 | }, | |
| 296 | decode (state) { | |
| 297 | return { | |
| 298 | start: c.uint.decode(state), | |
| 299 | length: c.uint.decode(state) | |
| 300 | } | |
| 301 | } | |
| 302 | } | |
| 303 | ||
| 304 | wire.request = { | |
| 305 | preencode (state, m) { | |
| 306 | state.end++ // flags | |
| 307 | c.uint.preencode(state, m.id) | |
| 308 | c.uint.preencode(state, m.fork) | |
| 309 | ||
| 310 | if (m.block) requestBlock.preencode(state, m.block) | |
| 311 | if (m.hash) requestBlock.preencode(state, m.hash) | |
| 312 | if (m.seek) requestSeek.preencode(state, m.seek) | |
| 313 | if (m.upgrade) requestUpgrade.preencode(state, m.upgrade) | |
| 314 | if (m.priority) c.uint.preencode(state, m.priority) | |
| 315 | }, | |
| 316 | encode (state, m) { | |
| 317 | const flags = (m.block ? 1 : 0) | (m.hash ? 2 : 0) | (m.seek ? 4 : 0) | (m.upgrade ? 8 : 0) | (m.manifest ? 16 : 0) | (m.priority ? 32 : 0) | |
| 318 | ||
| 319 | c.uint.encode(state, flags) | |
| 320 | c.uint.encode(state, m.id) | |
| 321 | c.uint.encode(state, m.fork) | |
| 322 | ||
| 323 | if (m.block) requestBlock.encode(state, m.block) | |
| 324 | if (m.hash) requestBlock.encode(state, m.hash) | |
| 325 | if (m.seek) requestSeek.encode(state, m.seek) | |
| 326 | if (m.upgrade) requestUpgrade.encode(state, m.upgrade) | |
| 327 | if (m.priority) c.uint.encode(state, m.priority) | |
| 328 | }, | |
| 329 | decode (state) { | |
| 330 | const flags = c.uint.decode(state) | |
| 331 | ||
| 332 | return { | |
| 333 | id: c.uint.decode(state), | |
| 334 | fork: c.uint.decode(state), | |
| 335 | block: flags & 1 ? requestBlock.decode(state) : null, | |
| 336 | hash: flags & 2 ? requestBlock.decode(state) : null, | |
| 337 | seek: flags & 4 ? requestSeek.decode(state) : null, | |
| 338 | upgrade: flags & 8 ? requestUpgrade.decode(state) : null, | |
| 339 | manifest: (flags & 16) !== 0, | |
| 340 | priority: flags & 32 ? c.uint.decode(state) : 0 | |
| 341 | } | |
| 342 | } | |
| 343 | } | |
| 344 | ||
| 345 | wire.cancel = { | |
| 346 | preencode (state, m) { | |
| 347 | c.uint.preencode(state, m.request) | |
| 348 | }, | |
| 349 | encode (state, m) { | |
| 350 | c.uint.encode(state, m.request) | |
| 351 | }, | |
| 352 | decode (state, m) { | |
| 353 | return { | |
| 354 | request: c.uint.decode(state) | |
| 355 | } | |
| 356 | } | |
| 357 | } | |
| 358 | ||
| 359 | const dataUpgrade = { | |
| 360 | preencode (state, u) { | |
| 361 | c.uint.preencode(state, u.start) | |
| 362 | c.uint.preencode(state, u.length) | |
| 363 | nodeArray.preencode(state, u.nodes) | |
| 364 | nodeArray.preencode(state, u.additionalNodes) | |
| 365 | c.buffer.preencode(state, u.signature) | |
| 366 | }, | |
| 367 | encode (state, u) { | |
| 368 | c.uint.encode(state, u.start) | |
| 369 | c.uint.encode(state, u.length) | |
| 370 | nodeArray.encode(state, u.nodes) | |
| 371 | nodeArray.encode(state, u.additionalNodes) | |
| 372 | c.buffer.encode(state, u.signature) | |
| 373 | }, | |
| 374 | decode (state) { | |
| 375 | return { | |
| 376 | start: c.uint.decode(state), | |
| 377 | length: c.uint.decode(state), | |
| 378 | nodes: nodeArray.decode(state), | |
| 379 | additionalNodes: nodeArray.decode(state), | |
| 380 | signature: c.buffer.decode(state) | |
| 381 | } | |
| 382 | } | |
| 383 | } | |
| 384 | ||
| 385 | const dataSeek = { | |
| 386 | preencode (state, s) { | |
| 387 | c.uint.preencode(state, s.bytes) | |
| 388 | nodeArray.preencode(state, s.nodes) | |
| 389 | }, | |
| 390 | encode (state, s) { | |
| 391 | c.uint.encode(state, s.bytes) | |
| 392 | nodeArray.encode(state, s.nodes) | |
| 393 | }, | |
| 394 | decode (state) { | |
| 395 | return { | |
| 396 | bytes: c.uint.decode(state), | |
| 397 | nodes: nodeArray.decode(state) | |
| 398 | } | |
| 399 | } | |
| 400 | } | |
| 401 | ||
| 402 | const dataBlock = { | |
| 403 | preencode (state, b) { | |
| 404 | c.uint.preencode(state, b.index) | |
| 405 | c.buffer.preencode(state, b.value) | |
| 406 | nodeArray.preencode(state, b.nodes) | |
| 407 | }, | |
| 408 | encode (state, b) { | |
| 409 | c.uint.encode(state, b.index) | |
| 410 | c.buffer.encode(state, b.value) | |
| 411 | nodeArray.encode(state, b.nodes) | |
| 412 | }, | |
| 413 | decode (state) { | |
| 414 | return { | |
| 415 | index: c.uint.decode(state), | |
| 416 | value: c.buffer.decode(state) || EMPTY, | |
| 417 | nodes: nodeArray.decode(state) | |
| 418 | } | |
| 419 | } | |
| 420 | } | |
| 421 | ||
| 422 | const dataHash = { | |
| 423 | preencode (state, b) { | |
| 424 | c.uint.preencode(state, b.index) | |
| 425 | nodeArray.preencode(state, b.nodes) | |
| 426 | }, | |
| 427 | encode (state, b) { | |
| 428 | c.uint.encode(state, b.index) | |
| 429 | nodeArray.encode(state, b.nodes) | |
| 430 | }, | |
| 431 | decode (state) { | |
| 432 | return { | |
| 433 | index: c.uint.decode(state), | |
| 434 | nodes: nodeArray.decode(state) | |
| 435 | } | |
| 436 | } | |
| 437 | } | |
| 438 | ||
| 439 | wire.data = { | |
| 440 | preencode (state, m) { | |
| 441 | state.end++ // flags | |
| 442 | c.uint.preencode(state, m.request) | |
| 443 | c.uint.preencode(state, m.fork) | |
| 444 | ||
| 445 | if (m.block) dataBlock.preencode(state, m.block) | |
| 446 | if (m.hash) dataHash.preencode(state, m.hash) | |
| 447 | if (m.seek) dataSeek.preencode(state, m.seek) | |
| 448 | if (m.upgrade) dataUpgrade.preencode(state, m.upgrade) | |
| 449 | if (m.manifest) manifest.preencode(state, m.manifest) | |
| 450 | }, | |
| 451 | encode (state, m) { | |
| 452 | const flags = (m.block ? 1 : 0) | (m.hash ? 2 : 0) | (m.seek ? 4 : 0) | (m.upgrade ? 8 : 0) | (m.manifest ? 16 : 0) | |
| 453 | ||
| 454 | c.uint.encode(state, flags) | |
| 455 | c.uint.encode(state, m.request) | |
| 456 | c.uint.encode(state, m.fork) | |
| 457 | ||
| 458 | if (m.block) dataBlock.encode(state, m.block) | |
| 459 | if (m.hash) dataHash.encode(state, m.hash) | |
| 460 | if (m.seek) dataSeek.encode(state, m.seek) | |
| 461 | if (m.upgrade) dataUpgrade.encode(state, m.upgrade) | |
| 462 | if (m.manifest) manifest.encode(state, m.manifest) | |
| 463 | }, | |
| 464 | decode (state) { | |
| 465 | const flags = c.uint.decode(state) | |
| 466 | ||
| 467 | return { | |
| 468 | request: c.uint.decode(state), | |
| 469 | fork: c.uint.decode(state), | |
| 470 | block: flags & 1 ? dataBlock.decode(state) : null, | |
| 471 | hash: flags & 2 ? dataHash.decode(state) : null, | |
| 472 | seek: flags & 4 ? dataSeek.decode(state) : null, | |
| 473 | upgrade: flags & 8 ? dataUpgrade.decode(state) : null, | |
| 474 | manifest: flags & 16 ? manifest.decode(state) : null | |
| 475 | } | |
| 476 | } | |
| 477 | } | |
| 478 | ||
| 479 | wire.noData = { | |
| 480 | preencode (state, m) { | |
| 481 | c.uint.preencode(state, m.request) | |
| 482 | }, | |
| 483 | encode (state, m) { | |
| 484 | c.uint.encode(state, m.request) | |
| 485 | }, | |
| 486 | decode (state, m) { | |
| 487 | return { | |
| 488 | request: c.uint.decode(state) | |
| 489 | } | |
| 490 | } | |
| 491 | } | |
| 492 | ||
| 493 | wire.want = { | |
| 494 | preencode (state, m) { | |
| 495 | c.uint.preencode(state, m.start) | |
| 496 | c.uint.preencode(state, m.length) | |
| 497 | }, | |
| 498 | encode (state, m) { | |
| 499 | c.uint.encode(state, m.start) | |
| 500 | c.uint.encode(state, m.length) | |
| 501 | }, | |
| 502 | decode (state) { | |
| 503 | return { | |
| 504 | start: c.uint.decode(state), | |
| 505 | length: c.uint.decode(state) | |
| 506 | } | |
| 507 | } | |
| 508 | } | |
| 509 | ||
| 510 | wire.unwant = { | |
| 511 | preencode (state, m) { | |
| 512 | c.uint.preencode(state, m.start) | |
| 513 | c.uint.preencode(state, m.length) | |
| 514 | }, | |
| 515 | encode (state, m) { | |
| 516 | c.uint.encode(state, m.start) | |
| 517 | c.uint.encode(state, m.length) | |
| 518 | }, | |
| 519 | decode (state, m) { | |
| 520 | return { | |
| 521 | start: c.uint.decode(state), | |
| 522 | length: c.uint.decode(state) | |
| 523 | } | |
| 524 | } | |
| 525 | } | |
| 526 | ||
| 527 | wire.range = { | |
| 528 | preencode (state, m) { | |
| 529 | state.end++ // flags | |
| 530 | c.uint.preencode(state, m.start) | |
| 531 | if (m.length !== 1) c.uint.preencode(state, m.length) | |
| 532 | }, | |
| 533 | encode (state, m) { | |
| 534 | c.uint.encode(state, (m.drop ? 1 : 0) | (m.length === 1 ? 2 : 0)) | |
| 535 | c.uint.encode(state, m.start) | |
| 536 | if (m.length !== 1) c.uint.encode(state, m.length) | |
| 537 | }, | |
| 538 | decode (state) { | |
| 539 | const flags = c.uint.decode(state) | |
| 540 | ||
| 541 | return { | |
| 542 | drop: (flags & 1) !== 0, | |
| 543 | start: c.uint.decode(state), | |
| 544 | length: (flags & 2) !== 0 ? 1 : c.uint.decode(state) | |
| 545 | } | |
| 546 | } | |
| 547 | } | |
| 548 | ||
| 549 | wire.bitfield = { | |
| 550 | preencode (state, m) { | |
| 551 | c.uint.preencode(state, m.start) | |
| 552 | c.uint32array.preencode(state, m.bitfield) | |
| 553 | }, | |
| 554 | encode (state, m) { | |
| 555 | c.uint.encode(state, m.start) | |
| 556 | c.uint32array.encode(state, m.bitfield) | |
| 557 | }, | |
| 558 | decode (state, m) { | |
| 559 | return { | |
| 560 | start: c.uint.decode(state), | |
| 561 | bitfield: c.uint32array.decode(state) | |
| 562 | } | |
| 563 | } | |
| 564 | } | |
| 565 | ||
| 566 | wire.sync = { | |
| 567 | preencode (state, m) { | |
| 568 | state.end++ // flags | |
| 569 | c.uint.preencode(state, m.fork) | |
| 570 | c.uint.preencode(state, m.length) | |
| 571 | c.uint.preencode(state, m.remoteLength) | |
| 572 | }, | |
| 573 | encode (state, m) { | |
| 574 | c.uint.encode(state, (m.canUpgrade ? 1 : 0) | (m.uploading ? 2 : 0) | (m.downloading ? 4 : 0) | (m.hasManifest ? 8 : 0)) | |
| 575 | c.uint.encode(state, m.fork) | |
| 576 | c.uint.encode(state, m.length) | |
| 577 | c.uint.encode(state, m.remoteLength) | |
| 578 | }, | |
| 579 | decode (state) { | |
| 580 | const flags = c.uint.decode(state) | |
| 581 | ||
| 582 | return { | |
| 583 | fork: c.uint.decode(state), | |
| 584 | length: c.uint.decode(state), | |
| 585 | remoteLength: c.uint.decode(state), | |
| 586 | canUpgrade: (flags & 1) !== 0, | |
| 587 | uploading: (flags & 2) !== 0, | |
| 588 | downloading: (flags & 4) !== 0, | |
| 589 | hasManifest: (flags & 8) !== 0 | |
| 590 | } | |
| 591 | } | |
| 592 | } | |
| 593 | ||
| 594 | wire.reorgHint = { | |
| 595 | preencode (state, m) { | |
| 596 | c.uint.preencode(state, m.from) | |
| 597 | c.uint.preencode(state, m.to) | |
| 598 | c.uint.preencode(state, m.ancestors) | |
| 599 | }, | |
| 600 | encode (state, m) { | |
| 601 | c.uint.encode(state, m.from) | |
| 602 | c.uint.encode(state, m.to) | |
| 603 | c.uint.encode(state, m.ancestors) | |
| 604 | }, | |
| 605 | decode (state) { | |
| 606 | return { | |
| 607 | from: c.uint.encode(state), | |
| 608 | to: c.uint.encode(state), | |
| 609 | ancestors: c.uint.encode(state) | |
| 610 | } | |
| 611 | } | |
| 612 | } | |
| 613 | ||
| 614 | wire.extension = { | |
| 615 | preencode (state, m) { | |
| 616 | c.string.preencode(state, m.name) | |
| 617 | c.raw.preencode(state, m.message) | |
| 618 | }, | |
| 619 | encode (state, m) { | |
| 620 | c.string.encode(state, m.name) | |
| 621 | c.raw.encode(state, m.message) | |
| 622 | }, | |
| 623 | decode (state) { | |
| 624 | return { | |
| 625 | name: c.string.decode(state), | |
| 626 | message: c.raw.decode(state) | |
| 627 | } | |
| 628 | } | |
| 629 | } | |
| 630 | ||
| 631 | const keyValue = { | |
| 632 | preencode (state, p) { | |
| 633 | c.string.preencode(state, p.key) | |
| 634 | c.buffer.preencode(state, p.value) | |
| 635 | }, | |
| 636 | encode (state, p) { | |
| 637 | c.string.encode(state, p.key) | |
| 638 | c.buffer.encode(state, p.value) | |
| 639 | }, | |
| 640 | decode (state) { | |
| 641 | return { | |
| 642 | key: c.string.decode(state), | |
| 643 | value: c.buffer.decode(state) | |
| 644 | } | |
| 645 | } | |
| 646 | } | |
| 647 | ||
| 648 | const treeUpgrade = { | |
| 649 | preencode (state, u) { | |
| 650 | c.uint.preencode(state, u.fork) | |
| 651 | c.uint.preencode(state, u.ancestors) | |
| 652 | c.uint.preencode(state, u.length) | |
| 653 | c.buffer.preencode(state, u.signature) | |
| 654 | }, | |
| 655 | encode (state, u) { | |
| 656 | c.uint.encode(state, u.fork) | |
| 657 | c.uint.encode(state, u.ancestors) | |
| 658 | c.uint.encode(state, u.length) | |
| 659 | c.buffer.encode(state, u.signature) | |
| 660 | }, | |
| 661 | decode (state) { | |
| 662 | return { | |
| 663 | fork: c.uint.decode(state), | |
| 664 | ancestors: c.uint.decode(state), | |
| 665 | length: c.uint.decode(state), | |
| 666 | signature: c.buffer.decode(state) | |
| 667 | } | |
| 668 | } | |
| 669 | } | |
| 670 | ||
| 671 | const bitfieldUpdate = { // TODO: can maybe be folded into a HAVE later on with the most recent spec | |
| 672 | preencode (state, b) { | |
| 673 | state.end++ // flags | |
| 674 | c.uint.preencode(state, b.start) | |
| 675 | c.uint.preencode(state, b.length) | |
| 676 | }, | |
| 677 | encode (state, b) { | |
| 678 | state.buffer[state.start++] = b.drop ? 1 : 0 | |
| 679 | c.uint.encode(state, b.start) | |
| 680 | c.uint.encode(state, b.length) | |
| 681 | }, | |
| 682 | decode (state) { | |
| 683 | const flags = c.uint.decode(state) | |
| 684 | return { | |
| 685 | drop: (flags & 1) !== 0, | |
| 686 | start: c.uint.decode(state), | |
| 687 | length: c.uint.decode(state) | |
| 688 | } | |
| 689 | } | |
| 690 | } | |
| 691 | ||
| 692 | const oplog = exports.oplog = {} | |
| 693 | ||
| 694 | oplog.entry = { | |
| 695 | preencode (state, m) { | |
| 696 | state.end++ // flags | |
| 697 | if (m.userData) keyValue.preencode(state, m.userData) | |
| 698 | if (m.treeNodes) nodeArray.preencode(state, m.treeNodes) | |
| 699 | if (m.treeUpgrade) treeUpgrade.preencode(state, m.treeUpgrade) | |
| 700 | if (m.bitfield) bitfieldUpdate.preencode(state, m.bitfield) | |
| 701 | }, | |
| 702 | encode (state, m) { | |
| 703 | const s = state.start++ | |
| 704 | let flags = 0 | |
| 705 | ||
| 706 | if (m.userData) { | |
| 707 | flags |= 1 | |
| 708 | keyValue.encode(state, m.userData) | |
| 709 | } | |
| 710 | if (m.treeNodes) { | |
| 711 | flags |= 2 | |
| 712 | nodeArray.encode(state, m.treeNodes) | |
| 713 | } | |
| 714 | if (m.treeUpgrade) { | |
| 715 | flags |= 4 | |
| 716 | treeUpgrade.encode(state, m.treeUpgrade) | |
| 717 | } | |
| 718 | if (m.bitfield) { | |
| 719 | flags |= 8 | |
| 720 | bitfieldUpdate.encode(state, m.bitfield) | |
| 721 | } | |
| 722 | ||
| 723 | state.buffer[s] = flags | |
| 724 | }, | |
| 725 | decode (state) { | |
| 726 | const flags = c.uint.decode(state) | |
| 727 | return { | |
| 728 | userData: (flags & 1) !== 0 ? keyValue.decode(state) : null, | |
| 729 | treeNodes: (flags & 2) !== 0 ? nodeArray.decode(state) : null, | |
| 730 | treeUpgrade: (flags & 4) !== 0 ? treeUpgrade.decode(state) : null, | |
| 731 | bitfield: (flags & 8) !== 0 ? bitfieldUpdate.decode(state) : null | |
| 732 | } | |
| 733 | } | |
| 734 | } | |
| 735 | ||
| 736 | const keyPair = { | |
| 737 | preencode (state, kp) { | |
| 738 | c.buffer.preencode(state, kp.publicKey) | |
| 739 | c.buffer.preencode(state, kp.secretKey) | |
| 740 | }, | |
| 741 | encode (state, kp) { | |
| 742 | c.buffer.encode(state, kp.publicKey) | |
| 743 | c.buffer.encode(state, kp.secretKey) | |
| 744 | }, | |
| 745 | decode (state) { | |
| 746 | return { | |
| 747 | publicKey: c.buffer.decode(state), | |
| 748 | secretKey: c.buffer.decode(state) | |
| 749 | } | |
| 750 | } | |
| 751 | } | |
| 752 | ||
| 753 | const reorgHint = { | |
| 754 | preencode (state, r) { | |
| 755 | c.uint.preencode(state, r.from) | |
| 756 | c.uint.preencode(state, r.to) | |
| 757 | c.uint.preencode(state, r.ancestors) | |
| 758 | }, | |
| 759 | encode (state, r) { | |
| 760 | c.uint.encode(state, r.from) | |
| 761 | c.uint.encode(state, r.to) | |
| 762 | c.uint.encode(state, r.ancestors) | |
| 763 | }, | |
| 764 | decode (state) { | |
| 765 | return { | |
| 766 | from: c.uint.decode(state), | |
| 767 | to: c.uint.decode(state), | |
| 768 | ancestors: c.uint.decode(state) | |
| 769 | } | |
| 770 | } | |
| 771 | } | |
| 772 | ||
| 773 | const reorgHintArray = c.array(reorgHint) | |
| 774 | ||
| 775 | const hints = { | |
| 776 | preencode (state, h) { | |
| 777 | reorgHintArray.preencode(state, h.reorgs) | |
| 778 | c.uint.preencode(state, h.contiguousLength) | |
| 779 | }, | |
| 780 | encode (state, h) { | |
| 781 | reorgHintArray.encode(state, h.reorgs) | |
| 782 | c.uint.encode(state, h.contiguousLength) | |
| 783 | }, | |
| 784 | decode (state) { | |
| 785 | return { | |
| 786 | reorgs: reorgHintArray.decode(state), | |
| 787 | contiguousLength: state.start < state.end ? c.uint.decode(state) : 0 | |
| 788 | } | |
| 789 | } | |
| 790 | } | |
| 791 | ||
| 792 | const treeHeader = { | |
| 793 | preencode (state, t) { | |
| 794 | c.uint.preencode(state, t.fork) | |
| 795 | c.uint.preencode(state, t.length) | |
| 796 | c.buffer.preencode(state, t.rootHash) | |
| 797 | c.buffer.preencode(state, t.signature) | |
| 798 | }, | |
| 799 | encode (state, t) { | |
| 800 | c.uint.encode(state, t.fork) | |
| 801 | c.uint.encode(state, t.length) | |
| 802 | c.buffer.encode(state, t.rootHash) | |
| 803 | c.buffer.encode(state, t.signature) | |
| 804 | }, | |
| 805 | decode (state) { | |
| 806 | return { | |
| 807 | fork: c.uint.decode(state), | |
| 808 | length: c.uint.decode(state), | |
| 809 | rootHash: c.buffer.decode(state), | |
| 810 | signature: c.buffer.decode(state) | |
| 811 | } | |
| 812 | } | |
| 813 | } | |
| 814 | ||
| 815 | const types = { | |
| 816 | preencode (state, t) { | |
| 817 | c.string.preencode(state, t.tree) | |
| 818 | c.string.preencode(state, t.bitfield) | |
| 819 | c.string.preencode(state, t.signer) | |
| 820 | }, | |
| 821 | encode (state, t) { | |
| 822 | c.string.encode(state, t.tree) | |
| 823 | c.string.encode(state, t.bitfield) | |
| 824 | c.string.encode(state, t.signer) | |
| 825 | }, | |
| 826 | decode (state) { | |
| 827 | return { | |
| 828 | tree: c.string.decode(state), | |
| 829 | bitfield: c.string.decode(state), | |
| 830 | signer: c.string.decode(state) | |
| 831 | } | |
| 832 | } | |
| 833 | } | |
| 834 | ||
| 835 | const externalHeader = { | |
| 836 | preencode (state, m) { | |
| 837 | c.uint.preencode(state, m.start) | |
| 838 | c.uint.preencode(state, m.length) | |
| 839 | }, | |
| 840 | encode (state, m) { | |
| 841 | c.uint.encode(state, m.start) | |
| 842 | c.uint.encode(state, m.length) | |
| 843 | }, | |
| 844 | decode (state) { | |
| 845 | return { | |
| 846 | start: c.uint.decode(state), | |
| 847 | length: c.uint.decode(state) | |
| 848 | } | |
| 849 | } | |
| 850 | } | |
| 851 | ||
| 852 | const keyValueArray = c.array(keyValue) | |
| 853 | ||
| 854 | oplog.header = { | |
| 855 | preencode (state, h) { | |
| 856 | state.end += 2 // version + flags | |
| 857 | if (h.external) { | |
| 858 | externalHeader.preencode(state, h.external) | |
| 859 | return | |
| 860 | } | |
| 861 | c.fixed32.preencode(state, h.key) | |
| 862 | if (h.manifest) manifest.preencode(state, h.manifest) | |
| 863 | if (h.keyPair) keyPair.preencode(state, h.keyPair) | |
| 864 | keyValueArray.preencode(state, h.userData) | |
| 865 | treeHeader.preencode(state, h.tree) | |
| 866 | hints.preencode(state, h.hints) | |
| 867 | }, | |
| 868 | encode (state, h) { | |
| 869 | c.uint.encode(state, 1) | |
| 870 | if (h.external) { | |
| 871 | c.uint.encode(state, 1) // ONLY set the first big for clarity | |
| 872 | externalHeader.encode(state, h.external) | |
| 873 | return | |
| 874 | } | |
| 875 | c.uint.encode(state, (h.manifest ? 2 : 0) | (h.keyPair ? 4 : 0)) | |
| 876 | c.fixed32.encode(state, h.key) | |
| 877 | if (h.manifest) manifest.encode(state, h.manifest) | |
| 878 | if (h.keyPair) keyPair.encode(state, h.keyPair) | |
| 879 | keyValueArray.encode(state, h.userData) | |
| 880 | treeHeader.encode(state, h.tree) | |
| 881 | hints.encode(state, h.hints) | |
| 882 | }, | |
| 883 | decode (state) { | |
| 884 | const version = c.uint.decode(state) | |
| 885 | ||
| 886 | if (version > 1) { | |
| 887 | throw INVALID_OPLOG_VERSION('Invalid header version. Expected <= 1, got ' + version) | |
| 888 | } | |
| 889 | ||
| 890 | if (version === 0) { | |
| 891 | const old = { | |
| 892 | types: types.decode(state), | |
| 893 | userData: keyValueArray.decode(state), | |
| 894 | tree: treeHeader.decode(state), | |
| 895 | signer: keyPair.decode(state), | |
| 896 | hints: hints.decode(state) | |
| 897 | } | |
| 898 | ||
| 899 | return { | |
| 900 | external: null, | |
| 901 | key: old.signer.publicKey, | |
| 902 | manifest: { | |
| 903 | version: 0, | |
| 904 | hash: old.types.tree, | |
| 905 | allowPatch: false, | |
| 906 | quorum: 1, | |
| 907 | signers: [{ | |
| 908 | signature: old.types.signer, | |
| 909 | namespace: DEFAULT_NAMESPACE, | |
| 910 | publicKey: old.signer.publicKey | |
| 911 | }], | |
| 912 | prologue: null | |
| 913 | }, | |
| 914 | keyPair: old.signer.secretKey ? old.signer : null, | |
| 915 | userData: old.userData, | |
| 916 | tree: old.tree, | |
| 917 | hints: old.hints | |
| 918 | } | |
| 919 | } | |
| 920 | ||
| 921 | const flags = c.uint.decode(state) | |
| 922 | ||
| 923 | if (flags & 1) { | |
| 924 | return { | |
| 925 | external: externalHeader.decode(state), | |
| 926 | key: null, | |
| 927 | manifest: null, | |
| 928 | keyPair: null, | |
| 929 | userData: null, | |
| 930 | tree: null, | |
| 931 | hints: null | |
| 932 | } | |
| 933 | } | |
| 934 | ||
| 935 | return { | |
| 936 | external: null, | |
| 937 | key: c.fixed32.decode(state), | |
| 938 | manifest: (flags & 2) !== 0 ? manifest.decode(state) : null, | |
| 939 | keyPair: (flags & 4) !== 0 ? keyPair.decode(state) : null, | |
| 940 | userData: keyValueArray.decode(state), | |
| 941 | tree: treeHeader.decode(state), | |
| 942 | hints: hints.decode(state) | |
| 943 | } | |
| 944 | } | |
| 945 | } | |
| 946 | ||
| 947 | const uintArray = c.array(c.uint) | |
| 948 | ||
| 949 | const multisigInput = { | |
| 950 | preencode (state, inp) { | |
| 951 | c.uint.preencode(state, inp.signer) | |
| 952 | c.fixed64.preencode(state, inp.signature) | |
| 953 | c.uint.preencode(state, inp.patch) | |
| 954 | }, | |
| 955 | encode (state, inp) { | |
| 956 | c.uint.encode(state, inp.signer) | |
| 957 | c.fixed64.encode(state, inp.signature) | |
| 958 | c.uint.encode(state, inp.patch) | |
| 959 | }, | |
| 960 | decode (state) { | |
| 961 | return { | |
| 962 | signer: c.uint.decode(state), | |
| 963 | signature: c.fixed64.decode(state), | |
| 964 | patch: c.uint.decode(state) | |
| 965 | } | |
| 966 | } | |
| 967 | } | |
| 968 | ||
| 969 | const patchEncodingv0 = { | |
| 970 | preencode (state, n) { | |
| 971 | c.uint.preencode(state, n.start) | |
| 972 | c.uint.preencode(state, n.length) | |
| 973 | uintArray.preencode(state, n.nodes) | |
| 974 | }, | |
| 975 | encode (state, n) { | |
| 976 | c.uint.encode(state, n.start) | |
| 977 | c.uint.encode(state, n.length) | |
| 978 | uintArray.encode(state, n.nodes) | |
| 979 | }, | |
| 980 | decode (state) { | |
| 981 | return { | |
| 982 | start: c.uint.decode(state), | |
| 983 | length: c.uint.decode(state), | |
| 984 | nodes: uintArray.decode(state) | |
| 985 | } | |
| 986 | } | |
| 987 | } | |
| 988 | ||
| 989 | const multisigInputv0 = { | |
| 990 | preencode (state, n) { | |
| 991 | state.end++ | |
| 992 | c.uint.preencode(state, n.signer) | |
| 993 | c.fixed64.preencode(state, n.signature) | |
| 994 | if (n.patch) patchEncodingv0.preencode(state, n.patch) | |
| 995 | }, | |
| 996 | encode (state, n) { | |
| 997 | c.uint.encode(state, n.patch ? 1 : 0) | |
| 998 | c.uint.encode(state, n.signer) | |
| 999 | c.fixed64.encode(state, n.signature) | |
| 1000 | if (n.patch) patchEncodingv0.encode(state, n.patch) | |
| 1001 | }, | |
| 1002 | decode (state) { | |
| 1003 | const flags = c.uint.decode(state) | |
| 1004 | return { | |
| 1005 | signer: c.uint.decode(state), | |
| 1006 | signature: c.fixed64.decode(state), | |
| 1007 | patch: (flags & 1) ? patchEncodingv0.decode(state) : null | |
| 1008 | } | |
| 1009 | } | |
| 1010 | } | |
| 1011 | ||
| 1012 | const multisigInputArrayv0 = c.array(multisigInputv0) | |
| 1013 | const multisigInputArray = c.array(multisigInput) | |
| 1014 | ||
| 1015 | const compactNode = { | |
| 1016 | preencode (state, n) { | |
| 1017 | c.uint.preencode(state, n.index) | |
| 1018 | c.uint.preencode(state, n.size) | |
| 1019 | c.fixed32.preencode(state, n.hash) | |
| 1020 | }, | |
| 1021 | encode (state, n) { | |
| 1022 | c.uint.encode(state, n.index) | |
| 1023 | c.uint.encode(state, n.size) | |
| 1024 | c.fixed32.encode(state, n.hash) | |
| 1025 | }, | |
| 1026 | decode (state) { | |
| 1027 | return { | |
| 1028 | index: c.uint.decode(state), | |
| 1029 | size: c.uint.decode(state), | |
| 1030 | hash: c.fixed32.decode(state) | |
| 1031 | } | |
| 1032 | } | |
| 1033 | } | |
| 1034 | ||
| 1035 | const compactNodeArray = c.array(compactNode) | |
| 1036 | ||
| 1037 | exports.multiSignaturev0 = { | |
| 1038 | preencode (state, s) { | |
| 1039 | multisigInputArrayv0.preencode(state, s.proofs) | |
| 1040 | compactNodeArray.preencode(state, s.patch) | |
| 1041 | }, | |
| 1042 | encode (state, s) { | |
| 1043 | multisigInputArrayv0.encode(state, s.proofs) | |
| 1044 | compactNodeArray.encode(state, s.patch) | |
| 1045 | }, | |
| 1046 | decode (state) { | |
| 1047 | return { | |
| 1048 | proofs: multisigInputArrayv0.decode(state), | |
| 1049 | patch: compactNodeArray.decode(state) | |
| 1050 | } | |
| 1051 | } | |
| 1052 | } | |
| 1053 | ||
| 1054 | exports.multiSignature = { | |
| 1055 | preencode (state, s) { | |
| 1056 | multisigInputArray.preencode(state, s.proofs) | |
| 1057 | compactNodeArray.preencode(state, s.patch) | |
| 1058 | }, | |
| 1059 | encode (state, s) { | |
| 1060 | multisigInputArray.encode(state, s.proofs) | |
| 1061 | compactNodeArray.encode(state, s.patch) | |
| 1062 | }, | |
| 1063 | decode (state) { | |
| 1064 | return { | |
| 1065 | proofs: multisigInputArray.decode(state), | |
| 1066 | patch: compactNodeArray.decode(state) | |
| 1067 | } | |
| 1068 | } | |
| 1069 | } |
| … | … | @@ -0,0 +1,124 @@ |
| 1 | const c = require('compact-encoding') | |
| 2 | const b4a = require('b4a') | |
| 3 | const flat = require('flat-tree') | |
| 4 | const { multiSignature, multiSignaturev0 } = require('./messages') | |
| 5 | ||
| 6 | module.exports = { | |
| 7 | assemblev0, | |
| 8 | assemble, | |
| 9 | inflatev0, | |
| 10 | inflate, | |
| 11 | partialSignature, | |
| 12 | signableLength | |
| 13 | } | |
| 14 | ||
| 15 | function inflatev0 (data) { | |
| 16 | return c.decode(multiSignaturev0, data) | |
| 17 | } | |
| 18 | ||
| 19 | function inflate (data) { | |
| 20 | return c.decode(multiSignature, data) | |
| 21 | } | |
| 22 | ||
| 23 | async function partialSignature (tree, signer, from, to = tree.length, signature = tree.signature) { | |
| 24 | if (from > tree.length) return null | |
| 25 | const nodes = to <= from ? null : await upgradeNodes(tree, from, to) | |
| 26 | ||
| 27 | if (signature.byteLength !== 64) signature = c.decode(multiSignature, signature).proofs[0].signature | |
| 28 | ||
| 29 | return { | |
| 30 | signer, | |
| 31 | signature, | |
| 32 | patch: nodes ? to - from : 0, | |
| 33 | nodes | |
| 34 | } | |
| 35 | } | |
| 36 | ||
| 37 | async function upgradeNodes (tree, from, to) { | |
| 38 | const p = await tree.proof({ upgrade: { start: from, length: to - from } }) | |
| 39 | return p.upgrade.nodes | |
| 40 | } | |
| 41 | ||
| 42 | function signableLength (lengths, quorum) { | |
| 43 | if (quorum <= 0) quorum = 1 | |
| 44 | if (quorum > lengths.length) return 0 | |
| 45 | ||
| 46 | return lengths.sort(cmp)[quorum - 1] | |
| 47 | } | |
| 48 | ||
| 49 | function cmp (a, b) { | |
| 50 | return b - a | |
| 51 | } | |
| 52 | ||
| 53 | function assemblev0 (inputs) { | |
| 54 | const proofs = [] | |
| 55 | const patch = [] | |
| 56 | ||
| 57 | for (const u of inputs) { | |
| 58 | proofs.push(compressProof(u, patch)) | |
| 59 | } | |
| 60 | ||
| 61 | return c.encode(multiSignaturev0, { proofs, patch }) | |
| 62 | } | |
| 63 | ||
| 64 | function assemble (inputs) { | |
| 65 | const proofs = [] | |
| 66 | const patch = [] | |
| 67 | const seen = new Set() | |
| 68 | ||
| 69 | for (const u of inputs) { | |
| 70 | if (u.nodes) { | |
| 71 | for (const node of u.nodes) { | |
| 72 | if (seen.has(node.index)) continue | |
| 73 | seen.add(node.index) | |
| 74 | patch.push(node) | |
| 75 | } | |
| 76 | } | |
| 77 | ||
| 78 | proofs.push({ | |
| 79 | signer: u.signer, | |
| 80 | signature: u.signature, | |
| 81 | patch: u.patch | |
| 82 | }) | |
| 83 | } | |
| 84 | ||
| 85 | return c.encode(multiSignature, { proofs, patch }) | |
| 86 | } | |
| 87 | ||
| 88 | function compareNode (a, b) { | |
| 89 | if (a.index !== b.index) return false | |
| 90 | if (a.size !== b.size) return false | |
| 91 | return b4a.equals(a.hash, b.hash) | |
| 92 | } | |
| 93 | ||
| 94 | function compressProof (proof, nodes) { | |
| 95 | return { | |
| 96 | signer: proof.signer, | |
| 97 | signature: proof.signature, | |
| 98 | patch: proof.patch ? compressUpgrade(proof, nodes) : null | |
| 99 | } | |
| 100 | } | |
| 101 | ||
| 102 | function compressUpgrade (p, nodes) { | |
| 103 | const u = { | |
| 104 | start: flat.rightSpan(p.nodes[p.nodes.length - 1].index) / 2 + 1, | |
| 105 | length: p.patch, | |
| 106 | nodes: [] | |
| 107 | } | |
| 108 | ||
| 109 | for (const node of p.nodes) { | |
| 110 | let present = false | |
| 111 | for (let i = 0; i < nodes.length; i++) { | |
| 112 | if (!compareNode(nodes[i], node)) continue | |
| 113 | ||
| 114 | u.nodes.push(i) | |
| 115 | present = true | |
| 116 | break | |
| 117 | } | |
| 118 | ||
| 119 | if (present) continue | |
| 120 | u.nodes.push(nodes.push(node) - 1) | |
| 121 | } | |
| 122 | ||
| 123 | return u | |
| 124 | } |
| … | … | @@ -0,0 +1,39 @@ |
| 1 | module.exports = class Mutex { | |
| 2 | constructor () { | |
| 3 | this.locked = false | |
| 4 | this.destroyed = false | |
| 5 | ||
| 6 | this._destroying = null | |
| 7 | this._destroyError = null | |
| 8 | this._queue = [] | |
| 9 | this._enqueue = (resolve, reject) => this._queue.push([resolve, reject]) | |
| 10 | } | |
| 11 | ||
| 12 | lock () { | |
| 13 | if (this.destroyed) return Promise.reject(this._destroyError || new Error('Mutex has been destroyed')) | |
| 14 | if (this.locked) return new Promise(this._enqueue) | |
| 15 | this.locked = true | |
| 16 | return Promise.resolve() | |
| 17 | } | |
| 18 | ||
| 19 | unlock () { | |
| 20 | if (!this._queue.length) { | |
| 21 | this.locked = false | |
| 22 | return | |
| 23 | } | |
| 24 | this._queue.shift()[0]() | |
| 25 | } | |
| 26 | ||
| 27 | destroy (err) { | |
| 28 | if (!this._destroying) this._destroying = this.locked ? this.lock().catch(() => {}) : Promise.resolve() | |
| 29 | ||
| 30 | this.destroyed = true | |
| 31 | if (err) this._destroyError = err | |
| 32 | ||
| 33 | if (err) { | |
| 34 | while (this._queue.length) this._queue.shift()[1](err) | |
| 35 | } | |
| 36 | ||
| 37 | return this._destroying | |
| 38 | } | |
| 39 | } |
| … | … | @@ -0,0 +1,228 @@ |
| 1 | const cenc = require('compact-encoding') | |
| 2 | const b4a = require('b4a') | |
| 3 | const { crc32 } = require('crc-universal') | |
| 4 | const { OPLOG_CORRUPT, OPLOG_HEADER_OVERFLOW, WRITE_FAILED } = require('hypercore-errors') | |
| 5 | ||
| 6 | module.exports = class Oplog { | |
| 7 | constructor (storage, { pageSize = 4096, headerEncoding = cenc.raw, entryEncoding = cenc.raw, readonly = false } = {}) { | |
| 8 | this.storage = storage | |
| 9 | this.headerEncoding = headerEncoding | |
| 10 | this.entryEncoding = entryEncoding | |
| 11 | this.readonly = readonly | |
| 12 | this.flushed = false | |
| 13 | this.byteLength = 0 | |
| 14 | this.length = 0 | |
| 15 | ||
| 16 | this._headers = [1, 0] | |
| 17 | this._pageSize = pageSize | |
| 18 | this._entryOffset = pageSize * 2 | |
| 19 | } | |
| 20 | ||
| 21 | _addHeader (state, len, headerBit, partialBit) { | |
| 22 | // add the uint header (frame length and flush info) | |
| 23 | state.start = state.start - len - 4 | |
| 24 | cenc.uint32.encode(state, (len << 2) | headerBit | partialBit) | |
| 25 | ||
| 26 | // crc32 the length + header-bit + content and prefix it | |
| 27 | state.start -= 8 | |
| 28 | cenc.uint32.encode(state, crc32(state.buffer.subarray(state.start + 4, state.start + 8 + len))) | |
| 29 | state.start += len + 4 | |
| 30 | } | |
| 31 | ||
| 32 | _decodeEntry (state, enc) { | |
| 33 | if (state.end - state.start < 8) return null | |
| 34 | const cksum = cenc.uint32.decode(state) | |
| 35 | const l = cenc.uint32.decode(state) | |
| 36 | const length = l >>> 2 | |
| 37 | const headerBit = l & 1 | |
| 38 | const partialBit = l & 2 | |
| 39 | ||
| 40 | if (state.end - state.start < length) return null | |
| 41 | ||
| 42 | const end = state.start + length | |
| 43 | ||
| 44 | if (crc32(state.buffer.subarray(state.start - 4, end)) !== cksum) { | |
| 45 | return null | |
| 46 | } | |
| 47 | ||
| 48 | const result = { header: headerBit, partial: partialBit !== 0, byteLength: length + 8, message: null } | |
| 49 | ||
| 50 | try { | |
| 51 | result.message = enc.decode({ start: state.start, end, buffer: state.buffer }) | |
| 52 | } catch { | |
| 53 | return null | |
| 54 | } | |
| 55 | ||
| 56 | state.start = end | |
| 57 | ||
| 58 | return result | |
| 59 | } | |
| 60 | ||
| 61 | async open () { | |
| 62 | const buffer = await this._readAll() // TODO: stream the oplog in on load maybe? | |
| 63 | const state = { start: 0, end: buffer.byteLength, buffer } | |
| 64 | const result = { header: null, entries: [] } | |
| 65 | ||
| 66 | this.byteLength = 0 | |
| 67 | this.length = 0 | |
| 68 | ||
| 69 | const h1 = this._decodeEntry(state, this.headerEncoding) | |
| 70 | state.start = this._pageSize | |
| 71 | ||
| 72 | const h2 = this._decodeEntry(state, this.headerEncoding) | |
| 73 | state.start = this._entryOffset | |
| 74 | ||
| 75 | if (!h1 && !h2) { | |
| 76 | // reset state... | |
| 77 | this.flushed = false | |
| 78 | this._headers[0] = 1 | |
| 79 | this._headers[1] = 0 | |
| 80 | ||
| 81 | if (buffer.byteLength >= this._entryOffset) { | |
| 82 | throw OPLOG_CORRUPT() | |
| 83 | } | |
| 84 | return result | |
| 85 | } | |
| 86 | ||
| 87 | this.flushed = true | |
| 88 | ||
| 89 | if (h1 && !h2) { | |
| 90 | this._headers[0] = h1.header | |
| 91 | this._headers[1] = h1.header | |
| 92 | } else if (!h1 && h2) { | |
| 93 | this._headers[0] = (h2.header + 1) & 1 | |
| 94 | this._headers[1] = h2.header | |
| 95 | } else { | |
| 96 | this._headers[0] = h1.header | |
| 97 | this._headers[1] = h2.header | |
| 98 | } | |
| 99 | ||
| 100 | const header = (this._headers[0] + this._headers[1]) & 1 | |
| 101 | const decoded = [] | |
| 102 | ||
| 103 | result.header = header ? h2.message : h1.message | |
| 104 | ||
| 105 | while (true) { | |
| 106 | const entry = this._decodeEntry(state, this.entryEncoding) | |
| 107 | if (!entry) break | |
| 108 | if (entry.header !== header) break | |
| 109 | ||
| 110 | decoded.push(entry) | |
| 111 | } | |
| 112 | ||
| 113 | while (decoded.length > 0 && decoded[decoded.length - 1].partial) decoded.pop() | |
| 114 | ||
| 115 | for (const e of decoded) { | |
| 116 | result.entries.push(e.message) | |
| 117 | this.byteLength += e.byteLength | |
| 118 | this.length++ | |
| 119 | } | |
| 120 | ||
| 121 | const size = this.byteLength + this._entryOffset | |
| 122 | ||
| 123 | if (size === buffer.byteLength) return result | |
| 124 | ||
| 125 | await new Promise((resolve, reject) => { | |
| 126 | if (this.readonly) return resolve() | |
| 127 | this.storage.truncate(size, err => { | |
| 128 | if (err) return reject(err) | |
| 129 | resolve() | |
| 130 | }) | |
| 131 | }) | |
| 132 | ||
| 133 | return result | |
| 134 | } | |
| 135 | ||
| 136 | _readAll () { | |
| 137 | return new Promise((resolve, reject) => { | |
| 138 | this.storage.open(err => { | |
| 139 | if (err && err.code !== 'ENOENT') return reject(err) | |
| 140 | if (err) return resolve(b4a.alloc(0)) | |
| 141 | this.storage.stat((err, stat) => { | |
| 142 | if (err && err.code !== 'ENOENT') return reject(err) | |
| 143 | this.storage.read(0, stat.size, (err, buf) => { | |
| 144 | if (err) return reject(err) | |
| 145 | resolve(buf) | |
| 146 | }) | |
| 147 | }) | |
| 148 | }) | |
| 149 | }) | |
| 150 | } | |
| 151 | ||
| 152 | flush (header) { | |
| 153 | const state = { start: 8, end: 8, buffer: null } | |
| 154 | const i = this._headers[0] === this._headers[1] ? 1 : 0 | |
| 155 | const bit = (this._headers[i] + 1) & 1 | |
| 156 | ||
| 157 | this.headerEncoding.preencode(state, header) | |
| 158 | if (state.end > this._pageSize) throw OPLOG_HEADER_OVERFLOW() | |
| 159 | state.buffer = b4a.allocUnsafe(state.end) | |
| 160 | this.headerEncoding.encode(state, header) | |
| 161 | this._addHeader(state, state.end - 8, bit, 0) | |
| 162 | ||
| 163 | return this._writeHeaderAndTruncate(i, bit, state.buffer) | |
| 164 | } | |
| 165 | ||
| 166 | _writeHeaderAndTruncate (i, bit, buf) { | |
| 167 | return new Promise((resolve, reject) => { | |
| 168 | this.storage.write(i === 0 ? 0 : this._pageSize, buf, err => { | |
| 169 | if (err) return reject(err) | |
| 170 | ||
| 171 | this.storage.truncate(this._entryOffset, err => { | |
| 172 | if (err) return reject(err) | |
| 173 | ||
| 174 | this._headers[i] = bit | |
| 175 | this.byteLength = 0 | |
| 176 | this.length = 0 | |
| 177 | this.flushed = true | |
| 178 | ||
| 179 | resolve() | |
| 180 | }) | |
| 181 | }) | |
| 182 | }) | |
| 183 | } | |
| 184 | ||
| 185 | append (batch, atomic = true) { | |
| 186 | if (!Array.isArray(batch)) batch = [batch] | |
| 187 | ||
| 188 | const state = { start: 0, end: batch.length * 8, buffer: null } | |
| 189 | const bit = (this._headers[0] + this._headers[1]) & 1 | |
| 190 | ||
| 191 | for (let i = 0; i < batch.length; i++) { | |
| 192 | this.entryEncoding.preencode(state, batch[i]) | |
| 193 | } | |
| 194 | ||
| 195 | state.buffer = b4a.allocUnsafe(state.end) | |
| 196 | ||
| 197 | for (let i = 0; i < batch.length; i++) { | |
| 198 | const start = state.start += 8 // space for header | |
| 199 | const partial = (atomic && i < batch.length - 1) ? 2 : 0 | |
| 200 | this.entryEncoding.encode(state, batch[i]) | |
| 201 | this._addHeader(state, state.start - start, bit, partial) | |
| 202 | } | |
| 203 | ||
| 204 | return this._append(state.buffer, batch.length) | |
| 205 | } | |
| 206 | ||
| 207 | close () { | |
| 208 | return new Promise((resolve, reject) => { | |
| 209 | this.storage.close(err => { | |
| 210 | if (err) return reject(err) | |
| 211 | resolve() | |
| 212 | }) | |
| 213 | }) | |
| 214 | } | |
| 215 | ||
| 216 | _append (buf, count) { | |
| 217 | return new Promise((resolve, reject) => { | |
| 218 | this.storage.write(this._entryOffset + this.byteLength, buf, err => { | |
| 219 | if (err) return reject(WRITE_FAILED(err.message)) | |
| 220 | ||
| 221 | this.byteLength += buf.byteLength | |
| 222 | this.length += count | |
| 223 | ||
| 224 | resolve() | |
| 225 | }) | |
| 226 | }) | |
| 227 | } | |
| 228 | } |
| … | … | @@ -0,0 +1,71 @@ |
| 1 | const FIFO = require('fast-fifo') | |
| 2 | ||
| 3 | module.exports = class ReceiverQueue { | |
| 4 | constructor () { | |
| 5 | this.queue = new FIFO() | |
| 6 | this.priority = [] | |
| 7 | this.requests = new Map() | |
| 8 | this.length = 0 | |
| 9 | } | |
| 10 | ||
| 11 | push (req) { | |
| 12 | // TODO: use a heap at some point if we wanna support multiple prios | |
| 13 | if (req.priority > 0) this.priority.push(req) | |
| 14 | else this.queue.push(req) | |
| 15 | ||
| 16 | this.requests.set(req.id, req) | |
| 17 | this.length++ | |
| 18 | } | |
| 19 | ||
| 20 | shift () { | |
| 21 | while (this.priority.length > 0) { | |
| 22 | const msg = this.priority.pop() | |
| 23 | const req = this._processRequest(msg) | |
| 24 | if (req !== null) return req | |
| 25 | } | |
| 26 | ||
| 27 | while (this.queue.length > 0) { | |
| 28 | const msg = this.queue.shift() | |
| 29 | const req = this._processRequest(msg) | |
| 30 | if (req !== null) return req | |
| 31 | } | |
| 32 | ||
| 33 | return null | |
| 34 | } | |
| 35 | ||
| 36 | _processRequest (req) { | |
| 37 | if (req.block || req.hash || req.seek || req.upgrade || req.manifest) { | |
| 38 | this.requests.delete(req.id) | |
| 39 | this.length-- | |
| 40 | return req | |
| 41 | } | |
| 42 | ||
| 43 | return null | |
| 44 | } | |
| 45 | ||
| 46 | clear () { | |
| 47 | this.queue.clear() | |
| 48 | this.priority = [] | |
| 49 | this.length = 0 | |
| 50 | this.requests.clear() | |
| 51 | } | |
| 52 | ||
| 53 | delete (id) { | |
| 54 | const req = this.requests.get(id) | |
| 55 | if (!req) return | |
| 56 | ||
| 57 | req.block = null | |
| 58 | req.hash = null | |
| 59 | req.seek = null | |
| 60 | req.upgrade = null | |
| 61 | req.manifest = false | |
| 62 | ||
| 63 | this.requests.delete(id) | |
| 64 | this.length-- | |
| 65 | ||
| 66 | if (this.length === 0) { | |
| 67 | this.queue.clear() | |
| 68 | this.priority = [] | |
| 69 | } | |
| 70 | } | |
| 71 | } |
| … | … | @@ -0,0 +1,346 @@ |
| 1 | const BigSparseArray = require('big-sparse-array') | |
| 2 | const quickbit = require('./compat').quickbit | |
| 3 | ||
| 4 | const BITS_PER_PAGE = 32768 | |
| 5 | const BYTES_PER_PAGE = BITS_PER_PAGE / 8 | |
| 6 | const WORDS_PER_PAGE = BYTES_PER_PAGE / 4 | |
| 7 | const BITS_PER_SEGMENT = 2097152 | |
| 8 | const BYTES_PER_SEGMENT = BITS_PER_SEGMENT / 8 | |
| 9 | const PAGES_PER_SEGMENT = BITS_PER_SEGMENT / BITS_PER_PAGE | |
| 10 | ||
| 11 | class RemoteBitfieldPage { | |
| 12 | constructor (index, bitfield, segment) { | |
| 13 | this.index = index | |
| 14 | this.offset = index * BYTES_PER_PAGE - segment.offset | |
| 15 | this.bitfield = bitfield | |
| 16 | this.segment = segment | |
| 17 | ||
| 18 | segment.add(this) | |
| 19 | } | |
| 20 | ||
| 21 | get tree () { | |
| 22 | return this.segment.tree | |
| 23 | } | |
| 24 | ||
| 25 | get (index) { | |
| 26 | return quickbit.get(this.bitfield, index) | |
| 27 | } | |
| 28 | ||
| 29 | set (index, val) { | |
| 30 | if (quickbit.set(this.bitfield, index, val)) { | |
| 31 | this.tree.update(this.offset * 8 + index) | |
| 32 | } | |
| 33 | } | |
| 34 | ||
| 35 | setRange (start, length, val) { | |
| 36 | quickbit.fill(this.bitfield, val, start, start + length) | |
| 37 | ||
| 38 | let i = Math.floor(start / 128) | |
| 39 | const n = i + Math.ceil(length / 128) | |
| 40 | ||
| 41 | while (i <= n) this.tree.update(this.offset * 8 + i++ * 128) | |
| 42 | } | |
| 43 | ||
| 44 | findFirst (val, position) { | |
| 45 | return quickbit.findFirst(this.bitfield, val, position) | |
| 46 | } | |
| 47 | ||
| 48 | findLast (val, position) { | |
| 49 | return quickbit.findLast(this.bitfield, val, position) | |
| 50 | } | |
| 51 | ||
| 52 | insert (start, bitfield) { | |
| 53 | this.bitfield.set(bitfield, start / 32) | |
| 54 | this.segment.refresh() | |
| 55 | } | |
| 56 | ||
| 57 | clear (start, bitfield) { | |
| 58 | quickbit.clear(this.bitfield, { field: bitfield, offset: start }) | |
| 59 | } | |
| 60 | } | |
| 61 | ||
| 62 | class RemoteBitfieldSegment { | |
| 63 | constructor (index) { | |
| 64 | this.index = index | |
| 65 | this.offset = index * BYTES_PER_SEGMENT | |
| 66 | this.tree = quickbit.Index.from([], BYTES_PER_SEGMENT) | |
| 67 | this.pages = new Array(PAGES_PER_SEGMENT) | |
| 68 | this.pagesLength = 0 | |
| 69 | } | |
| 70 | ||
| 71 | get chunks () { | |
| 72 | return this.tree.chunks | |
| 73 | } | |
| 74 | ||
| 75 | refresh () { | |
| 76 | this.tree = quickbit.Index.from(this.tree.chunks, BYTES_PER_SEGMENT) | |
| 77 | } | |
| 78 | ||
| 79 | add (page) { | |
| 80 | const pageIndex = page.index - this.index * PAGES_PER_SEGMENT | |
| 81 | if (pageIndex >= this.pagesLength) this.pagesLength = pageIndex + 1 | |
| 82 | ||
| 83 | this.pages[pageIndex] = page | |
| 84 | ||
| 85 | const chunk = { field: page.bitfield, offset: page.offset } | |
| 86 | ||
| 87 | this.chunks.push(chunk) | |
| 88 | ||
| 89 | for (let i = this.chunks.length - 2; i >= 0; i--) { | |
| 90 | const prev = this.chunks[i] | |
| 91 | if (prev.offset <= chunk.offset) break | |
| 92 | this.chunks[i] = chunk | |
| 93 | this.chunks[i + 1] = prev | |
| 94 | } | |
| 95 | } | |
| 96 | ||
| 97 | findFirst (val, position) { | |
| 98 | position = this.tree.skipFirst(!val, position) | |
| 99 | ||
| 100 | let j = position & (BITS_PER_PAGE - 1) | |
| 101 | let i = (position - j) / BITS_PER_PAGE | |
| 102 | ||
| 103 | if (i >= PAGES_PER_SEGMENT) return -1 | |
| 104 | ||
| 105 | while (i < this.pagesLength) { | |
| 106 | const p = this.pages[i] | |
| 107 | ||
| 108 | let index = -1 | |
| 109 | ||
| 110 | if (p) index = p.findFirst(val, j) | |
| 111 | else if (!val) index = j | |
| 112 | ||
| 113 | if (index !== -1) return i * BITS_PER_PAGE + index | |
| 114 | ||
| 115 | j = 0 | |
| 116 | i++ | |
| 117 | } | |
| 118 | ||
| 119 | return (val || this.pagesLength === PAGES_PER_SEGMENT) ? -1 : this.pagesLength * BITS_PER_PAGE | |
| 120 | } | |
| 121 | ||
| 122 | findLast (val, position) { | |
| 123 | position = this.tree.skipLast(!val, position) | |
| 124 | ||
| 125 | let j = position & (BITS_PER_PAGE - 1) | |
| 126 | let i = (position - j) / BITS_PER_PAGE | |
| 127 | ||
| 128 | if (i >= PAGES_PER_SEGMENT) return -1 | |
| 129 | ||
| 130 | while (i >= 0) { | |
| 131 | const p = this.pages[i] | |
| 132 | ||
| 133 | let index = -1 | |
| 134 | ||
| 135 | if (p) index = p.findLast(val, j) | |
| 136 | else if (!val) index = j | |
| 137 | ||
| 138 | if (index !== -1) return i * BITS_PER_PAGE + index | |
| 139 | ||
| 140 | j = BITS_PER_PAGE - 1 | |
| 141 | i-- | |
| 142 | } | |
| 143 | ||
| 144 | return -1 | |
| 145 | } | |
| 146 | } | |
| 147 | ||
| 148 | module.exports = class RemoteBitfield { | |
| 149 | static BITS_PER_PAGE = BITS_PER_PAGE | |
| 150 | ||
| 151 | constructor () { | |
| 152 | this._pages = new BigSparseArray() | |
| 153 | this._segments = new BigSparseArray() | |
| 154 | this._maxSegments = 0 | |
| 155 | } | |
| 156 | ||
| 157 | getBitfield (index) { | |
| 158 | const j = index & (BITS_PER_PAGE - 1) | |
| 159 | const i = (index - j) / BITS_PER_PAGE | |
| 160 | ||
| 161 | const p = this._pages.get(i) | |
| 162 | return p || null | |
| 163 | } | |
| 164 | ||
| 165 | get (index) { | |
| 166 | const j = index & (BITS_PER_PAGE - 1) | |
| 167 | const i = (index - j) / BITS_PER_PAGE | |
| 168 | ||
| 169 | const p = this._pages.get(i) | |
| 170 | ||
| 171 | return p ? p.get(j) : false | |
| 172 | } | |
| 173 | ||
| 174 | set (index, val) { | |
| 175 | const j = index & (BITS_PER_PAGE - 1) | |
| 176 | const i = (index - j) / BITS_PER_PAGE | |
| 177 | ||
| 178 | let p = this._pages.get(i) | |
| 179 | ||
| 180 | if (!p && val) { | |
| 181 | const k = Math.floor(i / PAGES_PER_SEGMENT) | |
| 182 | const s = this._segments.get(k) || this._segments.set(k, new RemoteBitfieldSegment(k)) | |
| 183 | if (this._maxSegments <= k) this._maxSegments = k + 1 | |
| 184 | ||
| 185 | p = this._pages.set(i, new RemoteBitfieldPage(i, new Uint32Array(WORDS_PER_PAGE), s)) | |
| 186 | } | |
| 187 | ||
| 188 | if (p) p.set(j, val) | |
| 189 | } | |
| 190 | ||
| 191 | setRange (start, length, val) { | |
| 192 | let j = start & (BITS_PER_PAGE - 1) | |
| 193 | let i = (start - j) / BITS_PER_PAGE | |
| 194 | ||
| 195 | while (length > 0) { | |
| 196 | let p = this._pages.get(i) | |
| 197 | ||
| 198 | if (!p && val) { | |
| 199 | const k = Math.floor(i / PAGES_PER_SEGMENT) | |
| 200 | const s = this._segments.get(k) || this._segments.set(k, new RemoteBitfieldSegment(k)) | |
| 201 | if (this._maxSegments <= k) this._maxSegments = k + 1 | |
| 202 | ||
| 203 | p = this._pages.set(i, new RemoteBitfieldPage(i, new Uint32Array(WORDS_PER_PAGE), s)) | |
| 204 | } | |
| 205 | ||
| 206 | const end = Math.min(j + length, BITS_PER_PAGE) | |
| 207 | const range = end - j | |
| 208 | ||
| 209 | if (p) p.setRange(j, range, val) | |
| 210 | ||
| 211 | j = 0 | |
| 212 | i++ | |
| 213 | length -= range | |
| 214 | } | |
| 215 | } | |
| 216 | ||
| 217 | findFirst (val, position) { | |
| 218 | let j = position & (BITS_PER_SEGMENT - 1) | |
| 219 | let i = (position - j) / BITS_PER_SEGMENT | |
| 220 | ||
| 221 | while (i < this._maxSegments) { | |
| 222 | const s = this._segments.get(i) | |
| 223 | ||
| 224 | let index = -1 | |
| 225 | ||
| 226 | if (s) index = s.findFirst(val, j) | |
| 227 | else if (!val) index = j | |
| 228 | ||
| 229 | if (index !== -1) return i * BITS_PER_SEGMENT + index | |
| 230 | ||
| 231 | j = 0 | |
| 232 | i++ | |
| 233 | } | |
| 234 | ||
| 235 | // For the val === false case, we always return at least | |
| 236 | // the 'position', also if nothing was found | |
| 237 | return val | |
| 238 | ? -1 | |
| 239 | : Math.max(position, this._maxSegments * BITS_PER_SEGMENT) | |
| 240 | } | |
| 241 | ||
| 242 | firstSet (position) { | |
| 243 | return this.findFirst(true, position) | |
| 244 | } | |
| 245 | ||
| 246 | firstUnset (position) { | |
| 247 | return this.findFirst(false, position) | |
| 248 | } | |
| 249 | ||
| 250 | findLast (val, position) { | |
| 251 | let j = position & (BITS_PER_SEGMENT - 1) | |
| 252 | let i = (position - j) / BITS_PER_SEGMENT | |
| 253 | ||
| 254 | while (i >= 0) { | |
| 255 | const s = this._segments.get(i) | |
| 256 | ||
| 257 | let index = -1 | |
| 258 | ||
| 259 | if (s) index = s.findLast(val, j) | |
| 260 | else if (!val) index = j | |
| 261 | ||
| 262 | if (index !== -1) return i * BITS_PER_SEGMENT + index | |
| 263 | ||
| 264 | j = BITS_PER_SEGMENT - 1 | |
| 265 | i-- | |
| 266 | } | |
| 267 | ||
| 268 | return -1 | |
| 269 | } | |
| 270 | ||
| 271 | lastSet (position) { | |
| 272 | return this.findLast(true, position) | |
| 273 | } | |
| 274 | ||
| 275 | lastUnset (position) { | |
| 276 | return this.findLast(false, position) | |
| 277 | } | |
| 278 | ||
| 279 | insert (start, bitfield) { | |
| 280 | if (start % 32 !== 0) return false | |
| 281 | ||
| 282 | let length = bitfield.byteLength * 8 | |
| 283 | ||
| 284 | let j = start & (BITS_PER_PAGE - 1) | |
| 285 | let i = (start - j) / BITS_PER_PAGE | |
| 286 | ||
| 287 | while (length > 0) { | |
| 288 | let p = this._pages.get(i) | |
| 289 | ||
| 290 | if (!p) { | |
| 291 | const k = Math.floor(i / PAGES_PER_SEGMENT) | |
| 292 | const s = this._segments.get(k) || this._segments.set(k, new RemoteBitfieldSegment(k)) | |
| 293 | if (this._maxSegments <= k) this._maxSegments = k + 1 | |
| 294 | ||
| 295 | p = this._pages.set(i, new RemoteBitfieldPage(i, new Uint32Array(WORDS_PER_PAGE), s)) | |
| 296 | } | |
| 297 | ||
| 298 | const end = Math.min(j + length, BITS_PER_PAGE) | |
| 299 | const range = end - j | |
| 300 | ||
| 301 | p.insert(j, bitfield.subarray(0, range / 32)) | |
| 302 | ||
| 303 | bitfield = bitfield.subarray(range / 32) | |
| 304 | ||
| 305 | j = 0 | |
| 306 | i++ | |
| 307 | length -= range | |
| 308 | } | |
| 309 | ||
| 310 | return true | |
| 311 | } | |
| 312 | ||
| 313 | clear (start, bitfield) { | |
| 314 | if (start % 32 !== 0) return false | |
| 315 | ||
| 316 | let length = bitfield.byteLength * 8 | |
| 317 | ||
| 318 | let j = start & (BITS_PER_PAGE - 1) | |
| 319 | let i = (start - j) / BITS_PER_PAGE | |
| 320 | ||
| 321 | while (length > 0) { | |
| 322 | let p = this._pages.get(i) | |
| 323 | ||
| 324 | if (!p) { | |
| 325 | const k = Math.floor(i / PAGES_PER_SEGMENT) | |
| 326 | const s = this._segments.get(k) || this._segments.set(k, new RemoteBitfieldSegment(k)) | |
| 327 | if (this._maxSegments <= k) this._maxSegments = k + 1 | |
| 328 | ||
| 329 | p = this._pages.set(i, new RemoteBitfieldPage(i, new Uint32Array(WORDS_PER_PAGE), s)) | |
| 330 | } | |
| 331 | ||
| 332 | const end = Math.min(j + length, BITS_PER_PAGE) | |
| 333 | const range = end - j | |
| 334 | ||
| 335 | p.clear(j, bitfield.subarray(0, range / 32)) | |
| 336 | ||
| 337 | bitfield = bitfield.subarray(range / 32) | |
| 338 | ||
| 339 | j = 0 | |
| 340 | i++ | |
| 341 | length -= range | |
| 342 | } | |
| 343 | ||
| 344 | return true | |
| 345 | } | |
| 346 | } |
| … | … | @@ -0,0 +1,2587 @@ |
| 1 | /* DEV DOCS | |
| 2 | Every hypercore has one Replicator object managing its connections to other peers. | |
| 3 | There is one Peer object per peer connected to the Hypercore. | |
| 4 | Hypercores do not know about other hypercores, so when a peer is connected to multiple cores, there exists one Peer object per core. | |
| 5 | ||
| 6 | Hypercore indicates block should be downloaded through methods like Replicator.addRange or Replicator.addBlock | |
| 7 | Hypercore calls Replicator.updateActivity every time a hypercore session opens/closes | |
| 8 | Replicator.updateActivity ensures the Hypercore is downloading blocks as expected | |
| 9 | Replicator keeps track of: | |
| 10 | - Which blocks need to be downloaded (Replicator._blocks) | |
| 11 | - Which blocks currently have inflight requests (Replicator._inflight) | |
| 12 | ||
| 13 | Blocks are requested from remote peers by Peer objects. The flow is: | |
| 14 | - The replicator's updatePeer method gets called | |
| 15 | - The replicator detects whether the Peer can accept more requests (for example by checking if it's maxed out on inflight blocks) | |
| 16 | - The replicator then tells the Peer what to request (e.g. Peer_requestRange or Peer._requestBlock methods) | |
| 17 | ||
| 18 | The Peer object is responsible for tracking | |
| 19 | - Which blocks does the Peer have available (tracked in remoteBitfield) | |
| 20 | - Which blocks are you actively looking for from this peer (tracked in missingBlocks) | |
| 21 | - How many blocks are currently inflight (tracked in inflight) | |
| 22 | The Peer uses this information to decide which blocks to request form the peer in response to _requestRange requests and the like. | |
| 23 | */ | |
| 24 | ||
| 25 | const b4a = require('b4a') | |
| 26 | const safetyCatch = require('safety-catch') | |
| 27 | const RandomIterator = require('random-array-iterator') | |
| 28 | const flatTree = require('flat-tree') | |
| 29 | const ReceiverQueue = require('./receiver-queue') | |
| 30 | const HotswapQueue = require('./hotswap-queue') | |
| 31 | const RemoteBitfield = require('./remote-bitfield') | |
| 32 | const { REQUEST_CANCELLED, REQUEST_TIMEOUT, INVALID_CAPABILITY, SNAPSHOT_NOT_AVAILABLE } = require('hypercore-errors') | |
| 33 | const m = require('./messages') | |
| 34 | const caps = require('./caps') | |
| 35 | ||
| 36 | const DEFAULT_MAX_INFLIGHT = [16, 512] | |
| 37 | const SCALE_LATENCY = 50 | |
| 38 | const DEFAULT_SEGMENT_SIZE = 256 * 1024 * 8 // 256 KiB in bits | |
| 39 | const NOT_DOWNLOADING_SLACK = 20000 + (Math.random() * 20000) | 0 | |
| 40 | const MAX_PEERS_UPGRADE = 3 | |
| 41 | ||
| 42 | const MAX_RANGES = 64 | |
| 43 | ||
| 44 | const PRIORITY = { | |
| 45 | NORMAL: 0, | |
| 46 | HIGH: 1, | |
| 47 | VERY_HIGH: 2, | |
| 48 | CANCELLED: 255 // reserved to mark cancellation | |
| 49 | } | |
| 50 | ||
| 51 | class Attachable { | |
| 52 | constructor () { | |
| 53 | this.resolved = false | |
| 54 | this.refs = [] | |
| 55 | } | |
| 56 | ||
| 57 | attach (session) { | |
| 58 | const r = { | |
| 59 | context: this, | |
| 60 | session, | |
| 61 | sindex: 0, | |
| 62 | rindex: 0, | |
| 63 | snapshot: true, | |
| 64 | resolve: null, | |
| 65 | reject: null, | |
| 66 | promise: null, | |
| 67 | timeout: null | |
| 68 | } | |
| 69 | ||
| 70 | r.sindex = session.push(r) - 1 | |
| 71 | r.rindex = this.refs.push(r) - 1 | |
| 72 | r.promise = new Promise((resolve, reject) => { | |
| 73 | r.resolve = resolve | |
| 74 | r.reject = reject | |
| 75 | }) | |
| 76 | ||
| 77 | return r | |
| 78 | } | |
| 79 | ||
| 80 | detach (r, err = null) { | |
| 81 | if (r.context !== this) return false | |
| 82 | ||
| 83 | this._detach(r) | |
| 84 | this._cancel(r, err) | |
| 85 | this.gc() | |
| 86 | ||
| 87 | return true | |
| 88 | } | |
| 89 | ||
| 90 | _detach (r) { | |
| 91 | const rh = this.refs.pop() | |
| 92 | const sh = r.session.pop() | |
| 93 | ||
| 94 | if (r.rindex < this.refs.length) this.refs[rh.rindex = r.rindex] = rh | |
| 95 | if (r.sindex < r.session.length) r.session[sh.sindex = r.sindex] = sh | |
| 96 | ||
| 97 | destroyRequestTimeout(r) | |
| 98 | r.context = null | |
| 99 | ||
| 100 | return r | |
| 101 | } | |
| 102 | ||
| 103 | gc () { | |
| 104 | if (this.refs.length === 0) this._unref() | |
| 105 | } | |
| 106 | ||
| 107 | _cancel (r, err) { | |
| 108 | r.reject(err || REQUEST_CANCELLED()) | |
| 109 | } | |
| 110 | ||
| 111 | _unref () { | |
| 112 | // overwrite me | |
| 113 | } | |
| 114 | ||
| 115 | resolve (val) { | |
| 116 | this.resolved = true | |
| 117 | while (this.refs.length > 0) { | |
| 118 | this._detach(this.refs[this.refs.length - 1]).resolve(val) | |
| 119 | } | |
| 120 | } | |
| 121 | ||
| 122 | reject (err) { | |
| 123 | this.resolved = true | |
| 124 | while (this.refs.length > 0) { | |
| 125 | this._detach(this.refs[this.refs.length - 1]).reject(err) | |
| 126 | } | |
| 127 | } | |
| 128 | ||
| 129 | setTimeout (r, ms) { | |
| 130 | destroyRequestTimeout(r) | |
| 131 | r.timeout = setTimeout(onrequesttimeout, ms, r) | |
| 132 | } | |
| 133 | } | |
| 134 | ||
| 135 | class BlockRequest extends Attachable { | |
| 136 | constructor (tracker, index, priority) { | |
| 137 | super() | |
| 138 | ||
| 139 | this.index = index | |
| 140 | this.priority = priority | |
| 141 | this.inflight = [] | |
| 142 | this.queued = false | |
| 143 | this.hotswap = null | |
| 144 | this.tracker = tracker | |
| 145 | } | |
| 146 | ||
| 147 | _unref () { | |
| 148 | this.queued = false | |
| 149 | ||
| 150 | for (const req of this.inflight) { | |
| 151 | req.peer._cancelRequest(req) | |
| 152 | } | |
| 153 | ||
| 154 | this.tracker.remove(this.index) | |
| 155 | removeHotswap(this) | |
| 156 | } | |
| 157 | } | |
| 158 | ||
| 159 | class RangeRequest extends Attachable { | |
| 160 | constructor (ranges, start, end, linear, ifAvailable, blocks) { | |
| 161 | super() | |
| 162 | ||
| 163 | this.start = start | |
| 164 | this.end = end | |
| 165 | this.linear = linear | |
| 166 | this.ifAvailable = ifAvailable | |
| 167 | this.blocks = blocks | |
| 168 | this.ranges = ranges | |
| 169 | ||
| 170 | // As passed by the user, immut | |
| 171 | this.userStart = start | |
| 172 | this.userEnd = end | |
| 173 | } | |
| 174 | ||
| 175 | _unref () { | |
| 176 | const i = this.ranges.indexOf(this) | |
| 177 | if (i === -1) return | |
| 178 | const h = this.ranges.pop() | |
| 179 | if (i < this.ranges.length) this.ranges[i] = h | |
| 180 | } | |
| 181 | ||
| 182 | _cancel (r) { | |
| 183 | r.resolve(false) | |
| 184 | } | |
| 185 | } | |
| 186 | ||
| 187 | class UpgradeRequest extends Attachable { | |
| 188 | constructor (replicator, fork, length) { | |
| 189 | super() | |
| 190 | ||
| 191 | this.fork = fork | |
| 192 | this.length = length | |
| 193 | this.inflight = [] | |
| 194 | this.replicator = replicator | |
| 195 | } | |
| 196 | ||
| 197 | _unref () { | |
| 198 | if (this.replicator.eagerUpgrade === true || this.inflight.length > 0) return | |
| 199 | this.replicator._upgrade = null | |
| 200 | } | |
| 201 | ||
| 202 | _cancel (r) { | |
| 203 | r.resolve(false) | |
| 204 | } | |
| 205 | } | |
| 206 | ||
| 207 | class SeekRequest extends Attachable { | |
| 208 | constructor (seeks, seeker) { | |
| 209 | super() | |
| 210 | ||
| 211 | this.seeker = seeker | |
| 212 | this.inflight = [] | |
| 213 | this.seeks = seeks | |
| 214 | } | |
| 215 | ||
| 216 | _unref () { | |
| 217 | if (this.inflight.length > 0) return | |
| 218 | const i = this.seeks.indexOf(this) | |
| 219 | if (i === -1) return | |
| 220 | const h = this.seeks.pop() | |
| 221 | if (i < this.seeks.length) this.seeks[i] = h | |
| 222 | } | |
| 223 | } | |
| 224 | ||
| 225 | class InflightTracker { | |
| 226 | constructor () { | |
| 227 | this._requests = [] | |
| 228 | this._free = [] | |
| 229 | } | |
| 230 | ||
| 231 | get idle () { | |
| 232 | return this._requests.length === this._free.length | |
| 233 | } | |
| 234 | ||
| 235 | * [Symbol.iterator] () { | |
| 236 | for (const req of this._requests) { | |
| 237 | if (req !== null) yield req | |
| 238 | } | |
| 239 | } | |
| 240 | ||
| 241 | add (req) { | |
| 242 | const id = this._free.length ? this._free.pop() : this._requests.push(null) | |
| 243 | req.id = id | |
| 244 | this._requests[id - 1] = req | |
| 245 | return req | |
| 246 | } | |
| 247 | ||
| 248 | get (id) { | |
| 249 | return id <= this._requests.length ? this._requests[id - 1] : null | |
| 250 | } | |
| 251 | ||
| 252 | remove (id, roundtrip) { | |
| 253 | if (id > this._requests.length) return | |
| 254 | this._requests[id - 1] = null | |
| 255 | if (roundtrip === true) this._free.push(id) | |
| 256 | } | |
| 257 | ||
| 258 | reusable (id) { | |
| 259 | this._free.push(id) | |
| 260 | } | |
| 261 | } | |
| 262 | ||
| 263 | class BlockTracker { | |
| 264 | constructor () { | |
| 265 | this._map = new Map() | |
| 266 | } | |
| 267 | ||
| 268 | [Symbol.iterator] () { | |
| 269 | return this._map.values() | |
| 270 | } | |
| 271 | ||
| 272 | isEmpty () { | |
| 273 | return this._map.size === 0 | |
| 274 | } | |
| 275 | ||
| 276 | has (index) { | |
| 277 | return this._map.has(index) | |
| 278 | } | |
| 279 | ||
| 280 | get (index) { | |
| 281 | return this._map.get(index) || null | |
| 282 | } | |
| 283 | ||
| 284 | add (index, priority) { | |
| 285 | let b = this._map.get(index) | |
| 286 | if (b) return b | |
| 287 | ||
| 288 | b = new BlockRequest(this, index, priority) | |
| 289 | this._map.set(index, b) | |
| 290 | ||
| 291 | return b | |
| 292 | } | |
| 293 | ||
| 294 | remove (index) { | |
| 295 | const b = this.get(index) | |
| 296 | this._map.delete(index) | |
| 297 | return b | |
| 298 | } | |
| 299 | } | |
| 300 | ||
| 301 | class RoundtripQueue { | |
| 302 | constructor () { | |
| 303 | this.queue = [] | |
| 304 | this.tick = 0 | |
| 305 | } | |
| 306 | ||
| 307 | clear () { | |
| 308 | const ids = new Array(this.queue.length) | |
| 309 | for (let i = 0; i < ids.length; i++) { | |
| 310 | ids[i] = this.queue[i][1] | |
| 311 | } | |
| 312 | ||
| 313 | this.queue = [] | |
| 314 | ||
| 315 | return ids | |
| 316 | } | |
| 317 | ||
| 318 | add (id) { | |
| 319 | this.queue.push([++this.tick, id]) | |
| 320 | } | |
| 321 | ||
| 322 | flush (tick) { | |
| 323 | let flushed = null | |
| 324 | ||
| 325 | for (let i = 0; i < this.queue.length; i++) { | |
| 326 | if (this.queue[i][0] > tick) break | |
| 327 | if (flushed === null) flushed = [] | |
| 328 | flushed.push(this.queue[i][1]) | |
| 329 | } | |
| 330 | ||
| 331 | if (flushed !== null) this.queue.splice(0, flushed.length) | |
| 332 | return flushed | |
| 333 | } | |
| 334 | } | |
| 335 | ||
| 336 | class Peer { | |
| 337 | constructor (replicator, protomux, channel, useSession, inflightRange) { | |
| 338 | this.core = replicator.core | |
| 339 | this.replicator = replicator | |
| 340 | this.stream = protomux.stream | |
| 341 | this.protomux = protomux | |
| 342 | this.remotePublicKey = this.stream.remotePublicKey | |
| 343 | this.remoteSupportsSeeks = false | |
| 344 | this.inflightRange = inflightRange | |
| 345 | ||
| 346 | this.paused = false | |
| 347 | this.removed = false | |
| 348 | ||
| 349 | this.useSession = useSession | |
| 350 | ||
| 351 | this.channel = channel | |
| 352 | this.channel.userData = this | |
| 353 | ||
| 354 | this.wireSync = this.channel.messages[0] | |
| 355 | this.wireRequest = this.channel.messages[1] | |
| 356 | this.wireCancel = this.channel.messages[2] | |
| 357 | this.wireData = this.channel.messages[3] | |
| 358 | this.wireNoData = this.channel.messages[4] | |
| 359 | this.wireWant = this.channel.messages[5] | |
| 360 | this.wireUnwant = this.channel.messages[6] | |
| 361 | this.wireBitfield = this.channel.messages[7] | |
| 362 | this.wireRange = this.channel.messages[8] | |
| 363 | this.wireExtension = this.channel.messages[9] | |
| 364 | ||
| 365 | // Same stats as replicator, but for this specific peer | |
| 366 | this.stats = { | |
| 367 | wireSync: { tx: 0, rx: 0 }, | |
| 368 | wireRequest: { tx: 0, rx: 0 }, | |
| 369 | wireCancel: { tx: 0, rx: 0 }, | |
| 370 | wireData: { tx: 0, rx: 0 }, | |
| 371 | wireWant: { tx: 0, rx: 0 }, | |
| 372 | wireBitfield: { tx: 0, rx: 0 }, | |
| 373 | wireRange: { tx: 0, rx: 0 }, | |
| 374 | wireExtension: { tx: 0, rx: 0 }, | |
| 375 | hotswaps: 0 | |
| 376 | } | |
| 377 | ||
| 378 | this.receiverQueue = new ReceiverQueue() | |
| 379 | this.receiverBusy = false | |
| 380 | ||
| 381 | // most often not used, so made on demand | |
| 382 | this.roundtripQueue = null | |
| 383 | ||
| 384 | this.inflight = 0 | |
| 385 | this.dataProcessing = 0 | |
| 386 | ||
| 387 | this.canUpgrade = true | |
| 388 | ||
| 389 | this.needsSync = false | |
| 390 | this.syncsProcessing = 0 | |
| 391 | ||
| 392 | this._remoteContiguousLength = 0 | |
| 393 | ||
| 394 | // TODO: tweak pipelining so that data sent BEFORE remoteOpened is not cap verified! | |
| 395 | // we might wanna tweak that with some crypto, ie use the cap to encrypt it... | |
| 396 | // or just be aware of that, to only push non leaky data | |
| 397 | ||
| 398 | this.remoteOpened = false | |
| 399 | this.remoteBitfield = new RemoteBitfield() | |
| 400 | this.missingBlocks = new RemoteBitfield() | |
| 401 | ||
| 402 | this.remoteFork = 0 | |
| 403 | this.remoteLength = 0 | |
| 404 | this.remoteCanUpgrade = false | |
| 405 | this.remoteUploading = true | |
| 406 | this.remoteDownloading = true | |
| 407 | this.remoteSynced = false | |
| 408 | this.remoteHasManifest = false | |
| 409 | this.remoteRequests = new Map() | |
| 410 | ||
| 411 | this.segmentsWanted = new Set() | |
| 412 | this.broadcastedNonSparse = false | |
| 413 | ||
| 414 | this.lengthAcked = 0 | |
| 415 | ||
| 416 | this.extensions = new Map() | |
| 417 | this.lastExtensionSent = '' | |
| 418 | this.lastExtensionRecv = '' | |
| 419 | ||
| 420 | replicator._ifAvailable++ | |
| 421 | } | |
| 422 | ||
| 423 | get remoteContiguousLength () { | |
| 424 | return this.remoteBitfield.findFirst(false, this._remoteContiguousLength) | |
| 425 | } | |
| 426 | ||
| 427 | getMaxInflight () { | |
| 428 | const stream = this.stream.rawStream | |
| 429 | if (!stream.udx) return Math.min(this.inflightRange[1], this.inflightRange[0] * 3) | |
| 430 | ||
| 431 | const scale = stream.rtt <= SCALE_LATENCY ? 1 : stream.rtt / SCALE_LATENCY * Math.min(1, 2 / this.replicator.peers.length) | |
| 432 | return Math.max(this.inflightRange[0], Math.round(Math.min(this.inflightRange[1], this.inflightRange[0] * scale))) | |
| 433 | } | |
| 434 | ||
| 435 | getMaxHotswapInflight () { | |
| 436 | const inf = this.getMaxInflight() | |
| 437 | return Math.max(16, inf / 2) | |
| 438 | } | |
| 439 | ||
| 440 | signalUpgrade () { | |
| 441 | if (this._shouldUpdateCanUpgrade() === true) this._updateCanUpgradeAndSync() | |
| 442 | else this.sendSync() | |
| 443 | } | |
| 444 | ||
| 445 | _markInflight (index) { | |
| 446 | this.missingBlocks.set(index, false) | |
| 447 | } | |
| 448 | ||
| 449 | broadcastRange (start, length, drop) { | |
| 450 | if (drop) this._unclearLocalRange(start, length) | |
| 451 | else this._clearLocalRange(start, length) | |
| 452 | ||
| 453 | // TODO: consider also adding early-returns on the drop===true case | |
| 454 | if (!drop) { | |
| 455 | // No need to broadcast if the remote already has this range | |
| 456 | ||
| 457 | if (this._remoteContiguousLength >= start + length) return | |
| 458 | ||
| 459 | if (length === 1) { | |
| 460 | if (this.remoteBitfield.get(start)) return | |
| 461 | } else { | |
| 462 | if (this.remoteBitfield.firstUnset(start) >= start + length) return | |
| 463 | } | |
| 464 | } | |
| 465 | ||
| 466 | this.wireRange.send({ | |
| 467 | drop, | |
| 468 | start, | |
| 469 | length | |
| 470 | }) | |
| 471 | incrementTx(this.stats.wireRange, this.replicator.stats.wireRange) | |
| 472 | } | |
| 473 | ||
| 474 | extension (name, message) { | |
| 475 | this.wireExtension.send({ name: name === this.lastExtensionSent ? '' : name, message }) | |
| 476 | incrementTx(this.stats.wireExtension, this.replicator.stats.wireExtension) | |
| 477 | this.lastExtensionSent = name | |
| 478 | } | |
| 479 | ||
| 480 | onextension (message) { | |
| 481 | const name = message.name || this.lastExtensionRecv | |
| 482 | this.lastExtensionRecv = name | |
| 483 | const ext = this.extensions.get(name) | |
| 484 | if (ext) ext._onmessage({ start: 0, end: message.message.byteLength, buffer: message.message }, this) | |
| 485 | } | |
| 486 | ||
| 487 | sendSync () { | |
| 488 | if (this.syncsProcessing !== 0) { | |
| 489 | this.needsSync = true | |
| 490 | return | |
| 491 | } | |
| 492 | ||
| 493 | if (this.core.tree.fork !== this.remoteFork) { | |
| 494 | this.canUpgrade = false | |
| 495 | } | |
| 496 | ||
| 497 | this.needsSync = false | |
| 498 | ||
| 499 | this.wireSync.send({ | |
| 500 | fork: this.core.tree.fork, | |
| 501 | length: this.core.tree.length, | |
| 502 | remoteLength: this.core.tree.fork === this.remoteFork ? this.remoteLength : 0, | |
| 503 | canUpgrade: this.canUpgrade, | |
| 504 | uploading: true, | |
| 505 | downloading: this.replicator.isDownloading(), | |
| 506 | hasManifest: !!this.core.header.manifest && this.core.compat === false | |
| 507 | }) | |
| 508 | incrementTx(this.stats.wireSync, this.replicator.stats.wireSync) | |
| 509 | } | |
| 510 | ||
| 511 | onopen ({ seeks, capability }) { | |
| 512 | const expected = caps.replicate(this.stream.isInitiator === false, this.replicator.key, this.stream.handshakeHash) | |
| 513 | ||
| 514 | if (b4a.equals(capability, expected) !== true) { // TODO: change this to a rejection instead, less leakage | |
| 515 | throw INVALID_CAPABILITY('Remote sent an invalid replication capability') | |
| 516 | } | |
| 517 | ||
| 518 | if (this.remoteOpened === true) return | |
| 519 | this.remoteOpened = true | |
| 520 | this.remoteSupportsSeeks = seeks | |
| 521 | ||
| 522 | this.protomux.cork() | |
| 523 | ||
| 524 | this.sendSync() | |
| 525 | ||
| 526 | const contig = Math.min(this.core.tree.length, this.core.header.hints.contiguousLength) | |
| 527 | if (contig > 0) { | |
| 528 | this.broadcastRange(0, contig, false) | |
| 529 | ||
| 530 | if (contig === this.core.tree.length) { | |
| 531 | this.broadcastedNonSparse = true | |
| 532 | } | |
| 533 | } | |
| 534 | ||
| 535 | this.replicator._ifAvailable-- | |
| 536 | this.replicator._addPeer(this) | |
| 537 | ||
| 538 | this.protomux.uncork() | |
| 539 | } | |
| 540 | ||
| 541 | onclose (isRemote) { | |
| 542 | // we might have signalled to the remote that we are done (ie not downloading) and the remote might agree on that | |
| 543 | // if that happens, the channel might be closed by the remote. if so just renegotiate it. | |
| 544 | // TODO: add a CLOSE_REASON to mux to we can make this cleaner... | |
| 545 | const reopen = isRemote === true && this.remoteOpened === true && this.remoteDownloading === false && | |
| 546 | this.remoteUploading === true && this.replicator.downloading === true | |
| 547 | ||
| 548 | if (this.remoteOpened === false) { | |
| 549 | if (this.useSession) { | |
| 550 | this.replicator._peerSessions-- | |
| 551 | this.replicator._closeSessionMaybe() | |
| 552 | } | |
| 553 | this.replicator._ifAvailable-- | |
| 554 | this.replicator.updateAll() | |
| 555 | return | |
| 556 | } | |
| 557 | ||
| 558 | this.remoteOpened = false | |
| 559 | this.removed = true | |
| 560 | this.remoteRequests.clear() // cancel all | |
| 561 | this.receiverQueue.clear() | |
| 562 | ||
| 563 | if (this.roundtripQueue !== null) { | |
| 564 | for (const id of this.roundtripQueue.clear()) this.replicator._inflight.reusable(id) | |
| 565 | } | |
| 566 | ||
| 567 | this.replicator._removePeer(this) | |
| 568 | ||
| 569 | if (reopen) { | |
| 570 | this.replicator._makePeer(this.protomux, this.useSession) | |
| 571 | } | |
| 572 | ||
| 573 | if (this.useSession) { | |
| 574 | this.replicator._peerSessions-- | |
| 575 | this.replicator._closeSessionMaybe() | |
| 576 | } | |
| 577 | } | |
| 578 | ||
| 579 | closeIfIdle () { | |
| 580 | if (this.remoteDownloading === false && this.replicator.isDownloading() === false) { | |
| 581 | // idling, shut it down... | |
| 582 | this.channel.close() | |
| 583 | return true | |
| 584 | } | |
| 585 | ||
| 586 | return false | |
| 587 | } | |
| 588 | ||
| 589 | async onsync ({ fork, length, remoteLength, canUpgrade, uploading, downloading, hasManifest }) { | |
| 590 | const lengthChanged = length !== this.remoteLength | |
| 591 | const sameFork = fork === this.core.tree.fork | |
| 592 | ||
| 593 | this.remoteSynced = true | |
| 594 | this.remoteFork = fork | |
| 595 | this.remoteLength = length | |
| 596 | this.remoteCanUpgrade = canUpgrade | |
| 597 | this.remoteUploading = uploading | |
| 598 | this.remoteDownloading = downloading | |
| 599 | this.remoteHasManifest = hasManifest | |
| 600 | ||
| 601 | if (this.closeIfIdle()) return | |
| 602 | ||
| 603 | this.lengthAcked = sameFork ? remoteLength : 0 | |
| 604 | this.syncsProcessing++ | |
| 605 | ||
| 606 | this.replicator._updateFork(this) | |
| 607 | ||
| 608 | if (this.remoteLength > this.core.tree.length && this.lengthAcked === this.core.tree.length) { | |
| 609 | if (this.replicator._addUpgradeMaybe() !== null) this._update() | |
| 610 | } | |
| 611 | ||
| 612 | const upgrade = (lengthChanged === false || sameFork === false) | |
| 613 | ? this.canUpgrade && sameFork | |
| 614 | : await this._canUpgrade(length, fork) | |
| 615 | ||
| 616 | if (length === this.remoteLength && fork === this.core.tree.fork) { | |
| 617 | this.canUpgrade = upgrade | |
| 618 | } | |
| 619 | ||
| 620 | if (--this.syncsProcessing !== 0) return // ie not latest | |
| 621 | ||
| 622 | if (this.needsSync === true || (this.core.tree.fork === this.remoteFork && this.core.tree.length > this.remoteLength)) { | |
| 623 | this.signalUpgrade() | |
| 624 | } | |
| 625 | ||
| 626 | this._update() | |
| 627 | } | |
| 628 | ||
| 629 | _shouldUpdateCanUpgrade () { | |
| 630 | return this.core.tree.fork === this.remoteFork && | |
| 631 | this.core.tree.length > this.remoteLength && | |
| 632 | this.canUpgrade === false && | |
| 633 | this.syncsProcessing === 0 | |
| 634 | } | |
| 635 | ||
| 636 | async _updateCanUpgradeAndSync () { | |
| 637 | const { length, fork } = this.core.tree | |
| 638 | ||
| 639 | const canUpgrade = await this._canUpgrade(this.remoteLength, this.remoteFork) | |
| 640 | ||
| 641 | if (this.syncsProcessing > 0 || length !== this.core.tree.length || fork !== this.core.tree.fork) { | |
| 642 | return | |
| 643 | } | |
| 644 | if (canUpgrade === this.canUpgrade) { | |
| 645 | return | |
| 646 | } | |
| 647 | ||
| 648 | this.canUpgrade = canUpgrade | |
| 649 | this.sendSync() | |
| 650 | } | |
| 651 | ||
| 652 | // Safe to call in the background - never fails | |
| 653 | async _canUpgrade (remoteLength, remoteFork) { | |
| 654 | if (remoteFork !== this.core.tree.fork) return false | |
| 655 | ||
| 656 | if (remoteLength === 0) return true | |
| 657 | if (remoteLength >= this.core.tree.length) return false | |
| 658 | ||
| 659 | try { | |
| 660 | // Rely on caching to make sure this is cheap... | |
| 661 | const canUpgrade = await this.core.tree.upgradeable(remoteLength) | |
| 662 | ||
| 663 | if (remoteFork !== this.core.tree.fork) return false | |
| 664 | ||
| 665 | return canUpgrade | |
| 666 | } catch { | |
| 667 | return false | |
| 668 | } | |
| 669 | } | |
| 670 | ||
| 671 | async _getProof (msg) { | |
| 672 | const proof = await this.core.tree.proof(msg) | |
| 673 | ||
| 674 | if (proof.block) { | |
| 675 | const index = msg.block.index | |
| 676 | ||
| 677 | if (msg.fork !== this.core.tree.fork || !this.core.bitfield.get(index)) { | |
| 678 | return null | |
| 679 | } | |
| 680 | ||
| 681 | proof.block.value = await this.core.blocks.get(index) | |
| 682 | } | |
| 683 | ||
| 684 | if (msg.manifest && !this.core.compat) { | |
| 685 | proof.manifest = this.core.header.manifest | |
| 686 | } | |
| 687 | ||
| 688 | return proof | |
| 689 | } | |
| 690 | ||
| 691 | async onrequest (msg) { | |
| 692 | const size = this.remoteRequests.size | |
| 693 | this.remoteRequests.set(msg.id, msg) | |
| 694 | ||
| 695 | // if size didnt change -> id overwrite -> old one is deleted, cancel current and re-add | |
| 696 | if (size === this.remoteRequests.size) { | |
| 697 | this._cancel(msg.id) | |
| 698 | this.remoteRequests.set(msg.id, msg) | |
| 699 | } | |
| 700 | ||
| 701 | if (!this.protomux.drained || this.receiverQueue.length) { | |
| 702 | this.receiverQueue.push(msg) | |
| 703 | return | |
| 704 | } | |
| 705 | ||
| 706 | await this._handleRequest(msg) | |
| 707 | } | |
| 708 | ||
| 709 | oncancel (msg) { | |
| 710 | this._cancel(msg.request) | |
| 711 | } | |
| 712 | ||
| 713 | _cancel (id) { | |
| 714 | this.remoteRequests.delete(id) | |
| 715 | this.receiverQueue.delete(id) | |
| 716 | } | |
| 717 | ||
| 718 | ondrain () { | |
| 719 | return this._handleRequests() | |
| 720 | } | |
| 721 | ||
| 722 | async _handleRequests () { | |
| 723 | if (this.receiverBusy) return | |
| 724 | this.receiverBusy = true | |
| 725 | this.protomux.cork() | |
| 726 | ||
| 727 | while (this.remoteOpened && this.protomux.drained && this.receiverQueue.length > 0 && !this.removed) { | |
| 728 | const msg = this.receiverQueue.shift() | |
| 729 | await this._handleRequest(msg) | |
| 730 | } | |
| 731 | ||
| 732 | this.protomux.uncork() | |
| 733 | this.receiverBusy = false | |
| 734 | } | |
| 735 | ||
| 736 | async _handleRequest (msg) { | |
| 737 | let proof = null | |
| 738 | ||
| 739 | // TODO: could still be answerable if (index, fork) is an ancestor of the current fork | |
| 740 | if (msg.fork === this.core.tree.fork) { | |
| 741 | try { | |
| 742 | proof = await this._getProof(msg) | |
| 743 | } catch (err) { | |
| 744 | safetyCatch(err) | |
| 745 | if (msg.fork === this.core.tree.fork && isCriticalError(err)) throw err | |
| 746 | } | |
| 747 | } | |
| 748 | ||
| 749 | // if cancelled do not reply | |
| 750 | if (this.remoteRequests.get(msg.id) !== msg) { | |
| 751 | return | |
| 752 | } | |
| 753 | ||
| 754 | // sync from now on, so safe to delete from the map | |
| 755 | this.remoteRequests.delete(msg.id) | |
| 756 | ||
| 757 | if (proof === null) { | |
| 758 | if (msg.manifest && this.core.header.manifest) { | |
| 759 | const manifest = this.core.header.manifest | |
| 760 | this.wireData.send({ request: msg.id, fork: this.core.tree.fork, block: null, hash: null, seek: null, upgrade: null, manifest }) | |
| 761 | incrementTx(this.stats.wireData, this.replicator.stats.wireData) | |
| 762 | return | |
| 763 | } | |
| 764 | ||
| 765 | this.wireNoData.send({ request: msg.id }) | |
| 766 | return | |
| 767 | } | |
| 768 | ||
| 769 | if (proof.block !== null) { | |
| 770 | this.replicator.onupload(proof.block.index, proof.block.value, this) | |
| 771 | } | |
| 772 | ||
| 773 | this.wireData.send({ | |
| 774 | request: msg.id, | |
| 775 | fork: msg.fork, | |
| 776 | block: proof.block, | |
| 777 | hash: proof.hash, | |
| 778 | seek: proof.seek, | |
| 779 | upgrade: proof.upgrade, | |
| 780 | manifest: proof.manifest | |
| 781 | }) | |
| 782 | incrementTx(this.stats.wireData, this.replicator.stats.wireData) | |
| 783 | } | |
| 784 | ||
| 785 | _cancelRequest (req) { | |
| 786 | if (req.priority === PRIORITY.CANCELLED) return | |
| 787 | // mark as cancelled also and avoid re-entry | |
| 788 | req.priority = PRIORITY.CANCELLED | |
| 789 | ||
| 790 | this.inflight-- | |
| 791 | this.replicator._requestDone(req.id, false) | |
| 792 | ||
| 793 | // clear inflight state | |
| 794 | if (isBlockRequest(req)) this.replicator._unmarkInflight(req.block.index) | |
| 795 | if (isUpgradeRequest(req)) this.replicator._clearInflightUpgrade(req) | |
| 796 | ||
| 797 | if (this.roundtripQueue === null) this.roundtripQueue = new RoundtripQueue() | |
| 798 | this.roundtripQueue.add(req.id) | |
| 799 | this.wireCancel.send({ request: req.id }) | |
| 800 | incrementTx(this.stats.wireCancel, this.replicator.stats.wireCancel) | |
| 801 | } | |
| 802 | ||
| 803 | _checkIfConflict () { | |
| 804 | this.paused = true | |
| 805 | ||
| 806 | const length = Math.min(this.core.tree.length, this.remoteLength) | |
| 807 | if (length === 0) return // pause and ignore | |
| 808 | ||
| 809 | this.wireRequest.send({ | |
| 810 | id: 0, // TODO: use an more explicit id for this eventually... | |
| 811 | fork: this.remoteFork, | |
| 812 | block: null, | |
| 813 | hash: null, | |
| 814 | seek: null, | |
| 815 | upgrade: { | |
| 816 | start: 0, | |
| 817 | length | |
| 818 | } | |
| 819 | }) | |
| 820 | ||
| 821 | incrementTx(this.stats.wireRequest, this.replicator.stats.wireRequest) | |
| 822 | } | |
| 823 | ||
| 824 | async ondata (data) { | |
| 825 | // always allow a fork conflict proof to be sent | |
| 826 | if (data.request === 0 && data.upgrade && data.upgrade.start === 0) { | |
| 827 | if (await this.core.checkConflict(data, this)) return | |
| 828 | this.paused = false | |
| 829 | } | |
| 830 | ||
| 831 | const req = data.request > 0 ? this.replicator._inflight.get(data.request) : null | |
| 832 | const reorg = data.fork > this.core.tree.fork | |
| 833 | ||
| 834 | // no push atm, TODO: check if this satisfies another pending request | |
| 835 | // allow reorg pushes tho as those are not written to storage so we'll take all the help we can get | |
| 836 | if (req === null && reorg === false) return | |
| 837 | ||
| 838 | if (req !== null) { | |
| 839 | if (req.peer !== this) return | |
| 840 | this._onrequestroundtrip(req) | |
| 841 | } | |
| 842 | ||
| 843 | try { | |
| 844 | if (reorg === true) return await this.replicator._onreorgdata(this, req, data) | |
| 845 | } catch (err) { | |
| 846 | safetyCatch(err) | |
| 847 | if (isBlockRequest(req)) this.replicator._unmarkInflight(req.block.index) | |
| 848 | ||
| 849 | this.paused = true | |
| 850 | this.replicator.oninvalid(err, req, data, this) | |
| 851 | return | |
| 852 | } | |
| 853 | ||
| 854 | this.dataProcessing++ | |
| 855 | ||
| 856 | try { | |
| 857 | if (!matchingRequest(req, data) || !(await this.core.verify(data, this))) { | |
| 858 | this.replicator._onnodata(this, req) | |
| 859 | return | |
| 860 | } | |
| 861 | } catch (err) { | |
| 862 | safetyCatch(err) | |
| 863 | if (isBlockRequest(req)) this.replicator._unmarkInflight(req.block.index) | |
| 864 | ||
| 865 | if (err.code === 'WRITE_FAILED') { | |
| 866 | // For example, we don't want to keep pulling data when storage is full | |
| 867 | // TODO: notify the user somehow | |
| 868 | this.paused = true | |
| 869 | return | |
| 870 | } | |
| 871 | ||
| 872 | if (this.core.closed && !isCriticalError(err)) return | |
| 873 | ||
| 874 | if (err.code !== 'INVALID_OPERATION') { | |
| 875 | // might be a fork, verify | |
| 876 | this._checkIfConflict() | |
| 877 | } | |
| 878 | ||
| 879 | this.replicator._onnodata(this, req) | |
| 880 | this.replicator.oninvalid(err, req, data, this) | |
| 881 | return | |
| 882 | } finally { | |
| 883 | this.dataProcessing-- | |
| 884 | } | |
| 885 | ||
| 886 | this.replicator._ondata(this, req, data) | |
| 887 | ||
| 888 | if (this._shouldUpdateCanUpgrade() === true) { | |
| 889 | this._updateCanUpgradeAndSync() | |
| 890 | } | |
| 891 | } | |
| 892 | ||
| 893 | onnodata ({ request }) { | |
| 894 | const req = request > 0 ? this.replicator._inflight.get(request) : null | |
| 895 | ||
| 896 | if (req === null || req.peer !== this) return | |
| 897 | ||
| 898 | this._onrequestroundtrip(req) | |
| 899 | this.replicator._onnodata(this, req) | |
| 900 | } | |
| 901 | ||
| 902 | _onrequestroundtrip (req) { | |
| 903 | if (req.priority === PRIORITY.CANCELLED) return | |
| 904 | // to avoid re-entry we also just mark it as cancelled | |
| 905 | req.priority = PRIORITY.CANCELLED | |
| 906 | ||
| 907 | this.inflight-- | |
| 908 | this.replicator._requestDone(req.id, true) | |
| 909 | if (this.roundtripQueue === null) return | |
| 910 | const flushed = this.roundtripQueue.flush(req.rt) | |
| 911 | if (flushed === null) return | |
| 912 | for (const id of flushed) this.replicator._inflight.reusable(id) | |
| 913 | } | |
| 914 | ||
| 915 | onwant ({ start, length }) { | |
| 916 | this.replicator._onwant(this, start, length) | |
| 917 | } | |
| 918 | ||
| 919 | onunwant () { | |
| 920 | // TODO | |
| 921 | } | |
| 922 | ||
| 923 | onbitfield ({ start, bitfield }) { | |
| 924 | if (start < this._remoteContiguousLength) this._remoteContiguousLength = start // bitfield is always the truth | |
| 925 | this.remoteBitfield.insert(start, bitfield) | |
| 926 | this.missingBlocks.insert(start, bitfield) | |
| 927 | this._clearLocalRange(start, bitfield.byteLength * 8) | |
| 928 | this._update() | |
| 929 | } | |
| 930 | ||
| 931 | _clearLocalRange (start, length) { | |
| 932 | const bitfield = this.core.skipBitfield === null ? this.core.bitfield : this.core.skipBitfield | |
| 933 | ||
| 934 | if (length === 1) { | |
| 935 | this.missingBlocks.set(start, this._remoteHasBlock(start) && !bitfield.get(start)) | |
| 936 | return | |
| 937 | } | |
| 938 | ||
| 939 | const contig = Math.min(this.core.tree.length, this.core.header.hints.contiguousLength) | |
| 940 | ||
| 941 | if (start + length < contig) { | |
| 942 | const delta = contig - start | |
| 943 | this.missingBlocks.setRange(start, delta, false) | |
| 944 | return | |
| 945 | } | |
| 946 | ||
| 947 | const rem = start & 32767 | |
| 948 | if (rem > 0) { | |
| 949 | start -= rem | |
| 950 | length += rem | |
| 951 | } | |
| 952 | ||
| 953 | const end = start + Math.min(length, this.core.tree.length) | |
| 954 | while (start < end) { | |
| 955 | const local = bitfield.getBitfield(start) | |
| 956 | ||
| 957 | if (local && local.bitfield) { | |
| 958 | this.missingBlocks.clear(start, local.bitfield) | |
| 959 | } | |
| 960 | ||
| 961 | start += 32768 | |
| 962 | } | |
| 963 | } | |
| 964 | ||
| 965 | _resetMissingBlock (index) { | |
| 966 | const bitfield = this.core.skipBitfield === null ? this.core.bitfield : this.core.skipBitfield | |
| 967 | this.missingBlocks.set(index, this._remoteHasBlock(index) && !bitfield.get(index)) | |
| 968 | } | |
| 969 | ||
| 970 | _unclearLocalRange (start, length) { | |
| 971 | if (length === 1) { | |
| 972 | this._resetMissingBlock(start) | |
| 973 | return | |
| 974 | } | |
| 975 | ||
| 976 | const rem = start & 2097151 | |
| 977 | if (rem > 0) { | |
| 978 | start -= rem | |
| 979 | length += rem | |
| 980 | } | |
| 981 | ||
| 982 | const fixedStart = start | |
| 983 | ||
| 984 | const end = start + Math.min(length, this.remoteLength) | |
| 985 | while (start < end) { | |
| 986 | const remote = this.remoteBitfield.getBitfield(start) | |
| 987 | if (remote && remote.bitfield) { | |
| 988 | this.missingBlocks.insert(start, remote.bitfield) | |
| 989 | } | |
| 990 | ||
| 991 | start += 2097152 | |
| 992 | } | |
| 993 | ||
| 994 | this._clearLocalRange(fixedStart, length) | |
| 995 | } | |
| 996 | ||
| 997 | onrange ({ drop, start, length }) { | |
| 998 | const has = drop === false | |
| 999 | ||
| 1000 | if (drop === true && start < this._remoteContiguousLength) { | |
| 1001 | this._remoteContiguousLength = start | |
| 1002 | } | |
| 1003 | ||
| 1004 | if (start === 0 && drop === false) { | |
| 1005 | if (length > this._remoteContiguousLength) this._remoteContiguousLength = length | |
| 1006 | } else if (length === 1) { | |
| 1007 | const bitfield = this.core.skipBitfield === null ? this.core.bitfield : this.core.skipBitfield | |
| 1008 | this.remoteBitfield.set(start, has) | |
| 1009 | this.missingBlocks.set(start, has && !bitfield.get(start)) | |
| 1010 | } else { | |
| 1011 | const rangeStart = this.remoteBitfield.findFirst(!has, start) | |
| 1012 | const rangeLength = length - (rangeStart - start) | |
| 1013 | ||
| 1014 | if (rangeLength > 0) { | |
| 1015 | this.remoteBitfield.setRange(rangeStart, rangeLength, has) | |
| 1016 | this.missingBlocks.setRange(rangeStart, rangeLength, has) | |
| 1017 | if (has) this._clearLocalRange(rangeStart, rangeLength) | |
| 1018 | } | |
| 1019 | } | |
| 1020 | ||
| 1021 | if (drop === false) this._update() | |
| 1022 | } | |
| 1023 | ||
| 1024 | onreorghint () { | |
| 1025 | // TODO | |
| 1026 | } | |
| 1027 | ||
| 1028 | _update () { | |
| 1029 | // TODO: if this is in a batch or similar it would be better to defer it | |
| 1030 | // we could do that with nextTick/microtick mb? (combined with a property on the session to signal read buffer mb) | |
| 1031 | this.replicator.updatePeer(this) | |
| 1032 | } | |
| 1033 | ||
| 1034 | async _onconflict () { | |
| 1035 | this.protomux.cork() | |
| 1036 | if (this.remoteLength > 0 && this.core.tree.fork === this.remoteFork) { | |
| 1037 | await this.onrequest({ | |
| 1038 | id: 0, | |
| 1039 | fork: this.core.tree.fork, | |
| 1040 | block: null, | |
| 1041 | hash: null, | |
| 1042 | seek: null, | |
| 1043 | upgrade: { | |
| 1044 | start: 0, | |
| 1045 | length: Math.min(this.core.tree.length, this.remoteLength) | |
| 1046 | } | |
| 1047 | }) | |
| 1048 | } | |
| 1049 | this.channel.close() | |
| 1050 | this.protomux.uncork() | |
| 1051 | } | |
| 1052 | ||
| 1053 | _makeRequest (needsUpgrade, priority, minLength) { | |
| 1054 | if (needsUpgrade === true && this.replicator._shouldUpgrade(this) === false) { | |
| 1055 | return null | |
| 1056 | } | |
| 1057 | ||
| 1058 | // ensure that the remote has signalled they have the length we request | |
| 1059 | if (this.remoteLength < minLength) { | |
| 1060 | return null | |
| 1061 | } | |
| 1062 | ||
| 1063 | if (needsUpgrade === false && this.replicator._autoUpgrade(this) === true) { | |
| 1064 | needsUpgrade = true | |
| 1065 | } | |
| 1066 | ||
| 1067 | return { | |
| 1068 | peer: this, | |
| 1069 | rt: this.roundtripQueue === null ? 0 : this.roundtripQueue.tick, | |
| 1070 | id: 0, | |
| 1071 | fork: this.remoteFork, | |
| 1072 | block: null, | |
| 1073 | hash: null, | |
| 1074 | seek: null, | |
| 1075 | upgrade: needsUpgrade === false | |
| 1076 | ? null | |
| 1077 | : { start: this.core.tree.length, length: this.remoteLength - this.core.tree.length }, | |
| 1078 | // remote manifest check can be removed eventually... | |
| 1079 | manifest: this.core.header.manifest === null && this.remoteHasManifest === true, | |
| 1080 | priority | |
| 1081 | } | |
| 1082 | } | |
| 1083 | ||
| 1084 | _requestManifest () { | |
| 1085 | const req = this._makeRequest(false, 0, 0) | |
| 1086 | this._send(req) | |
| 1087 | } | |
| 1088 | ||
| 1089 | _requestUpgrade (u) { | |
| 1090 | const req = this._makeRequest(true, 0, 0) | |
| 1091 | if (req === null) return false | |
| 1092 | ||
| 1093 | this._send(req) | |
| 1094 | ||
| 1095 | return true | |
| 1096 | } | |
| 1097 | ||
| 1098 | _requestSeek (s) { | |
| 1099 | // if replicator is updating the seeks etc, bail and wait for it to drain | |
| 1100 | if (this.replicator._updatesPending > 0) return false | |
| 1101 | ||
| 1102 | const { length, fork } = this.core.tree | |
| 1103 | ||
| 1104 | if (fork !== this.remoteFork) return false | |
| 1105 | ||
| 1106 | if (s.seeker.start >= length) { | |
| 1107 | const req = this._makeRequest(true, 0, 0) | |
| 1108 | ||
| 1109 | // We need an upgrade for the seek, if non can be provided, skip | |
| 1110 | if (req === null) return false | |
| 1111 | ||
| 1112 | req.seek = this.remoteSupportsSeeks ? { bytes: s.seeker.bytes, padding: s.seeker.padding } : null | |
| 1113 | ||
| 1114 | s.inflight.push(req) | |
| 1115 | this._send(req) | |
| 1116 | ||
| 1117 | return true | |
| 1118 | } | |
| 1119 | ||
| 1120 | const len = s.seeker.end - s.seeker.start | |
| 1121 | const off = s.seeker.start + Math.floor(Math.random() * len) | |
| 1122 | ||
| 1123 | for (let i = 0; i < len; i++) { | |
| 1124 | let index = off + i | |
| 1125 | if (index > s.seeker.end) index -= len | |
| 1126 | ||
| 1127 | if (this._remoteHasBlock(index) === false) continue | |
| 1128 | if (this.core.bitfield.get(index) === true) continue | |
| 1129 | if (!this._hasTreeParent(index)) continue | |
| 1130 | ||
| 1131 | // Check if this block is currently inflight - if so pick another | |
| 1132 | const b = this.replicator._blocks.get(index) | |
| 1133 | if (b !== null && b.inflight.length > 0) continue | |
| 1134 | ||
| 1135 | // Block is not inflight, but we only want the hash, check if that is inflight | |
| 1136 | const h = this.replicator._hashes.add(index, PRIORITY.NORMAL) | |
| 1137 | if (h.inflight.length > 0) continue | |
| 1138 | ||
| 1139 | const req = this._makeRequest(false, h.priority, index + 1) | |
| 1140 | if (req === null) continue | |
| 1141 | ||
| 1142 | const nodes = flatTree.depth(s.seeker.start + s.seeker.end - 1) | |
| 1143 | ||
| 1144 | req.hash = { index: 2 * index, nodes } | |
| 1145 | req.seek = this.remoteSupportsSeeks ? { bytes: s.seeker.bytes, padding: s.seeker.padding } : null | |
| 1146 | ||
| 1147 | s.inflight.push(req) | |
| 1148 | h.inflight.push(req) | |
| 1149 | this._send(req) | |
| 1150 | ||
| 1151 | return true | |
| 1152 | } | |
| 1153 | ||
| 1154 | this._maybeWant(s.seeker.start, len) | |
| 1155 | return false | |
| 1156 | } | |
| 1157 | ||
| 1158 | _hasTreeParent (index) { | |
| 1159 | if (this.remoteLength >= this.core.tree.length) return true | |
| 1160 | ||
| 1161 | const ite = flatTree.iterator(index * 2) | |
| 1162 | ||
| 1163 | let span = 2 | |
| 1164 | let length = 0 | |
| 1165 | ||
| 1166 | while (true) { | |
| 1167 | ite.parent() | |
| 1168 | ||
| 1169 | const left = (ite.index - ite.factor / 2 + 1) / 2 | |
| 1170 | length = left + span | |
| 1171 | ||
| 1172 | // if larger than local AND larger than remote - they share the root so its ok | |
| 1173 | if (length > this.core.tree.length) { | |
| 1174 | if (length > this.remoteLength) return true | |
| 1175 | break | |
| 1176 | } | |
| 1177 | ||
| 1178 | // its less than local but larger than remote so skip it | |
| 1179 | if (length > this.remoteLength) break | |
| 1180 | ||
| 1181 | span *= 2 | |
| 1182 | const first = this.core.bitfield.findFirst(true, left) | |
| 1183 | if (first > -1 && first < length) return true | |
| 1184 | } | |
| 1185 | ||
| 1186 | // TODO: push to async queue and check against our local merkle tree if we actually can request this block | |
| 1187 | return false | |
| 1188 | } | |
| 1189 | ||
| 1190 | _remoteHasBlock (index) { | |
| 1191 | return index < this._remoteContiguousLength || this.remoteBitfield.get(index) === true | |
| 1192 | } | |
| 1193 | ||
| 1194 | _sendBlockRequest (req, b) { | |
| 1195 | req.block = { index: b.index, nodes: 0 } | |
| 1196 | this.replicator._markInflight(b.index) | |
| 1197 | ||
| 1198 | b.inflight.push(req) | |
| 1199 | this.replicator.hotswaps.add(b) | |
| 1200 | this._send(req) | |
| 1201 | } | |
| 1202 | ||
| 1203 | _requestBlock (b) { | |
| 1204 | const { length, fork } = this.core.tree | |
| 1205 | ||
| 1206 | if (this._remoteHasBlock(b.index) === false || fork !== this.remoteFork) { | |
| 1207 | this._maybeWant(b.index) | |
| 1208 | return false | |
| 1209 | } | |
| 1210 | ||
| 1211 | if (!this._hasTreeParent(b.index)) { | |
| 1212 | return false | |
| 1213 | } | |
| 1214 | ||
| 1215 | const req = this._makeRequest(b.index >= length, b.priority, b.index + 1) | |
| 1216 | if (req === null) return false | |
| 1217 | ||
| 1218 | this._sendBlockRequest(req, b) | |
| 1219 | ||
| 1220 | return true | |
| 1221 | } | |
| 1222 | ||
| 1223 | _requestRangeBlock (index, length) { | |
| 1224 | if (this.core.bitfield.get(index) === true || !this._hasTreeParent(index)) return false | |
| 1225 | ||
| 1226 | const b = this.replicator._blocks.add(index, PRIORITY.NORMAL) | |
| 1227 | if (b.inflight.length > 0) { | |
| 1228 | this.missingBlocks.set(index, false) // in case we missed some states just set them ondemand, nbd | |
| 1229 | return false | |
| 1230 | } | |
| 1231 | ||
| 1232 | const req = this._makeRequest(index >= length, b.priority, index + 1) | |
| 1233 | ||
| 1234 | // If the request cannot be satisfied, dealloc the block request if no one is subscribed to it | |
| 1235 | if (req === null) { | |
| 1236 | b.gc() | |
| 1237 | return false | |
| 1238 | } | |
| 1239 | ||
| 1240 | this._sendBlockRequest(req, b) | |
| 1241 | ||
| 1242 | // Don't think this will ever happen, as the pending queue is drained before the range queue | |
| 1243 | // but doesn't hurt to check this explicitly here also. | |
| 1244 | if (b.queued) b.queued = false | |
| 1245 | return true | |
| 1246 | } | |
| 1247 | ||
| 1248 | _findNext (i) { | |
| 1249 | if (i < this._remoteContiguousLength) { | |
| 1250 | if (this.core.skipBitfield === null) this.replicator._openSkipBitfield() | |
| 1251 | i = this.core.skipBitfield.findFirst(false, i) | |
| 1252 | if (i < this._remoteContiguousLength && i > -1) return i | |
| 1253 | i = this._remoteContiguousLength | |
| 1254 | } | |
| 1255 | ||
| 1256 | return this.missingBlocks.findFirst(true, i) | |
| 1257 | } | |
| 1258 | ||
| 1259 | _requestRange (r) { | |
| 1260 | const { length, fork } = this.core.tree | |
| 1261 | ||
| 1262 | if (r.blocks) { | |
| 1263 | let min = -1 | |
| 1264 | let max = -1 | |
| 1265 | ||
| 1266 | for (let i = r.start; i < r.end; i++) { | |
| 1267 | const index = r.blocks[i] | |
| 1268 | if (min === -1 || index < min) min = index | |
| 1269 | if (max === -1 || index > max) max = index | |
| 1270 | const has = index < this._remoteContiguousLength || this.missingBlocks.get(index) === true | |
| 1271 | if (has === true && this._requestRangeBlock(index, length)) return true | |
| 1272 | } | |
| 1273 | ||
| 1274 | if (min > -1) this._maybeWant(min, max - min) | |
| 1275 | return false | |
| 1276 | } | |
| 1277 | ||
| 1278 | const end = Math.min(this.core.tree.length, Math.min(r.end === -1 ? this.remoteLength : r.end, this.remoteLength)) | |
| 1279 | if (end <= r.start || fork !== this.remoteFork) return false | |
| 1280 | ||
| 1281 | const len = end - r.start | |
| 1282 | const off = r.start + (r.linear ? 0 : Math.floor(Math.random() * len)) | |
| 1283 | ||
| 1284 | let i = off | |
| 1285 | ||
| 1286 | while (true) { | |
| 1287 | i = this._findNext(i) | |
| 1288 | if (i === -1 || i >= end) break | |
| 1289 | ||
| 1290 | if (this._requestRangeBlock(i, length)) return true | |
| 1291 | i++ | |
| 1292 | } | |
| 1293 | ||
| 1294 | i = r.start | |
| 1295 | ||
| 1296 | while (true) { | |
| 1297 | i = this._findNext(i) | |
| 1298 | if (i === -1 || i >= off) break | |
| 1299 | ||
| 1300 | if (this._requestRangeBlock(i, length)) return true | |
| 1301 | i++ | |
| 1302 | } | |
| 1303 | ||
| 1304 | this._maybeWant(r.start, len) | |
| 1305 | return false | |
| 1306 | } | |
| 1307 | ||
| 1308 | _requestForkProof (f) { | |
| 1309 | const req = this._makeRequest(false, 0, 0) | |
| 1310 | ||
| 1311 | req.upgrade = { start: 0, length: this.remoteLength } | |
| 1312 | req.manifest = !this.core.header.manifest | |
| 1313 | ||
| 1314 | f.inflight.push(req) | |
| 1315 | this._send(req) | |
| 1316 | } | |
| 1317 | ||
| 1318 | _requestForkRange (f) { | |
| 1319 | if (f.fork !== this.remoteFork || f.batch.want === null) return false | |
| 1320 | ||
| 1321 | const end = Math.min(f.batch.want.end, this.remoteLength) | |
| 1322 | if (end < f.batch.want.start) return false | |
| 1323 | ||
| 1324 | const len = end - f.batch.want.start | |
| 1325 | const off = f.batch.want.start + Math.floor(Math.random() * len) | |
| 1326 | ||
| 1327 | for (let i = 0; i < len; i++) { | |
| 1328 | let index = off + i | |
| 1329 | if (index >= end) index -= len | |
| 1330 | ||
| 1331 | if (this._remoteHasBlock(index) === false) continue | |
| 1332 | ||
| 1333 | const req = this._makeRequest(false, 0, 0) | |
| 1334 | ||
| 1335 | req.hash = { index: 2 * index, nodes: f.batch.want.nodes } | |
| 1336 | ||
| 1337 | f.inflight.push(req) | |
| 1338 | this._send(req) | |
| 1339 | ||
| 1340 | return true | |
| 1341 | } | |
| 1342 | ||
| 1343 | this._maybeWant(f.batch.want.start, len) | |
| 1344 | return false | |
| 1345 | } | |
| 1346 | ||
| 1347 | _maybeWant (start, length = 1) { | |
| 1348 | if (start + length <= this.remoteContiguousLength) return | |
| 1349 | ||
| 1350 | let i = Math.floor(start / DEFAULT_SEGMENT_SIZE) | |
| 1351 | const n = Math.ceil((start + length) / DEFAULT_SEGMENT_SIZE) | |
| 1352 | ||
| 1353 | for (; i < n; i++) { | |
| 1354 | if (this.segmentsWanted.has(i)) continue | |
| 1355 | this.segmentsWanted.add(i) | |
| 1356 | ||
| 1357 | this.wireWant.send({ | |
| 1358 | start: i * DEFAULT_SEGMENT_SIZE, | |
| 1359 | length: DEFAULT_SEGMENT_SIZE | |
| 1360 | }) | |
| 1361 | incrementTx(this.stats.wireWant, this.replicator.stats.wireWant) | |
| 1362 | } | |
| 1363 | } | |
| 1364 | ||
| 1365 | isActive () { | |
| 1366 | if (this.paused || this.removed) return false | |
| 1367 | return true | |
| 1368 | } | |
| 1369 | ||
| 1370 | async _send (req) { | |
| 1371 | const fork = this.core.tree.fork | |
| 1372 | ||
| 1373 | this.inflight++ | |
| 1374 | this.replicator._inflight.add(req) | |
| 1375 | ||
| 1376 | if (req.upgrade !== null && req.fork === fork) { | |
| 1377 | const u = this.replicator._addUpgrade() | |
| 1378 | u.inflight.push(req) | |
| 1379 | } | |
| 1380 | ||
| 1381 | try { | |
| 1382 | if (req.block !== null && req.fork === fork) { | |
| 1383 | req.block.nodes = await this.core.tree.missingNodes(2 * req.block.index) | |
| 1384 | if (req.priority === PRIORITY.CANCELLED) return | |
| 1385 | } | |
| 1386 | if (req.hash !== null && req.fork === fork && req.hash.nodes === 0) { | |
| 1387 | req.hash.nodes = await this.core.tree.missingNodes(req.hash.index) | |
| 1388 | if (req.priority === PRIORITY.CANCELLED) return | |
| 1389 | ||
| 1390 | // nodes === 0, we already have it, bail | |
| 1391 | if (req.hash.nodes === 0 && (req.hash.index & 1) === 0) { | |
| 1392 | this.inflight-- | |
| 1393 | this.replicator._resolveHashLocally(this, req) | |
| 1394 | return | |
| 1395 | } | |
| 1396 | } | |
| 1397 | } catch (err) { | |
| 1398 | this.stream.destroy(err) | |
| 1399 | return | |
| 1400 | } | |
| 1401 | ||
| 1402 | this.wireRequest.send(req) | |
| 1403 | incrementTx(this.stats.wireRequest, this.replicator.stats.wireRequest) | |
| 1404 | } | |
| 1405 | } | |
| 1406 | ||
| 1407 | module.exports = class Replicator { | |
| 1408 | static Peer = Peer // hack to be able to access Peer from outside this module | |
| 1409 | ||
| 1410 | constructor (core, key, { | |
| 1411 | notDownloadingLinger = NOT_DOWNLOADING_SLACK, | |
| 1412 | eagerUpgrade = true, | |
| 1413 | allowFork = true, | |
| 1414 | inflightRange = null, | |
| 1415 | onpeerupdate = noop, | |
| 1416 | onupload = noop, | |
| 1417 | oninvalid = noop | |
| 1418 | } = {}) { | |
| 1419 | this.key = key | |
| 1420 | this.discoveryKey = core.crypto.discoveryKey(key) | |
| 1421 | this.core = core | |
| 1422 | this.eagerUpgrade = eagerUpgrade | |
| 1423 | this.allowFork = allowFork | |
| 1424 | this.onpeerupdate = onpeerupdate | |
| 1425 | this.onupload = onupload | |
| 1426 | this.oninvalid = oninvalid | |
| 1427 | this.ondownloading = null // optional external hook for monitoring downloading status | |
| 1428 | this.peers = [] | |
| 1429 | this.findingPeers = 0 // updateable from the outside | |
| 1430 | this.destroyed = false | |
| 1431 | this.downloading = false | |
| 1432 | this.activeSessions = 0 | |
| 1433 | ||
| 1434 | this.hotswaps = new HotswapQueue() | |
| 1435 | this.inflightRange = inflightRange || DEFAULT_MAX_INFLIGHT | |
| 1436 | ||
| 1437 | // Note: nodata and unwant not currently tracked | |
| 1438 | // tx = transmitted, rx = received | |
| 1439 | this.stats = { | |
| 1440 | wireSync: { tx: 0, rx: 0 }, | |
| 1441 | wireRequest: { tx: 0, rx: 0 }, | |
| 1442 | wireCancel: { tx: 0, rx: 0 }, | |
| 1443 | wireData: { tx: 0, rx: 0 }, | |
| 1444 | wireWant: { tx: 0, rx: 0 }, | |
| 1445 | wireBitfield: { tx: 0, rx: 0 }, | |
| 1446 | wireRange: { tx: 0, rx: 0 }, | |
| 1447 | wireExtension: { tx: 0, rx: 0 }, | |
| 1448 | hotswaps: 0 | |
| 1449 | } | |
| 1450 | ||
| 1451 | this._attached = new Set() | |
| 1452 | this._inflight = new InflightTracker() | |
| 1453 | this._blocks = new BlockTracker() | |
| 1454 | this._hashes = new BlockTracker() | |
| 1455 | ||
| 1456 | this._queued = [] | |
| 1457 | ||
| 1458 | this._seeks = [] | |
| 1459 | this._upgrade = null | |
| 1460 | this._reorgs = [] | |
| 1461 | this._ranges = [] | |
| 1462 | ||
| 1463 | this._hadPeers = false | |
| 1464 | this._ifAvailable = 0 | |
| 1465 | this._updatesPending = 0 | |
| 1466 | this._applyingReorg = null | |
| 1467 | this._manifestPeer = null | |
| 1468 | this._hasSession = false | |
| 1469 | this._peerSessions = 0 | |
| 1470 | this._notDownloadingLinger = notDownloadingLinger | |
| 1471 | this._downloadingTimer = null | |
| 1472 | ||
| 1473 | const self = this | |
| 1474 | this._onstreamclose = onstreamclose | |
| 1475 | ||
| 1476 | function onstreamclose () { | |
| 1477 | self.detachFrom(this.userData) | |
| 1478 | } | |
| 1479 | } | |
| 1480 | ||
| 1481 | updateActivity (inc, session) { | |
| 1482 | this.activeSessions += inc | |
| 1483 | this.setDownloading(this.activeSessions !== 0, session) | |
| 1484 | } | |
| 1485 | ||
| 1486 | isDownloading () { | |
| 1487 | return this.downloading || !this._inflight.idle | |
| 1488 | } | |
| 1489 | ||
| 1490 | setDownloading (downloading) { | |
| 1491 | clearTimeout(this._downloadingTimer) | |
| 1492 | ||
| 1493 | if (this.destroyed) return | |
| 1494 | if (downloading || this._notDownloadingLinger === 0) { | |
| 1495 | this.setDownloadingNow(downloading) | |
| 1496 | return | |
| 1497 | } | |
| 1498 | ||
| 1499 | this._downloadingTimer = setTimeout(setDownloadingLater, this._notDownloadingLinger, this, downloading) | |
| 1500 | } | |
| 1501 | ||
| 1502 | setDownloadingNow (downloading) { | |
| 1503 | this._downloadingTimer = null | |
| 1504 | if (this.downloading === downloading) return | |
| 1505 | this.downloading = downloading | |
| 1506 | if (!downloading && this.isDownloading()) return | |
| 1507 | ||
| 1508 | for (const peer of this.peers) peer.signalUpgrade() | |
| 1509 | ||
| 1510 | if (downloading) { // restart channel if needed... | |
| 1511 | for (const protomux of this._attached) { | |
| 1512 | if (!protomux.stream.handshakeHash) continue | |
| 1513 | if (protomux.opened({ protocol: 'hypercore/alpha', id: this.discoveryKey })) continue | |
| 1514 | this._makePeer(protomux, true) | |
| 1515 | } | |
| 1516 | } else { | |
| 1517 | for (const peer of this.peers) peer.closeIfIdle() | |
| 1518 | } | |
| 1519 | ||
| 1520 | if (this.ondownloading !== null && downloading) this.ondownloading() | |
| 1521 | } | |
| 1522 | ||
| 1523 | cork () { | |
| 1524 | for (const peer of this.peers) peer.protomux.cork() | |
| 1525 | } | |
| 1526 | ||
| 1527 | uncork () { | |
| 1528 | for (const peer of this.peers) peer.protomux.uncork() | |
| 1529 | } | |
| 1530 | ||
| 1531 | // Called externally when a range of new blocks has been processed/removed | |
| 1532 | onhave (start, length, drop = false) { | |
| 1533 | for (const peer of this.peers) peer.broadcastRange(start, length, drop) | |
| 1534 | } | |
| 1535 | ||
| 1536 | // Called externally when a truncation upgrade has been processed | |
| 1537 | ontruncate (newLength, truncated) { | |
| 1538 | const notify = [] | |
| 1539 | ||
| 1540 | for (const blk of this._blocks) { | |
| 1541 | if (blk.index < newLength) continue | |
| 1542 | notify.push(blk) | |
| 1543 | } | |
| 1544 | ||
| 1545 | for (const blk of notify) { | |
| 1546 | for (const r of blk.refs) { | |
| 1547 | if (r.snapshot === false) continue | |
| 1548 | blk.detach(r, SNAPSHOT_NOT_AVAILABLE()) | |
| 1549 | } | |
| 1550 | } | |
| 1551 | ||
| 1552 | for (const peer of this.peers) peer._unclearLocalRange(newLength, truncated) | |
| 1553 | } | |
| 1554 | ||
| 1555 | // Called externally when a upgrade has been processed | |
| 1556 | onupgrade () { | |
| 1557 | for (const peer of this.peers) peer.signalUpgrade() | |
| 1558 | if (this._blocks.isEmpty() === false) this._resolveBlocksLocally() | |
| 1559 | if (this._upgrade !== null) this._resolveUpgradeRequest(null) | |
| 1560 | if (this._ranges.length !== 0 || this._seeks.length !== 0) this._updateNonPrimary(true) | |
| 1561 | } | |
| 1562 | ||
| 1563 | // Called externally when a conflict has been detected and verified | |
| 1564 | async onconflict (from) { | |
| 1565 | const all = [] | |
| 1566 | for (const peer of this.peers) { | |
| 1567 | all.push(peer._onconflict()) | |
| 1568 | } | |
| 1569 | await Promise.allSettled(all) | |
| 1570 | } | |
| 1571 | ||
| 1572 | async applyPendingReorg () { | |
| 1573 | if (this._applyingReorg !== null) { | |
| 1574 | await this._applyingReorg | |
| 1575 | return true | |
| 1576 | } | |
| 1577 | ||
| 1578 | for (let i = this._reorgs.length - 1; i >= 0; i--) { | |
| 1579 | const f = this._reorgs[i] | |
| 1580 | if (f.batch !== null && f.batch.finished) { | |
| 1581 | await this._applyReorg(f) | |
| 1582 | return true | |
| 1583 | } | |
| 1584 | } | |
| 1585 | ||
| 1586 | return false | |
| 1587 | } | |
| 1588 | ||
| 1589 | addUpgrade (session) { | |
| 1590 | if (this._upgrade !== null) { | |
| 1591 | const ref = this._upgrade.attach(session) | |
| 1592 | this._checkUpgradeIfAvailable() | |
| 1593 | return ref | |
| 1594 | } | |
| 1595 | ||
| 1596 | const ref = this._addUpgrade().attach(session) | |
| 1597 | ||
| 1598 | this.updateAll() | |
| 1599 | ||
| 1600 | return ref | |
| 1601 | } | |
| 1602 | ||
| 1603 | addBlock (session, index) { | |
| 1604 | const b = this._blocks.add(index, PRIORITY.HIGH) | |
| 1605 | const ref = b.attach(session) | |
| 1606 | ||
| 1607 | this._queueBlock(b) | |
| 1608 | this.updateAll() | |
| 1609 | ||
| 1610 | return ref | |
| 1611 | } | |
| 1612 | ||
| 1613 | addSeek (session, seeker) { | |
| 1614 | const s = new SeekRequest(this._seeks, seeker) | |
| 1615 | const ref = s.attach(session) | |
| 1616 | ||
| 1617 | this._seeks.push(s) | |
| 1618 | this.updateAll() | |
| 1619 | ||
| 1620 | return ref | |
| 1621 | } | |
| 1622 | ||
| 1623 | addRange (session, { start = 0, end = -1, length = toLength(start, end), blocks = null, linear = false, ifAvailable = false } = {}) { | |
| 1624 | if (blocks !== null) { // if using blocks, start, end just acts as frames around the blocks array | |
| 1625 | start = 0 | |
| 1626 | end = length = blocks.length | |
| 1627 | } | |
| 1628 | ||
| 1629 | const r = new RangeRequest( | |
| 1630 | this._ranges, | |
| 1631 | start, | |
| 1632 | length === -1 ? -1 : start + length, | |
| 1633 | linear, | |
| 1634 | ifAvailable, | |
| 1635 | blocks | |
| 1636 | ) | |
| 1637 | ||
| 1638 | const ref = r.attach(session) | |
| 1639 | ||
| 1640 | // Trigger this to see if this is already resolved... | |
| 1641 | // Also auto compresses the range based on local bitfield | |
| 1642 | clampRange(this.core, r) | |
| 1643 | ||
| 1644 | this._ranges.push(r) | |
| 1645 | ||
| 1646 | if (r.end !== -1 && r.start >= r.end) { | |
| 1647 | this._resolveRangeRequest(r, this._ranges.length - 1) | |
| 1648 | return ref | |
| 1649 | } | |
| 1650 | ||
| 1651 | this.updateAll() | |
| 1652 | ||
| 1653 | return ref | |
| 1654 | } | |
| 1655 | ||
| 1656 | cancel (ref) { | |
| 1657 | ref.context.detach(ref, null) | |
| 1658 | } | |
| 1659 | ||
| 1660 | clearRequests (session, err = null) { | |
| 1661 | let cleared = false | |
| 1662 | while (session.length > 0) { | |
| 1663 | const ref = session[session.length - 1] | |
| 1664 | ref.context.detach(ref, err) | |
| 1665 | cleared = true | |
| 1666 | } | |
| 1667 | ||
| 1668 | if (cleared) this.updateAll() | |
| 1669 | } | |
| 1670 | ||
| 1671 | _addUpgradeMaybe () { | |
| 1672 | return this.eagerUpgrade === true ? this._addUpgrade() : this._upgrade | |
| 1673 | } | |
| 1674 | ||
| 1675 | // TODO: this function is OVER called atm, at each updatePeer/updateAll | |
| 1676 | // instead its more efficient to only call it when the conditions in here change - ie on sync/add/remove peer | |
| 1677 | // Do this when we have more tests. | |
| 1678 | _checkUpgradeIfAvailable () { | |
| 1679 | if (this._ifAvailable > 0 && this.peers.length < MAX_PEERS_UPGRADE) return | |
| 1680 | if (this._upgrade === null || this._upgrade.refs.length === 0) return | |
| 1681 | if (this._hadPeers === false && this.findingPeers > 0) return | |
| 1682 | ||
| 1683 | const maxPeers = Math.min(this.peers.length, MAX_PEERS_UPGRADE) | |
| 1684 | ||
| 1685 | // check if a peer can upgrade us | |
| 1686 | ||
| 1687 | for (let i = 0; i < maxPeers; i++) { | |
| 1688 | const peer = this.peers[i] | |
| 1689 | ||
| 1690 | if (peer.remoteSynced === false) return | |
| 1691 | ||
| 1692 | if (this.core.tree.length === 0 && peer.remoteLength > 0) return | |
| 1693 | ||
| 1694 | if (peer.remoteLength <= this._upgrade.length || peer.remoteFork !== this._upgrade.fork) continue | |
| 1695 | ||
| 1696 | if (peer.syncsProcessing > 0) return | |
| 1697 | ||
| 1698 | if (peer.lengthAcked !== this.core.tree.length && peer.remoteFork === this.core.tree.fork) return | |
| 1699 | if (peer.remoteCanUpgrade === true) return | |
| 1700 | } | |
| 1701 | ||
| 1702 | // check if reorgs in progress... | |
| 1703 | ||
| 1704 | if (this._applyingReorg !== null) return | |
| 1705 | ||
| 1706 | // TODO: we prob should NOT wait for inflight reorgs here, seems better to just resolve the upgrade | |
| 1707 | // and then apply the reorg on the next call in case it's slow - needs some testing in practice | |
| 1708 | ||
| 1709 | for (let i = 0; i < this._reorgs.length; i++) { | |
| 1710 | const r = this._reorgs[i] | |
| 1711 | if (r.inflight.length > 0) return | |
| 1712 | } | |
| 1713 | ||
| 1714 | // if something is inflight, wait for that first | |
| 1715 | if (this._upgrade.inflight.length > 0) return | |
| 1716 | ||
| 1717 | // nothing to do, indicate no update avail | |
| 1718 | ||
| 1719 | const u = this._upgrade | |
| 1720 | this._upgrade = null | |
| 1721 | u.resolve(false) | |
| 1722 | } | |
| 1723 | ||
| 1724 | _addUpgrade () { | |
| 1725 | if (this._upgrade !== null) return this._upgrade | |
| 1726 | ||
| 1727 | // TODO: needs a reorg: true/false flag to indicate if the user requested a reorg | |
| 1728 | this._upgrade = new UpgradeRequest(this, this.core.tree.fork, this.core.tree.length) | |
| 1729 | ||
| 1730 | return this._upgrade | |
| 1731 | } | |
| 1732 | ||
| 1733 | _addReorg (fork, peer) { | |
| 1734 | if (this.allowFork === false) return null | |
| 1735 | ||
| 1736 | // TODO: eager gc old reorgs from the same peer | |
| 1737 | // not super important because they'll get gc'ed when the request finishes | |
| 1738 | // but just spam the remote can do ... | |
| 1739 | ||
| 1740 | for (const f of this._reorgs) { | |
| 1741 | if (f.fork > fork && f.batch !== null) return null | |
| 1742 | if (f.fork === fork) return f | |
| 1743 | } | |
| 1744 | ||
| 1745 | const f = { | |
| 1746 | fork, | |
| 1747 | inflight: [], | |
| 1748 | batch: null | |
| 1749 | } | |
| 1750 | ||
| 1751 | this._reorgs.push(f) | |
| 1752 | ||
| 1753 | // maintain sorted by fork | |
| 1754 | let i = this._reorgs.length - 1 | |
| 1755 | while (i > 0 && this._reorgs[i - 1].fork > fork) { | |
| 1756 | this._reorgs[i] = this._reorgs[i - 1] | |
| 1757 | this._reorgs[--i] = f | |
| 1758 | } | |
| 1759 | ||
| 1760 | return f | |
| 1761 | } | |
| 1762 | ||
| 1763 | _shouldUpgrade (peer) { | |
| 1764 | if (this._upgrade !== null && this._upgrade.inflight.length > 0) return false | |
| 1765 | return peer.remoteCanUpgrade === true && | |
| 1766 | peer.remoteLength > this.core.tree.length && | |
| 1767 | peer.lengthAcked === this.core.tree.length | |
| 1768 | } | |
| 1769 | ||
| 1770 | _autoUpgrade (peer) { | |
| 1771 | return this._upgrade !== null && peer.remoteFork === this.core.tree.fork && this._shouldUpgrade(peer) | |
| 1772 | } | |
| 1773 | ||
| 1774 | _addPeer (peer) { | |
| 1775 | this._hadPeers = true | |
| 1776 | this.peers.push(peer) | |
| 1777 | this.updatePeer(peer) | |
| 1778 | this.onpeerupdate(true, peer) | |
| 1779 | } | |
| 1780 | ||
| 1781 | _requestDone (id, roundtrip) { | |
| 1782 | this._inflight.remove(id, roundtrip) | |
| 1783 | if (this.isDownloading() === true) return | |
| 1784 | for (const peer of this.peers) peer.signalUpgrade() | |
| 1785 | } | |
| 1786 | ||
| 1787 | _removePeer (peer) { | |
| 1788 | this.peers.splice(this.peers.indexOf(peer), 1) | |
| 1789 | ||
| 1790 | if (this._manifestPeer === peer) this._manifestPeer = null | |
| 1791 | ||
| 1792 | for (const req of this._inflight) { | |
| 1793 | if (req.peer !== peer) continue | |
| 1794 | this._inflight.remove(req.id, true) | |
| 1795 | this._clearRequest(peer, req) | |
| 1796 | } | |
| 1797 | ||
| 1798 | if (peer.useSession) this._closeSessionMaybe() | |
| 1799 | ||
| 1800 | this.onpeerupdate(false, peer) | |
| 1801 | this.updateAll() | |
| 1802 | } | |
| 1803 | ||
| 1804 | _queueBlock (b) { | |
| 1805 | if (b.inflight.length > 0 || b.queued === true) return | |
| 1806 | b.queued = true | |
| 1807 | this._queued.push(b) | |
| 1808 | } | |
| 1809 | ||
| 1810 | _resolveHashLocally (peer, req) { | |
| 1811 | this._requestDone(req.id, false) | |
| 1812 | this._resolveBlockRequest(this._hashes, req.hash.index / 2, null, req) | |
| 1813 | this.updatePeer(peer) | |
| 1814 | } | |
| 1815 | ||
| 1816 | // Runs in the background - not allowed to throw | |
| 1817 | async _resolveBlocksLocally () { | |
| 1818 | // TODO: check if fork compat etc. Requires that we pass down truncation info | |
| 1819 | ||
| 1820 | let clear = null | |
| 1821 | ||
| 1822 | for (const b of this._blocks) { | |
| 1823 | if (this.core.bitfield.get(b.index) === false) continue | |
| 1824 | ||
| 1825 | try { | |
| 1826 | b.resolve(await this.core.blocks.get(b.index)) | |
| 1827 | } catch (err) { | |
| 1828 | b.reject(err) | |
| 1829 | } | |
| 1830 | ||
| 1831 | if (clear === null) clear = [] | |
| 1832 | clear.push(b) | |
| 1833 | } | |
| 1834 | ||
| 1835 | if (clear === null) return | |
| 1836 | ||
| 1837 | // Currently the block tracker does not support deletes during iteration, so we make | |
| 1838 | // sure to clear them afterwards. | |
| 1839 | for (const b of clear) { | |
| 1840 | this._blocks.remove(b.index) | |
| 1841 | removeHotswap(b) | |
| 1842 | } | |
| 1843 | } | |
| 1844 | ||
| 1845 | _resolveBlockRequest (tracker, index, value, req) { | |
| 1846 | const b = tracker.remove(index) | |
| 1847 | if (b === null) return false | |
| 1848 | ||
| 1849 | removeInflight(b.inflight, req) | |
| 1850 | removeHotswap(b) | |
| 1851 | b.queued = false | |
| 1852 | ||
| 1853 | b.resolve(value) | |
| 1854 | ||
| 1855 | if (b.inflight.length > 0) { // if anything is still inflight, cancel it | |
| 1856 | for (let i = b.inflight.length - 1; i >= 0; i--) { | |
| 1857 | const req = b.inflight[i] | |
| 1858 | req.peer._cancelRequest(req) | |
| 1859 | } | |
| 1860 | } | |
| 1861 | ||
| 1862 | return true | |
| 1863 | } | |
| 1864 | ||
| 1865 | _resolveUpgradeRequest (req) { | |
| 1866 | if (req !== null) removeInflight(this._upgrade.inflight, req) | |
| 1867 | ||
| 1868 | if (this.core.tree.length === this._upgrade.length && this.core.tree.fork === this._upgrade.fork) return false | |
| 1869 | ||
| 1870 | const u = this._upgrade | |
| 1871 | this._upgrade = null | |
| 1872 | u.resolve(true) | |
| 1873 | ||
| 1874 | return true | |
| 1875 | } | |
| 1876 | ||
| 1877 | _resolveRangeRequest (req, index) { | |
| 1878 | const head = this._ranges.pop() | |
| 1879 | ||
| 1880 | if (index < this._ranges.length) this._ranges[index] = head | |
| 1881 | ||
| 1882 | req.resolve(true) | |
| 1883 | } | |
| 1884 | ||
| 1885 | _clearInflightBlock (tracker, req) { | |
| 1886 | const isBlock = tracker === this._blocks | |
| 1887 | const index = isBlock === true ? req.block.index : req.hash.index / 2 | |
| 1888 | const b = tracker.get(index) | |
| 1889 | ||
| 1890 | if (b === null || removeInflight(b.inflight, req) === false) return | |
| 1891 | ||
| 1892 | if (removeHotswap(b) === true && b.inflight.length > 0) { | |
| 1893 | this.hotswaps.add(b) | |
| 1894 | } | |
| 1895 | ||
| 1896 | if (b.refs.length > 0 && isBlock === true) { | |
| 1897 | this._queueBlock(b) | |
| 1898 | return | |
| 1899 | } | |
| 1900 | ||
| 1901 | b.gc() | |
| 1902 | } | |
| 1903 | ||
| 1904 | _clearInflightUpgrade (req) { | |
| 1905 | if (removeInflight(this._upgrade.inflight, req) === false) return | |
| 1906 | this._upgrade.gc() | |
| 1907 | } | |
| 1908 | ||
| 1909 | _clearInflightSeeks (req) { | |
| 1910 | for (const s of this._seeks) { | |
| 1911 | if (removeInflight(s.inflight, req) === false) continue | |
| 1912 | s.gc() | |
| 1913 | } | |
| 1914 | } | |
| 1915 | ||
| 1916 | _clearInflightReorgs (req) { | |
| 1917 | for (const r of this._reorgs) { | |
| 1918 | removeInflight(r.inflight, req) | |
| 1919 | } | |
| 1920 | } | |
| 1921 | ||
| 1922 | _clearOldReorgs (fork) { | |
| 1923 | for (let i = 0; i < this._reorgs.length; i++) { | |
| 1924 | const f = this._reorgs[i] | |
| 1925 | if (f.fork >= fork) continue | |
| 1926 | if (i === this._reorgs.length - 1) this._reorgs.pop() | |
| 1927 | else this._reorgs[i] = this._reorgs.pop() | |
| 1928 | i-- | |
| 1929 | } | |
| 1930 | } | |
| 1931 | ||
| 1932 | // "slow" updates here - async but not allowed to ever throw | |
| 1933 | async _updateNonPrimary (updateAll) { | |
| 1934 | // Check if running, if so skip it and the running one will issue another update for us (debounce) | |
| 1935 | while (++this._updatesPending === 1) { | |
| 1936 | let len = Math.min(MAX_RANGES, this._ranges.length) | |
| 1937 | ||
| 1938 | for (let i = 0; i < len; i++) { | |
| 1939 | const r = this._ranges[i] | |
| 1940 | ||
| 1941 | clampRange(this.core, r) | |
| 1942 | ||
| 1943 | if (r.end !== -1 && r.start >= r.end) { | |
| 1944 | this._resolveRangeRequest(r, i--) | |
| 1945 | if (len > this._ranges.length) len-- | |
| 1946 | if (this._ranges.length === MAX_RANGES) updateAll = true | |
| 1947 | } | |
| 1948 | } | |
| 1949 | ||
| 1950 | for (let i = 0; i < this._seeks.length; i++) { | |
| 1951 | const s = this._seeks[i] | |
| 1952 | ||
| 1953 | let err = null | |
| 1954 | let res = null | |
| 1955 | ||
| 1956 | try { | |
| 1957 | res = await s.seeker.update() | |
| 1958 | } catch (error) { | |
| 1959 | err = error | |
| 1960 | } | |
| 1961 | ||
| 1962 | if (!res && !err) continue | |
| 1963 | ||
| 1964 | if (i < this._seeks.length - 1) this._seeks[i] = this._seeks.pop() | |
| 1965 | else this._seeks.pop() | |
| 1966 | ||
| 1967 | i-- | |
| 1968 | ||
| 1969 | if (err) s.reject(err) | |
| 1970 | else s.resolve(res) | |
| 1971 | } | |
| 1972 | ||
| 1973 | // No additional updates scheduled - break | |
| 1974 | if (--this._updatesPending === 0) break | |
| 1975 | // Debounce the additional updates - continue | |
| 1976 | this._updatesPending = 0 | |
| 1977 | } | |
| 1978 | ||
| 1979 | if (this._inflight.idle || updateAll) this.updateAll() | |
| 1980 | } | |
| 1981 | ||
| 1982 | _maybeResolveIfAvailableRanges () { | |
| 1983 | if (this._ifAvailable > 0 || !this._inflight.idle || !this._ranges.length) return | |
| 1984 | ||
| 1985 | for (let i = 0; i < this.peers.length; i++) { | |
| 1986 | if (this.peers[i].dataProcessing > 0) return | |
| 1987 | } | |
| 1988 | ||
| 1989 | for (let i = 0; i < this._ranges.length; i++) { | |
| 1990 | const r = this._ranges[i] | |
| 1991 | ||
| 1992 | if (r.ifAvailable) { | |
| 1993 | this._resolveRangeRequest(r, i--) | |
| 1994 | } | |
| 1995 | } | |
| 1996 | } | |
| 1997 | ||
| 1998 | _clearRequest (peer, req) { | |
| 1999 | if (req.block !== null) { | |
| 2000 | this._clearInflightBlock(this._blocks, req) | |
| 2001 | this._unmarkInflight(req.block.index) | |
| 2002 | } | |
| 2003 | ||
| 2004 | if (req.hash !== null) { | |
| 2005 | this._clearInflightBlock(this._hashes, req) | |
| 2006 | } | |
| 2007 | ||
| 2008 | if (req.upgrade !== null && this._upgrade !== null) { | |
| 2009 | this._clearInflightUpgrade(req) | |
| 2010 | } | |
| 2011 | ||
| 2012 | if (this._seeks.length > 0) { | |
| 2013 | this._clearInflightSeeks(req) | |
| 2014 | } | |
| 2015 | ||
| 2016 | if (this._reorgs.length > 0) { | |
| 2017 | this._clearInflightReorgs(req) | |
| 2018 | } | |
| 2019 | } | |
| 2020 | ||
| 2021 | _onnodata (peer, req) { | |
| 2022 | this._clearRequest(peer, req) | |
| 2023 | this.updateAll() | |
| 2024 | } | |
| 2025 | ||
| 2026 | _openSkipBitfield () { | |
| 2027 | // technically the skip bitfield gets bits cleared if .clear() is called | |
| 2028 | // also which might be in inflight also, but that just results in that section being overcalled shortly | |
| 2029 | // worst case, so ok for now | |
| 2030 | ||
| 2031 | const bitfield = this.core.openSkipBitfield() | |
| 2032 | ||
| 2033 | for (const req of this._inflight) { | |
| 2034 | if (req.block) bitfield.set(req.block.index, true) // skip | |
| 2035 | } | |
| 2036 | } | |
| 2037 | ||
| 2038 | _markInflight (index) { | |
| 2039 | if (this.core.skipBitfield !== null) this.core.skipBitfield.set(index, true) | |
| 2040 | for (const peer of this.peers) peer._markInflight(index) | |
| 2041 | } | |
| 2042 | ||
| 2043 | _unmarkInflight (index) { | |
| 2044 | if (this.core.skipBitfield !== null) this.core.skipBitfield.set(index, this.core.bitfield.get(index)) | |
| 2045 | for (const peer of this.peers) peer._resetMissingBlock(index) | |
| 2046 | } | |
| 2047 | ||
| 2048 | _ondata (peer, req, data) { | |
| 2049 | if (data.block !== null) { | |
| 2050 | this._resolveBlockRequest(this._blocks, data.block.index, data.block.value, req) | |
| 2051 | } | |
| 2052 | ||
| 2053 | if (data.hash !== null && (data.hash.index & 1) === 0) { | |
| 2054 | this._resolveBlockRequest(this._hashes, data.hash.index / 2, null, req) | |
| 2055 | } | |
| 2056 | ||
| 2057 | if (this._upgrade !== null) { | |
| 2058 | this._resolveUpgradeRequest(req) | |
| 2059 | } | |
| 2060 | ||
| 2061 | if (this._seeks.length > 0) { | |
| 2062 | this._clearInflightSeeks(req) | |
| 2063 | } | |
| 2064 | ||
| 2065 | if (this._reorgs.length > 0) { | |
| 2066 | this._clearInflightReorgs(req) | |
| 2067 | } | |
| 2068 | ||
| 2069 | if (this._manifestPeer === peer && this.core.header.manifest !== null) { | |
| 2070 | this._manifestPeer = null | |
| 2071 | } | |
| 2072 | ||
| 2073 | if (this._seeks.length > 0 || this._ranges.length > 0) this._updateNonPrimary(this._seeks.length > 0) | |
| 2074 | this.updatePeer(peer) | |
| 2075 | } | |
| 2076 | ||
| 2077 | _onwant (peer, start, length) { | |
| 2078 | const contig = Math.min(this.core.tree.length, this.core.header.hints.contiguousLength) | |
| 2079 | ||
| 2080 | if (start + length < contig || (this.core.tree.length === contig)) { | |
| 2081 | peer.wireRange.send({ | |
| 2082 | drop: false, | |
| 2083 | start: 0, | |
| 2084 | length: contig | |
| 2085 | }) | |
| 2086 | incrementTx(peer.stats.wireRange, this.stats.wireRange) | |
| 2087 | return | |
| 2088 | } | |
| 2089 | ||
| 2090 | length = Math.min(length, this.core.tree.length - start) | |
| 2091 | ||
| 2092 | peer.protomux.cork() | |
| 2093 | ||
| 2094 | for (const msg of this.core.bitfield.want(start, length)) { | |
| 2095 | peer.wireBitfield.send(msg) | |
| 2096 | incrementTx(peer.stats.wireBitfield, this.stats.wireBitfield) | |
| 2097 | } | |
| 2098 | ||
| 2099 | peer.protomux.uncork() | |
| 2100 | } | |
| 2101 | ||
| 2102 | async _onreorgdata (peer, req, data) { | |
| 2103 | const newBatch = data.upgrade && await this.core.verifyReorg(data) | |
| 2104 | const f = this._addReorg(data.fork, peer) | |
| 2105 | ||
| 2106 | if (f === null) { | |
| 2107 | this.updateAll() | |
| 2108 | return | |
| 2109 | } | |
| 2110 | ||
| 2111 | removeInflight(f.inflight, req) | |
| 2112 | ||
| 2113 | if (f.batch) { | |
| 2114 | await f.batch.update(data) | |
| 2115 | } else if (data.upgrade) { | |
| 2116 | f.batch = newBatch | |
| 2117 | ||
| 2118 | // Remove "older" reorgs in progress as we just verified this one. | |
| 2119 | this._clearOldReorgs(f.fork) | |
| 2120 | } | |
| 2121 | ||
| 2122 | if (f.batch && f.batch.finished) { | |
| 2123 | if (this._addUpgradeMaybe() !== null) { | |
| 2124 | await this._applyReorg(f) | |
| 2125 | } | |
| 2126 | } | |
| 2127 | ||
| 2128 | this.updateAll() | |
| 2129 | } | |
| 2130 | ||
| 2131 | // Never throws, allowed to run in the background | |
| 2132 | async _applyReorg (f) { | |
| 2133 | // TODO: more optimal here to check if potentially a better reorg | |
| 2134 | // is available, ie higher fork, and request that one first. | |
| 2135 | // This will request that one after this finishes, which is fine, but we | |
| 2136 | // should investigate the complexity in going the other way | |
| 2137 | ||
| 2138 | const u = this._upgrade | |
| 2139 | ||
| 2140 | this._reorgs = [] // clear all as the nodes are against the old tree - easier | |
| 2141 | this._applyingReorg = this.core.reorg(f.batch, null) // TODO: null should be the first/last peer? | |
| 2142 | ||
| 2143 | try { | |
| 2144 | await this._applyingReorg | |
| 2145 | } catch (err) { | |
| 2146 | this._upgrade = null | |
| 2147 | u.reject(err) | |
| 2148 | } | |
| 2149 | ||
| 2150 | this._applyingReorg = null | |
| 2151 | ||
| 2152 | if (this._upgrade !== null) { | |
| 2153 | this._resolveUpgradeRequest(null) | |
| 2154 | } | |
| 2155 | ||
| 2156 | for (const peer of this.peers) this._updateFork(peer) | |
| 2157 | ||
| 2158 | // TODO: all the remaining is a tmp workaround until we have a flag/way for ANY_FORK | |
| 2159 | for (const r of this._ranges) { | |
| 2160 | r.start = r.userStart | |
| 2161 | r.end = r.userEnd | |
| 2162 | } | |
| 2163 | ||
| 2164 | this.updateAll() | |
| 2165 | } | |
| 2166 | ||
| 2167 | _maybeUpdate () { | |
| 2168 | return this._upgrade !== null && this._upgrade.inflight.length === 0 | |
| 2169 | } | |
| 2170 | ||
| 2171 | _maybeRequestManifest () { | |
| 2172 | return this.core.header.manifest === null && this._manifestPeer === null | |
| 2173 | } | |
| 2174 | ||
| 2175 | _updateFork (peer) { | |
| 2176 | if (this._applyingReorg !== null || this.allowFork === false || peer.remoteFork <= this.core.tree.fork) { | |
| 2177 | return false | |
| 2178 | } | |
| 2179 | ||
| 2180 | const f = this._addReorg(peer.remoteFork, peer) | |
| 2181 | ||
| 2182 | // TODO: one per peer is better | |
| 2183 | if (f !== null && f.batch === null && f.inflight.length === 0) { | |
| 2184 | return peer._requestForkProof(f) | |
| 2185 | } | |
| 2186 | ||
| 2187 | return false | |
| 2188 | } | |
| 2189 | ||
| 2190 | _updateHotswap (peer) { | |
| 2191 | const maxHotswaps = peer.getMaxHotswapInflight() | |
| 2192 | if (!peer.isActive() || peer.inflight >= maxHotswaps) return | |
| 2193 | ||
| 2194 | for (const b of this.hotswaps.pick(peer)) { | |
| 2195 | if (peer._requestBlock(b) === false) continue | |
| 2196 | peer.stats.hotswaps++ | |
| 2197 | peer.replicator.stats.hotswaps++ | |
| 2198 | if (peer.inflight >= maxHotswaps) break | |
| 2199 | } | |
| 2200 | } | |
| 2201 | ||
| 2202 | _updatePeer (peer) { | |
| 2203 | if (!peer.isActive() || peer.inflight >= peer.getMaxInflight()) { | |
| 2204 | return false | |
| 2205 | } | |
| 2206 | ||
| 2207 | // Eagerly request the manifest even if the remote length is 0. If not 0 we'll get as part of the upgrade request... | |
| 2208 | if (this._maybeRequestManifest() === true && peer.remoteLength === 0 && peer.remoteHasManifest === true) { | |
| 2209 | this._manifestPeer = peer | |
| 2210 | peer._requestManifest() | |
| 2211 | } | |
| 2212 | ||
| 2213 | for (const s of this._seeks) { | |
| 2214 | if (s.inflight.length > 0) continue // TODO: one per peer is better | |
| 2215 | if (peer._requestSeek(s) === true) { | |
| 2216 | return true | |
| 2217 | } | |
| 2218 | } | |
| 2219 | ||
| 2220 | // Implied that any block in the queue should be requested, no matter how many inflights | |
| 2221 | const blks = new RandomIterator(this._queued) | |
| 2222 | ||
| 2223 | for (const b of blks) { | |
| 2224 | if (b.queued === false || peer._requestBlock(b) === true) { | |
| 2225 | b.queued = false | |
| 2226 | blks.dequeue() | |
| 2227 | return true | |
| 2228 | } | |
| 2229 | } | |
| 2230 | ||
| 2231 | return false | |
| 2232 | } | |
| 2233 | ||
| 2234 | _updatePeerNonPrimary (peer) { | |
| 2235 | if (!peer.isActive() || peer.inflight >= peer.getMaxInflight()) { | |
| 2236 | return false | |
| 2237 | } | |
| 2238 | ||
| 2239 | const ranges = new RandomIterator(this._ranges) | |
| 2240 | let tried = 0 | |
| 2241 | ||
| 2242 | for (const r of ranges) { | |
| 2243 | if (peer._requestRange(r) === true) { | |
| 2244 | return true | |
| 2245 | } | |
| 2246 | if (++tried >= MAX_RANGES) break | |
| 2247 | } | |
| 2248 | ||
| 2249 | // Iterate from newest fork to oldest fork... | |
| 2250 | for (let i = this._reorgs.length - 1; i >= 0; i--) { | |
| 2251 | const f = this._reorgs[i] | |
| 2252 | if (f.batch !== null && f.inflight.length === 0 && peer._requestForkRange(f) === true) { | |
| 2253 | return true | |
| 2254 | } | |
| 2255 | } | |
| 2256 | ||
| 2257 | if (this._maybeUpdate() === true && peer._requestUpgrade(this._upgrade) === true) { | |
| 2258 | return true | |
| 2259 | } | |
| 2260 | ||
| 2261 | return false | |
| 2262 | } | |
| 2263 | ||
| 2264 | updatePeer (peer) { | |
| 2265 | // Quick shortcut to wait for flushing reorgs - not needed but less waisted requests | |
| 2266 | if (this._applyingReorg !== null) return | |
| 2267 | ||
| 2268 | while (this._updatePeer(peer) === true); | |
| 2269 | while (this._updatePeerNonPrimary(peer) === true); | |
| 2270 | ||
| 2271 | if (this.peers.length > 1 && this._blocks.isEmpty() === false) { | |
| 2272 | this._updateHotswap(peer) | |
| 2273 | } | |
| 2274 | ||
| 2275 | this._checkUpgradeIfAvailable() | |
| 2276 | this._maybeResolveIfAvailableRanges() | |
| 2277 | } | |
| 2278 | ||
| 2279 | updateAll () { | |
| 2280 | // Quick shortcut to wait for flushing reorgs - not needed but less waisted requests | |
| 2281 | if (this._applyingReorg !== null) return | |
| 2282 | ||
| 2283 | const peers = new RandomIterator(this.peers) | |
| 2284 | ||
| 2285 | for (const peer of peers) { | |
| 2286 | if (this._updatePeer(peer) === true) { | |
| 2287 | peers.requeue() | |
| 2288 | } | |
| 2289 | } | |
| 2290 | ||
| 2291 | // Check if we can skip the non primary check fully | |
| 2292 | if (this._maybeUpdate() === false && this._ranges.length === 0 && this._reorgs.length === 0) { | |
| 2293 | this._checkUpgradeIfAvailable() | |
| 2294 | return | |
| 2295 | } | |
| 2296 | ||
| 2297 | for (const peer of peers.restart()) { | |
| 2298 | if (this._updatePeerNonPrimary(peer) === true) { | |
| 2299 | peers.requeue() | |
| 2300 | } | |
| 2301 | } | |
| 2302 | ||
| 2303 | this._checkUpgradeIfAvailable() | |
| 2304 | this._maybeResolveIfAvailableRanges() | |
| 2305 | } | |
| 2306 | ||
| 2307 | _closeSessionMaybe () { | |
| 2308 | if (this._hasSession && this._peerSessions === 0) { | |
| 2309 | this._hasSession = false | |
| 2310 | this.core.active-- | |
| 2311 | } | |
| 2312 | ||
| 2313 | // we were the last active ref, so lets shut things down | |
| 2314 | if (this.core.active === 0 && this.core.sessions.length === 0) { | |
| 2315 | this.destroy() | |
| 2316 | this.core.close().catch(safetyCatch) | |
| 2317 | return | |
| 2318 | } | |
| 2319 | ||
| 2320 | // in case one session is still alive but its been marked for auto close also kill it | |
| 2321 | if (this.core.sessions.length === 1 && this.core.active === 1 && this.core.sessions[0].autoClose) { | |
| 2322 | this.core.sessions[0].close().catch(safetyCatch) | |
| 2323 | } | |
| 2324 | } | |
| 2325 | ||
| 2326 | attached (protomux) { | |
| 2327 | return this._attached.has(protomux) | |
| 2328 | } | |
| 2329 | ||
| 2330 | ensureSession () { | |
| 2331 | if (this._hasSession) return | |
| 2332 | this._hasSession = true | |
| 2333 | this.core.active++ | |
| 2334 | } | |
| 2335 | ||
| 2336 | attachTo (protomux, useSession) { | |
| 2337 | if (this.core.closed) return | |
| 2338 | if (useSession) this.ensureSession() | |
| 2339 | ||
| 2340 | const makePeer = this._makePeer.bind(this, protomux, useSession) | |
| 2341 | ||
| 2342 | this._attached.add(protomux) | |
| 2343 | protomux.pair({ protocol: 'hypercore/alpha', id: this.discoveryKey }, makePeer) | |
| 2344 | protomux.stream.setMaxListeners(0) | |
| 2345 | protomux.stream.on('close', this._onstreamclose) | |
| 2346 | ||
| 2347 | if (useSession) this._peerSessions++ | |
| 2348 | this._ifAvailable++ | |
| 2349 | ||
| 2350 | protomux.stream.opened.then((opened) => { | |
| 2351 | if (useSession) this._peerSessions-- | |
| 2352 | this._ifAvailable-- | |
| 2353 | ||
| 2354 | if (opened && !this.destroyed) makePeer() | |
| 2355 | else if (useSession) this._closeSessionMaybe() | |
| 2356 | this._checkUpgradeIfAvailable() | |
| 2357 | }) | |
| 2358 | } | |
| 2359 | ||
| 2360 | detachFrom (protomux) { | |
| 2361 | if (this._attached.delete(protomux)) { | |
| 2362 | protomux.stream.removeListener('close', this._onstreamclose) | |
| 2363 | protomux.unpair({ protocol: 'hypercore/alpha', id: this.discoveryKey }) | |
| 2364 | } | |
| 2365 | } | |
| 2366 | ||
| 2367 | destroy () { | |
| 2368 | this.destroyed = true | |
| 2369 | ||
| 2370 | if (this._downloadingTimer) { | |
| 2371 | clearTimeout(this._downloadingTimer) | |
| 2372 | this._downloadingTimer = null | |
| 2373 | } | |
| 2374 | ||
| 2375 | const waiting = [] | |
| 2376 | ||
| 2377 | while (this.peers.length) { | |
| 2378 | const peer = this.peers[this.peers.length - 1] | |
| 2379 | this.detachFrom(peer.protomux) | |
| 2380 | peer.channel.close() // peer is removed from array in onclose | |
| 2381 | waiting.push(peer.channel.fullyClosed()) | |
| 2382 | } | |
| 2383 | ||
| 2384 | for (const protomux of this._attached) { | |
| 2385 | this.detachFrom(protomux) | |
| 2386 | } | |
| 2387 | ||
| 2388 | return Promise.all(waiting) | |
| 2389 | } | |
| 2390 | ||
| 2391 | _makePeer (protomux, useSession) { | |
| 2392 | const replicator = this | |
| 2393 | if (protomux.opened({ protocol: 'hypercore/alpha', id: this.discoveryKey })) return onnochannel() | |
| 2394 | ||
| 2395 | const channel = protomux.createChannel({ | |
| 2396 | userData: null, | |
| 2397 | protocol: 'hypercore/alpha', | |
| 2398 | aliases: ['hypercore'], | |
| 2399 | id: this.discoveryKey, | |
| 2400 | handshake: m.wire.handshake, | |
| 2401 | messages: [ | |
| 2402 | { encoding: m.wire.sync, onmessage: onwiresync }, | |
| 2403 | { encoding: m.wire.request, onmessage: onwirerequest }, | |
| 2404 | { encoding: m.wire.cancel, onmessage: onwirecancel }, | |
| 2405 | { encoding: m.wire.data, onmessage: onwiredata }, | |
| 2406 | { encoding: m.wire.noData, onmessage: onwirenodata }, | |
| 2407 | { encoding: m.wire.want, onmessage: onwirewant }, | |
| 2408 | { encoding: m.wire.unwant, onmessage: onwireunwant }, | |
| 2409 | { encoding: m.wire.bitfield, onmessage: onwirebitfield }, | |
| 2410 | { encoding: m.wire.range, onmessage: onwirerange }, | |
| 2411 | { encoding: m.wire.extension, onmessage: onwireextension } | |
| 2412 | ], | |
| 2413 | onopen: onwireopen, | |
| 2414 | onclose: onwireclose, | |
| 2415 | ondrain: onwiredrain | |
| 2416 | }) | |
| 2417 | ||
| 2418 | if (channel === null) return onnochannel() | |
| 2419 | ||
| 2420 | const peer = new Peer(replicator, protomux, channel, useSession, this.inflightRange) | |
| 2421 | const stream = protomux.stream | |
| 2422 | ||
| 2423 | if (useSession) { | |
| 2424 | // session may have been unref'd underneath us | |
| 2425 | replicator.ensureSession() | |
| 2426 | replicator._peerSessions++ | |
| 2427 | } | |
| 2428 | ||
| 2429 | peer.channel.open({ | |
| 2430 | seeks: true, | |
| 2431 | capability: caps.replicate(stream.isInitiator, this.key, stream.handshakeHash) | |
| 2432 | }) | |
| 2433 | ||
| 2434 | return true | |
| 2435 | ||
| 2436 | function onnochannel () { | |
| 2437 | if (useSession) replicator._closeSessionMaybe() | |
| 2438 | return false | |
| 2439 | } | |
| 2440 | } | |
| 2441 | } | |
| 2442 | ||
| 2443 | function matchingRequest (req, data) { | |
| 2444 | if (data.block !== null && (req.block === null || req.block.index !== data.block.index)) return false | |
| 2445 | if (data.hash !== null && (req.hash === null || req.hash.index !== data.hash.index)) return false | |
| 2446 | if (data.seek !== null && (req.seek === null || req.seek.bytes !== data.seek.bytes)) return false | |
| 2447 | if (data.upgrade !== null && req.upgrade === null) return false | |
| 2448 | return req.fork === data.fork | |
| 2449 | } | |
| 2450 | ||
| 2451 | function removeHotswap (block) { | |
| 2452 | if (block.hotswap === null) return false | |
| 2453 | block.hotswap.ref.remove(block) | |
| 2454 | return true | |
| 2455 | } | |
| 2456 | ||
| 2457 | function removeInflight (inf, req) { | |
| 2458 | const i = inf.indexOf(req) | |
| 2459 | if (i === -1) return false | |
| 2460 | if (i < inf.length - 1) inf[i] = inf.pop() | |
| 2461 | else inf.pop() | |
| 2462 | return true | |
| 2463 | } | |
| 2464 | ||
| 2465 | function noop () {} | |
| 2466 | ||
| 2467 | function toLength (start, end) { | |
| 2468 | return end === -1 ? -1 : (end < start ? 0 : end - start) | |
| 2469 | } | |
| 2470 | ||
| 2471 | function clampRange (core, r) { | |
| 2472 | if (r.blocks === null) { | |
| 2473 | const start = core.bitfield.firstUnset(r.start) | |
| 2474 | ||
| 2475 | if (r.end === -1) r.start = start === -1 ? core.tree.length : start | |
| 2476 | else if (start === -1 || start >= r.end) r.start = r.end | |
| 2477 | else { | |
| 2478 | r.start = start | |
| 2479 | ||
| 2480 | const end = core.bitfield.lastUnset(r.end - 1) | |
| 2481 | ||
| 2482 | if (end === -1 || start >= end + 1) r.end = r.start | |
| 2483 | else r.end = end + 1 | |
| 2484 | } | |
| 2485 | } else { | |
| 2486 | while (r.start < r.end && core.bitfield.get(r.blocks[r.start])) r.start++ | |
| 2487 | while (r.start < r.end && core.bitfield.get(r.blocks[r.end - 1])) r.end-- | |
| 2488 | } | |
| 2489 | } | |
| 2490 | ||
| 2491 | function onrequesttimeout (req) { | |
| 2492 | if (req.context) req.context.detach(req, REQUEST_TIMEOUT()) | |
| 2493 | } | |
| 2494 | ||
| 2495 | function destroyRequestTimeout (req) { | |
| 2496 | if (req.timeout !== null) { | |
| 2497 | clearTimeout(req.timeout) | |
| 2498 | req.timeout = null | |
| 2499 | } | |
| 2500 | } | |
| 2501 | ||
| 2502 | function isCriticalError (err) { | |
| 2503 | // TODO: expose .critical or similar on the hypercore errors that are critical (if all not are) | |
| 2504 | return err.name === 'HypercoreError' | |
| 2505 | } | |
| 2506 | ||
| 2507 | function onwireopen (m, c) { | |
| 2508 | return c.userData.onopen(m) | |
| 2509 | } | |
| 2510 | ||
| 2511 | function onwireclose (isRemote, c) { | |
| 2512 | return c.userData.onclose(isRemote) | |
| 2513 | } | |
| 2514 | ||
| 2515 | function onwiredrain (c) { | |
| 2516 | return c.userData.ondrain() | |
| 2517 | } | |
| 2518 | ||
| 2519 | function onwiresync (m, c) { | |
| 2520 | incrementRx(c.userData.stats.wireSync, c.userData.replicator.stats.wireSync) | |
| 2521 | return c.userData.onsync(m) | |
| 2522 | } | |
| 2523 | ||
| 2524 | function onwirerequest (m, c) { | |
| 2525 | incrementRx(c.userData.stats.wireRequest, c.userData.replicator.stats.wireRequest) | |
| 2526 | return c.userData.onrequest(m) | |
| 2527 | } | |
| 2528 | ||
| 2529 | function onwirecancel (m, c) { | |
| 2530 | incrementRx(c.userData.stats.wireCancel, c.userData.replicator.stats.wireCancel) | |
| 2531 | return c.userData.oncancel(m) | |
| 2532 | } | |
| 2533 | ||
| 2534 | function onwiredata (m, c) { | |
| 2535 | incrementRx(c.userData.stats.wireData, c.userData.replicator.stats.wireData) | |
| 2536 | return c.userData.ondata(m) | |
| 2537 | } | |
| 2538 | ||
| 2539 | function onwirenodata (m, c) { | |
| 2540 | return c.userData.onnodata(m) | |
| 2541 | } | |
| 2542 | ||
| 2543 | function onwirewant (m, c) { | |
| 2544 | incrementRx(c.userData.stats.wireWant, c.userData.replicator.stats.wireWant) | |
| 2545 | return c.userData.onwant(m) | |
| 2546 | } | |
| 2547 | ||
| 2548 | function onwireunwant (m, c) { | |
| 2549 | return c.userData.onunwant(m) | |
| 2550 | } | |
| 2551 | ||
| 2552 | function onwirebitfield (m, c) { | |
| 2553 | incrementRx(c.userData.stats.wireBitfield, c.userData.replicator.stats.wireBitfield) | |
| 2554 | return c.userData.onbitfield(m) | |
| 2555 | } | |
| 2556 | ||
| 2557 | function onwirerange (m, c) { | |
| 2558 | incrementRx(c.userData.stats.wireRange, c.userData.replicator.stats.wireRange) | |
| 2559 | return c.userData.onrange(m) | |
| 2560 | } | |
| 2561 | ||
| 2562 | function onwireextension (m, c) { | |
| 2563 | incrementRx(c.userData.stats.wireExtension, c.userData.replicator.stats.wireExtension) | |
| 2564 | return c.userData.onextension(m) | |
| 2565 | } | |
| 2566 | ||
| 2567 | function setDownloadingLater (repl, downloading, session) { | |
| 2568 | repl.setDownloadingNow(downloading, session) | |
| 2569 | } | |
| 2570 | ||
| 2571 | function isBlockRequest (req) { | |
| 2572 | return req !== null && req.block !== null | |
| 2573 | } | |
| 2574 | ||
| 2575 | function isUpgradeRequest (req) { | |
| 2576 | return req !== null && req.upgrade !== null | |
| 2577 | } | |
| 2578 | ||
| 2579 | function incrementTx (stats1, stats2) { | |
| 2580 | stats1.tx++ | |
| 2581 | stats2.tx++ | |
| 2582 | } | |
| 2583 | ||
| 2584 | function incrementRx (stats1, stats2) { | |
| 2585 | stats1.rx++ | |
| 2586 | stats2.rx++ | |
| 2587 | } |
| … | … | @@ -0,0 +1,130 @@ |
| 1 | const { Writable, Readable } = require('streamx') | |
| 2 | ||
| 3 | class ReadStream extends Readable { | |
| 4 | constructor (core, opts = {}) { | |
| 5 | super() | |
| 6 | ||
| 7 | this.core = core | |
| 8 | this.start = opts.start || 0 | |
| 9 | this.end = typeof opts.end === 'number' ? opts.end : -1 | |
| 10 | this.snapshot = !opts.live && opts.snapshot !== false | |
| 11 | this.live = !!opts.live | |
| 12 | } | |
| 13 | ||
| 14 | _open (cb) { | |
| 15 | this._openP().then(cb, cb) | |
| 16 | } | |
| 17 | ||
| 18 | _read (cb) { | |
| 19 | this._readP().then(cb, cb) | |
| 20 | } | |
| 21 | ||
| 22 | async _openP () { | |
| 23 | if (this.end === -1) await this.core.update() | |
| 24 | else await this.core.ready() | |
| 25 | if (this.snapshot && this.end === -1) this.end = this.core.length | |
| 26 | } | |
| 27 | ||
| 28 | async _readP () { | |
| 29 | const end = this.live ? -1 : (this.end === -1 ? this.core.length : this.end) | |
| 30 | if (end >= 0 && this.start >= end) { | |
| 31 | this.push(null) | |
| 32 | return | |
| 33 | } | |
| 34 | ||
| 35 | this.push(await this.core.get(this.start++)) | |
| 36 | } | |
| 37 | } | |
| 38 | ||
| 39 | exports.ReadStream = ReadStream | |
| 40 | ||
| 41 | class WriteStream extends Writable { | |
| 42 | constructor (core) { | |
| 43 | super() | |
| 44 | this.core = core | |
| 45 | } | |
| 46 | ||
| 47 | _writev (batch, cb) { | |
| 48 | this._writevP(batch).then(cb, cb) | |
| 49 | } | |
| 50 | ||
| 51 | async _writevP (batch) { | |
| 52 | await this.core.append(batch) | |
| 53 | } | |
| 54 | } | |
| 55 | ||
| 56 | exports.WriteStream = WriteStream | |
| 57 | ||
| 58 | class ByteStream extends Readable { | |
| 59 | constructor (core, opts = {}) { | |
| 60 | super() | |
| 61 | ||
| 62 | this._core = core | |
| 63 | this._index = 0 | |
| 64 | this._range = null | |
| 65 | ||
| 66 | this._byteOffset = opts.byteOffset || 0 | |
| 67 | this._byteLength = typeof opts.byteLength === 'number' ? opts.byteLength : -1 | |
| 68 | this._prefetch = typeof opts.prefetch === 'number' ? opts.prefetch : 32 | |
| 69 | ||
| 70 | this._applyOffset = this._byteOffset > 0 | |
| 71 | } | |
| 72 | ||
| 73 | _open (cb) { | |
| 74 | this._openp().then(cb, cb) | |
| 75 | } | |
| 76 | ||
| 77 | _read (cb) { | |
| 78 | this._readp().then(cb, cb) | |
| 79 | } | |
| 80 | ||
| 81 | async _openp () { | |
| 82 | if (this._byteLength === -1) { | |
| 83 | await this._core.update() | |
| 84 | this._byteLength = Math.max(this._core.byteLength - this._byteOffset, 0) | |
| 85 | } | |
| 86 | } | |
| 87 | ||
| 88 | async _readp () { | |
| 89 | let data = null | |
| 90 | ||
| 91 | if (this._byteLength === 0) { | |
| 92 | this.push(null) | |
| 93 | return | |
| 94 | } | |
| 95 | ||
| 96 | let relativeOffset = 0 | |
| 97 | ||
| 98 | if (this._applyOffset) { | |
| 99 | this._applyOffset = false | |
| 100 | ||
| 101 | const [block, byteOffset] = await this._core.seek(this._byteOffset) | |
| 102 | ||
| 103 | this._index = block | |
| 104 | relativeOffset = byteOffset | |
| 105 | } | |
| 106 | ||
| 107 | this._predownload(this._index + 1) | |
| 108 | data = await this._core.get(this._index++, { valueEncoding: 'binary' }) | |
| 109 | ||
| 110 | if (relativeOffset > 0) data = data.subarray(relativeOffset) | |
| 111 | ||
| 112 | if (data.byteLength > this._byteLength) data = data.subarray(0, this._byteLength) | |
| 113 | this._byteLength -= data.byteLength | |
| 114 | ||
| 115 | this.push(data) | |
| 116 | if (this._byteLength === 0) this.push(null) | |
| 117 | } | |
| 118 | ||
| 119 | _predownload (index) { | |
| 120 | if (this._range) this._range.destroy() | |
| 121 | this._range = this._core.download({ start: index, end: index + this._prefetch, linear: true }) | |
| 122 | } | |
| 123 | ||
| 124 | _destroy (cb) { | |
| 125 | if (this._range) this._range.destroy() | |
| 126 | cb(null) | |
| 127 | } | |
| 128 | } | |
| 129 | ||
| 130 | exports.ByteStream = ByteStream |
| … | … | @@ -0,0 +1,305 @@ |
| 1 | const defaultCrypto = require('hypercore-crypto') | |
| 2 | const b4a = require('b4a') | |
| 3 | const c = require('compact-encoding') | |
| 4 | const flat = require('flat-tree') | |
| 5 | const { BAD_ARGUMENT } = require('hypercore-errors') | |
| 6 | const unslab = require('unslab') | |
| 7 | ||
| 8 | const m = require('./messages') | |
| 9 | const multisig = require('./multisig') | |
| 10 | const caps = require('./caps') | |
| 11 | ||
| 12 | class Signer { | |
| 13 | constructor (crypto, manifestHash, version, index, { signature = 'ed25519', publicKey, namespace = caps.DEFAULT_NAMESPACE } = {}) { | |
| 14 | if (!publicKey) throw BAD_ARGUMENT('public key is required for a signer') | |
| 15 | if (signature !== 'ed25519') throw BAD_ARGUMENT('Only Ed25519 signatures are supported') | |
| 16 | ||
| 17 | this.crypto = crypto | |
| 18 | this.manifestHash = manifestHash | |
| 19 | this.version = version | |
| 20 | this.signer = index | |
| 21 | this.signature = signature | |
| 22 | this.publicKey = publicKey | |
| 23 | this.namespace = namespace | |
| 24 | } | |
| 25 | ||
| 26 | _ctx () { | |
| 27 | return this.version === 0 ? this.namespace : this.manifestHash | |
| 28 | } | |
| 29 | ||
| 30 | verify (batch, signature) { | |
| 31 | return this.crypto.verify(batch.signable(this._ctx()), signature, this.publicKey) | |
| 32 | } | |
| 33 | ||
| 34 | sign (batch, keyPair) { | |
| 35 | return this.crypto.sign(batch.signable(this._ctx()), keyPair.secretKey) | |
| 36 | } | |
| 37 | } | |
| 38 | ||
| 39 | class CompatSigner extends Signer { | |
| 40 | constructor (crypto, index, signer, legacy) { | |
| 41 | super(crypto, null, 0, index, signer) | |
| 42 | this.legacy = legacy | |
| 43 | } | |
| 44 | ||
| 45 | verify (batch, signature) { | |
| 46 | return this.crypto.verify(batch.signableCompat(this.legacy), signature, this.publicKey) | |
| 47 | } | |
| 48 | ||
| 49 | sign (batch, keyPair) { | |
| 50 | return this.crypto.sign(batch.signableCompat(this.legacy), keyPair.secretKey) | |
| 51 | } | |
| 52 | } | |
| 53 | ||
| 54 | module.exports = class Verifier { | |
| 55 | constructor (manifestHash, manifest, { compat = isCompat(manifestHash, manifest), crypto = defaultCrypto, legacy = false } = {}) { | |
| 56 | const self = this | |
| 57 | ||
| 58 | this.manifestHash = manifestHash | |
| 59 | this.compat = compat || manifest === null | |
| 60 | this.version = this.compat ? 0 : typeof manifest.version === 'number' ? manifest.version : 1 | |
| 61 | this.hash = manifest.hash || 'blake2b' | |
| 62 | this.allowPatch = !this.compat && !!manifest.allowPatch | |
| 63 | this.quorum = this.compat ? 1 : defaultQuorum(manifest) | |
| 64 | ||
| 65 | this.signers = manifest.signers ? manifest.signers.map(createSigner) : [] | |
| 66 | this.prologue = this.compat ? null : (manifest.prologue || null) | |
| 67 | ||
| 68 | function createSigner (signer, index) { | |
| 69 | return self.compat | |
| 70 | ? new CompatSigner(crypto, index, signer, legacy) | |
| 71 | : new Signer(crypto, manifestHash, self.version, index, signer) | |
| 72 | } | |
| 73 | } | |
| 74 | ||
| 75 | _verifyCompat (batch, signature) { | |
| 76 | if (!signature) return false | |
| 77 | ||
| 78 | if (this.compat || (!this.allowPatch && this.signers.length === 1)) { | |
| 79 | return !!signature && this.signers[0].verify(batch, signature) | |
| 80 | } | |
| 81 | ||
| 82 | return this._verifyMulti(batch, signature) | |
| 83 | } | |
| 84 | ||
| 85 | _inflate (signature) { | |
| 86 | if (this.version >= 1) return multisig.inflate(signature) | |
| 87 | const { proofs, patch } = multisig.inflatev0(signature) | |
| 88 | ||
| 89 | return { | |
| 90 | proofs: proofs.map(proofToVersion1), | |
| 91 | patch | |
| 92 | } | |
| 93 | } | |
| 94 | ||
| 95 | _verifyMulti (batch, signature) { | |
| 96 | if (!signature || this.quorum === 0) return false | |
| 97 | ||
| 98 | const { proofs, patch } = this._inflate(signature) | |
| 99 | if (proofs.length < this.quorum) return false | |
| 100 | ||
| 101 | const tried = new Uint8Array(this.signers.length) | |
| 102 | const nodes = this.allowPatch && patch.length ? toMap(patch) : null | |
| 103 | ||
| 104 | for (let i = 0; i < this.quorum; i++) { | |
| 105 | const inp = proofs[i] | |
| 106 | ||
| 107 | let tree = batch | |
| 108 | ||
| 109 | if (inp.patch && this.allowPatch) { | |
| 110 | tree = batch.clone() | |
| 111 | ||
| 112 | const upgrade = generateUpgrade(nodes, batch.length, inp.patch) | |
| 113 | const proof = { fork: tree.fork, block: null, hash: null, seek: null, upgrade, manifest: null } | |
| 114 | ||
| 115 | try { | |
| 116 | if (!tree.verifyUpgrade(proof)) return false | |
| 117 | } catch { | |
| 118 | return false | |
| 119 | } | |
| 120 | } | |
| 121 | ||
| 122 | if (inp.signer >= this.signers.length || tried[inp.signer]) return false | |
| 123 | tried[inp.signer] = 1 | |
| 124 | ||
| 125 | const s = this.signers[inp.signer] | |
| 126 | if (!s.verify(tree, inp.signature)) return false | |
| 127 | } | |
| 128 | ||
| 129 | return true | |
| 130 | } | |
| 131 | ||
| 132 | verify (batch, signature) { | |
| 133 | if (this.version !== 1) { | |
| 134 | return this._verifyCompat(batch, signature) | |
| 135 | } | |
| 136 | ||
| 137 | if (this.prologue !== null && batch.length <= this.prologue.length) { | |
| 138 | return batch.length === this.prologue.length && b4a.equals(batch.hash(), this.prologue.hash) | |
| 139 | } | |
| 140 | ||
| 141 | return this._verifyMulti(batch, signature) | |
| 142 | } | |
| 143 | ||
| 144 | // TODO: better api for this that is more ... multisig-ey | |
| 145 | sign (batch, keyPair) { | |
| 146 | if (!keyPair || !keyPair.secretKey) throw BAD_ARGUMENT('No key pair was passed') | |
| 147 | ||
| 148 | for (const s of this.signers) { | |
| 149 | if (b4a.equals(s.publicKey, keyPair.publicKey)) { | |
| 150 | const signature = s.sign(batch, keyPair) | |
| 151 | if (this.signers.length !== 1 || this.version === 0) return signature | |
| 152 | return this.assemble([{ signer: 0, signature, patch: 0, nodes: null }]) | |
| 153 | } | |
| 154 | } | |
| 155 | ||
| 156 | throw BAD_ARGUMENT('Public key is not a declared signer') | |
| 157 | } | |
| 158 | ||
| 159 | assemble (inputs) { | |
| 160 | return this.version === 0 ? multisig.assemblev0(inputs) : multisig.assemble(inputs) | |
| 161 | } | |
| 162 | ||
| 163 | static manifestHash (manifest) { | |
| 164 | return manifestHash(manifest) | |
| 165 | } | |
| 166 | ||
| 167 | static defaultSignerManifest (publicKey) { | |
| 168 | return { | |
| 169 | version: 1, | |
| 170 | hash: 'blake2b', | |
| 171 | allowPatch: false, | |
| 172 | quorum: 1, | |
| 173 | signers: [{ | |
| 174 | signature: 'ed25519', | |
| 175 | namespace: caps.DEFAULT_NAMESPACE, | |
| 176 | publicKey | |
| 177 | }], | |
| 178 | prologue: null | |
| 179 | } | |
| 180 | } | |
| 181 | ||
| 182 | static fromManifest (manifest, opts) { | |
| 183 | const m = this.createManifest(manifest) | |
| 184 | return new this(manifestHash(m), m, opts) | |
| 185 | } | |
| 186 | ||
| 187 | static createManifest (inp) { | |
| 188 | if (!inp) return null | |
| 189 | ||
| 190 | const manifest = { | |
| 191 | version: typeof inp.version === 'number' ? inp.version : 1, | |
| 192 | hash: 'blake2b', | |
| 193 | allowPatch: !!inp.allowPatch, | |
| 194 | quorum: defaultQuorum(inp), | |
| 195 | signers: inp.signers ? inp.signers.map(parseSigner) : [], | |
| 196 | prologue: null | |
| 197 | } | |
| 198 | ||
| 199 | if (inp.hash && inp.hash !== 'blake2b') throw BAD_ARGUMENT('Only Blake2b hashes are supported') | |
| 200 | ||
| 201 | if (inp.prologue) { | |
| 202 | if (!(b4a.isBuffer(inp.prologue.hash) && inp.prologue.hash.byteLength === 32) || !(inp.prologue.length >= 0)) { | |
| 203 | throw BAD_ARGUMENT('Invalid prologue') | |
| 204 | } | |
| 205 | ||
| 206 | manifest.prologue = inp.prologue | |
| 207 | manifest.prologue.hash = unslab(manifest.prologue.hash) | |
| 208 | } | |
| 209 | ||
| 210 | return manifest | |
| 211 | } | |
| 212 | ||
| 213 | static isValidManifest (key, manifest) { | |
| 214 | return b4a.equals(key, manifestHash(manifest)) | |
| 215 | } | |
| 216 | ||
| 217 | static isCompat (key, manifest) { | |
| 218 | return isCompat(key, manifest) | |
| 219 | } | |
| 220 | ||
| 221 | static sign (manifest, batch, keyPair, opts) { | |
| 222 | return Verifier.fromManifest(manifest, opts).sign(batch, keyPair) | |
| 223 | } | |
| 224 | } | |
| 225 | ||
| 226 | function toMap (nodes) { | |
| 227 | const m = new Map() | |
| 228 | for (const node of nodes) m.set(node.index, node) | |
| 229 | return m | |
| 230 | } | |
| 231 | ||
| 232 | function isCompat (key, manifest) { | |
| 233 | return !!(manifest && manifest.signers.length === 1 && b4a.equals(key, manifest.signers[0].publicKey)) | |
| 234 | } | |
| 235 | ||
| 236 | function defaultQuorum (man) { | |
| 237 | if (typeof man.quorum === 'number') return man.quorum | |
| 238 | if (!man.signers || !man.signers.length) return 0 | |
| 239 | return (man.signers.length >> 1) + 1 | |
| 240 | } | |
| 241 | ||
| 242 | function generateUpgrade (patch, start, length) { | |
| 243 | const upgrade = { start, length, nodes: null, additionalNodes: [], signature: null } | |
| 244 | ||
| 245 | const from = start * 2 | |
| 246 | const to = from + length * 2 | |
| 247 | ||
| 248 | for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) { | |
| 249 | if (ite.index + ite.factor / 2 < from) continue | |
| 250 | ||
| 251 | if (upgrade.nodes === null && ite.contains(from - 2)) { | |
| 252 | upgrade.nodes = [] | |
| 253 | ||
| 254 | const root = ite.index | |
| 255 | const target = from - 2 | |
| 256 | ||
| 257 | ite.seek(target) | |
| 258 | ||
| 259 | while (ite.index !== root) { | |
| 260 | ite.sibling() | |
| 261 | if (ite.index > target) upgrade.nodes.push(patch.get(ite.index)) | |
| 262 | ite.parent() | |
| 263 | } | |
| 264 | ||
| 265 | continue | |
| 266 | } | |
| 267 | ||
| 268 | if (upgrade.nodes === null) upgrade.nodes = [] | |
| 269 | upgrade.nodes.push(patch.get(ite.index)) | |
| 270 | } | |
| 271 | ||
| 272 | if (upgrade.nodes === null) upgrade.nodes = [] | |
| 273 | return upgrade | |
| 274 | } | |
| 275 | ||
| 276 | function parseSigner (signer) { | |
| 277 | validateSigner(signer) | |
| 278 | return { | |
| 279 | signature: 'ed25519', | |
| 280 | namespace: unslab(signer.namespace || caps.DEFAULT_NAMESPACE), | |
| 281 | publicKey: unslab(signer.publicKey) | |
| 282 | } | |
| 283 | } | |
| 284 | ||
| 285 | function validateSigner (signer) { | |
| 286 | if (!signer || !signer.publicKey) throw BAD_ARGUMENT('Signer missing public key') | |
| 287 | if (signer.signature && signer.signature !== 'ed25519') throw BAD_ARGUMENT('Only Ed25519 signatures are supported') | |
| 288 | } | |
| 289 | ||
| 290 | function manifestHash (manifest) { | |
| 291 | const state = { start: 0, end: 32, buffer: null } | |
| 292 | m.manifest.preencode(state, manifest) | |
| 293 | state.buffer = b4a.allocUnsafe(state.end) | |
| 294 | c.raw.encode(state, caps.MANIFEST) | |
| 295 | m.manifest.encode(state, manifest) | |
| 296 | return defaultCrypto.hash(state.buffer) | |
| 297 | } | |
| 298 | ||
| 299 | function proofToVersion1 (proof) { | |
| 300 | return { | |
| 301 | signer: proof.signer, | |
| 302 | signature: proof.signature, | |
| 303 | patch: proof.patch ? proof.patch.length : 0 | |
| 304 | } | |
| 305 | } |
| … | … | @@ -0,0 +1,2 @@ |
| 1 | // explicitly exposed as hypercore/messages | |
| 2 | module.exports = require('./lib/messages') |
| … | … | @@ -0,0 +1,81 @@ |
| 1 | { | |
| 2 | "name": "hypercore", | |
| 3 | "version": "10.38.2", | |
| 4 | "description": "Hypercore is a secure, distributed append-only log", | |
| 5 | "main": "index.js", | |
| 6 | "scripts": { | |
| 7 | "test": "standard && node test/all.js", | |
| 8 | "test:generate": "brittle -r test/all.js test/*.js" | |
| 9 | }, | |
| 10 | "repository": { | |
| 11 | "type": "git", | |
| 12 | "url": "git+https://github.com/holepunchto/hypercore.git" | |
| 13 | }, | |
| 14 | "contributors": [ | |
| 15 | { | |
| 16 | "name": "Mathias Buus", | |
| 17 | "email": "mathiasbuus@gmail.com", | |
| 18 | "url": "https://mafinto.sh" | |
| 19 | }, | |
| 20 | { | |
| 21 | "name": "Andrew Osheroff", | |
| 22 | "email": "andrewosh@gmail.com", | |
| 23 | "url": "https://andrewosh.com" | |
| 24 | } | |
| 25 | ], | |
| 26 | "license": "MIT", | |
| 27 | "bugs": { | |
| 28 | "url": "https://github.com/holepunchto/hypercore/issues" | |
| 29 | }, | |
| 30 | "homepage": "https://github.com/holepunchto/hypercore#readme", | |
| 31 | "files": [ | |
| 32 | "index.js", | |
| 33 | "errors.js", | |
| 34 | "messages.js", | |
| 35 | "lib/**.js" | |
| 36 | ], | |
| 37 | "imports": { | |
| 38 | "events": { | |
| 39 | "bare": "bare-events", | |
| 40 | "default": "events" | |
| 41 | } | |
| 42 | }, | |
| 43 | "dependencies": { | |
| 44 | "@hyperswarm/secret-stream": "^6.0.0", | |
| 45 | "b4a": "^1.1.0", | |
| 46 | "bare-events": "^2.2.0", | |
| 47 | "big-sparse-array": "^1.0.3", | |
| 48 | "compact-encoding": "^2.11.0", | |
| 49 | "crc-universal": "^1.0.2", | |
| 50 | "fast-fifo": "^1.3.0", | |
| 51 | "flat-tree": "^1.9.0", | |
| 52 | "hypercore-crypto": "^3.2.1", | |
| 53 | "hypercore-errors": "^1.2.0", | |
| 54 | "hypercore-id-encoding": "^1.2.0", | |
| 55 | "is-options": "^1.0.1", | |
| 56 | "protomux": "^3.5.0", | |
| 57 | "quickbit-universal": "^2.2.0", | |
| 58 | "random-access-file": "^4.0.0", | |
| 59 | "random-array-iterator": "^1.0.0", | |
| 60 | "safety-catch": "^1.0.1", | |
| 61 | "sodium-universal": "^4.0.0", | |
| 62 | "streamx": "^2.12.4", | |
| 63 | "unslab": "^1.3.0", | |
| 64 | "xache": "^1.1.0", | |
| 65 | "z32": "^1.0.0" | |
| 66 | }, | |
| 67 | "devDependencies": { | |
| 68 | "brittle": "^3.0.0", | |
| 69 | "debugging-stream": "^3.1.0", | |
| 70 | "hyperswarm": "^4.3.6", | |
| 71 | "rache": "^1.0.0", | |
| 72 | "random-access-memory": "^6.1.0", | |
| 73 | "random-access-memory-overlay": "^3.0.0", | |
| 74 | "range-parser": "^1.2.1", | |
| 75 | "speedometer": "^1.1.0", | |
| 76 | "standard": "^17.0.0", | |
| 77 | "test-tmp": "^1.0.2", | |
| 78 | "tiny-byte-size": "^1.1.0", | |
| 79 | "udx-native": "^1.6.1" | |
| 80 | } | |
| 81 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,399 @@ |
| 1 | # Hyperdrive | |
| 2 | ||
| 3 | [See API docs at docs.holepunch.to](https://docs.holepunch.to/building-blocks/hyperdrive) | |
| 4 | ||
| 5 | Hyperdrive is a secure, real-time distributed file system | |
| 6 | ||
| 7 | ## Install | |
| 8 | ||
| 9 | ```sh | |
| 10 | npm install hyperdrive | |
| 11 | ``` | |
| 12 | ||
| 13 | ## Usage | |
| 14 | ||
| 15 | ```js | |
| 16 | const Hyperdrive = require('hyperdrive') | |
| 17 | const Corestore = require('corestore') | |
| 18 | ||
| 19 | const store = new Corestore('./storage') | |
| 20 | const drive = new Hyperdrive(store) | |
| 21 | ||
| 22 | await drive.put('/blob.txt', Buffer.from('example')) | |
| 23 | await drive.put('/images/logo.png', Buffer.from('..')) | |
| 24 | await drive.put('/images/old-logo.png', Buffer.from('..')) | |
| 25 | ||
| 26 | const buffer = await drive.get('/blob.txt') | |
| 27 | console.log(buffer) // => <Buffer ..> "example" | |
| 28 | ||
| 29 | const entry = await drive.entry('/blob.txt') | |
| 30 | console.log(entry) // => { seq, key, value: { executable, linkname, blob, metadata } } | |
| 31 | ||
| 32 | await drive.del('/images/old-logo.png') | |
| 33 | ||
| 34 | await drive.symlink('/images/logo.shortcut', '/images/logo.png') | |
| 35 | ||
| 36 | for await (const file of drive.list('/images')) { | |
| 37 | console.log('list', file) // => { key, value } | |
| 38 | } | |
| 39 | ||
| 40 | const rs = drive.createReadStream('/blob.txt') | |
| 41 | for await (const chunk of rs) { | |
| 42 | console.log('rs', chunk) // => <Buffer ..> | |
| 43 | } | |
| 44 | ||
| 45 | const ws = drive.createWriteStream('/blob.txt') | |
| 46 | ws.write('new example') | |
| 47 | ws.end() | |
| 48 | ws.once('close', () => console.log('file saved')) | |
| 49 | ``` | |
| 50 | ||
| 51 | ## API | |
| 52 | ||
| 53 | #### `const drive = new Hyperdrive(store, [key])` | |
| 54 | ||
| 55 | Creates a new Hyperdrive instance. `store` must be an instance of `Corestore`. | |
| 56 | ||
| 57 | By default it uses the core at `{ name: 'db' }` from `store`, unless you set the public `key`. | |
| 58 | ||
| 59 | #### `await drive.ready()` | |
| 60 | ||
| 61 | Waits until internal state is loaded. | |
| 62 | ||
| 63 | Use it once before reading synchronous properties like `drive.discoveryKey`, unless you called any of the other APIs. | |
| 64 | ||
| 65 | #### `await drive.close()` | |
| 66 | ||
| 67 | Fully close this drive, including its underlying Hypercore backed datastructures. | |
| 68 | ||
| 69 | #### `drive.corestore` | |
| 70 | ||
| 71 | The Corestore instance used as storage. | |
| 72 | ||
| 73 | #### `drive.db` | |
| 74 | ||
| 75 | The underlying Hyperbee backing the drive file structure. | |
| 76 | ||
| 77 | #### `drive.core` | |
| 78 | ||
| 79 | The Hypercore used for `drive.db`. | |
| 80 | ||
| 81 | #### `drive.id` | |
| 82 | ||
| 83 | String containing the id (z-base-32 of the public key) identifying this drive. | |
| 84 | ||
| 85 | #### `drive.key` | |
| 86 | ||
| 87 | The public key of the Hypercore backing the drive. | |
| 88 | ||
| 89 | #### `drive.discoveryKey` | |
| 90 | ||
| 91 | The hash of the public key of the Hypercore backing the drive. | |
| 92 | ||
| 93 | Can be used as a `topic` to seed the drive using Hyperswarm. | |
| 94 | ||
| 95 | #### `drive.contentKey` | |
| 96 | ||
| 97 | The public key of the [Hyperblobs](https://github.com/holepunchto/hyperblobs) instance holding blobs associated with entries in the drive. | |
| 98 | ||
| 99 | #### `drive.writable` | |
| 100 | ||
| 101 | Boolean indicating if we can write or delete data in this drive. | |
| 102 | ||
| 103 | #### `drive.readable` | |
| 104 | ||
| 105 | Boolean indicating if we can read from this drive. After closing the drive this will be `false`. | |
| 106 | ||
| 107 | #### `drive.version` | |
| 108 | ||
| 109 | Number that indicates how many modifications were made, useful as a version identifier. | |
| 110 | ||
| 111 | #### `drive.supportsMetadata` | |
| 112 | ||
| 113 | Boolean indicating if the drive handles or not metadata. Always `true`. | |
| 114 | ||
| 115 | #### `await drive.put(path, buffer, [options])` | |
| 116 | ||
| 117 | Creates a file at `path` in the drive. `options` are the same as in `createWriteStream`. | |
| 118 | ||
| 119 | #### `const buffer = await drive.get(path, [options])` | |
| 120 | ||
| 121 | Returns the blob at `path` in the drive. If no blob exists, returns `null`. | |
| 122 | ||
| 123 | It also returns `null` for symbolic links. | |
| 124 | ||
| 125 | `options` include: | |
| 126 | ```js | |
| 127 | { | |
| 128 | wait: true, // Wait for block to be downloaded | |
| 129 | timeout: 0 // Wait at max some milliseconds (0 means no timeout) | |
| 130 | } | |
| 131 | ``` | |
| 132 | ||
| 133 | #### `const entry = await drive.entry(path, [options])` | |
| 134 | ||
| 135 | Returns the entry at `path` in the drive. It looks like this: | |
| 136 | ```js | |
| 137 | { | |
| 138 | seq: Number, | |
| 139 | key: String, | |
| 140 | value: { | |
| 141 | executable: Boolean, // Whether the blob at path is an executable | |
| 142 | linkname: null, // If entry not symlink, otherwise a string to the entry this links to | |
| 143 | blob: { // Hyperblobs id that can be used to fetch the blob associated with this entry | |
| 144 | blockOffset: Number, | |
| 145 | blockLength: Number, | |
| 146 | byteOffset: Number, | |
| 147 | byteLength: Number | |
| 148 | }, | |
| 149 | metadata: null | |
| 150 | } | |
| 151 | } | |
| 152 | ``` | |
| 153 | ||
| 154 | `options` include: | |
| 155 | ```js | |
| 156 | { | |
| 157 | follow: false, // Follow symlinks, 16 max or throws an error | |
| 158 | wait: true, // Wait for block to be downloaded | |
| 159 | timeout: 0 // Wait at max some milliseconds (0 means no timeout) | |
| 160 | } | |
| 161 | ``` | |
| 162 | ||
| 163 | #### `const exists = await drive.exists(path)` | |
| 164 | ||
| 165 | Returns `true` if the entry at `path` does exists, otherwise `false`. | |
| 166 | ||
| 167 | #### `await drive.del(path)` | |
| 168 | ||
| 169 | Deletes the file at `path` from the drive. | |
| 170 | ||
| 171 | #### `const comparison = drive.compare(entryA, entryB)` | |
| 172 | ||
| 173 | Returns `0` if entries are the same, `1` if `entryA` is older, and `-1` if `entryB` is older. | |
| 174 | ||
| 175 | #### `const cleared = await drive.clear(path, [options])` | |
| 176 | ||
| 177 | Deletes the blob from storage to free up space, but the file structure reference is kept. | |
| 178 | ||
| 179 | `options` include: | |
| 180 | ```js | |
| 181 | { | |
| 182 | diff: false // Returned `cleared` bytes object is null unless you enable this | |
| 183 | } | |
| 184 | ``` | |
| 185 | ||
| 186 | #### `const cleared = await drive.clearAll([options])` | |
| 187 | ||
| 188 | Deletes all the blobs from storage to free up space, similar to how `drive.clear()` works. | |
| 189 | ||
| 190 | `options` include: | |
| 191 | ```js | |
| 192 | { | |
| 193 | diff: false // Returned `cleared` bytes object is null unless you enable this | |
| 194 | } | |
| 195 | ``` | |
| 196 | ||
| 197 | #### `await drive.truncate(version, [options] })` | |
| 198 | ||
| 199 | Truncates the Hyperdrive to a previous version (both the file-structure reference and the blobs). | |
| 200 | ||
| 201 | A `blobs: <length>` option can be passed in if you know the corresponding blobs length, but it is recommended to let the method figure it out for you. | |
| 202 | ||
| 203 | #### `await drive.purge()` | |
| 204 | ||
| 205 | Purge both cores (db and blobs) from your storage, completely removing all the drive's data. | |
| 206 | ||
| 207 | #### `await drive.symlink(path, linkname)` | |
| 208 | ||
| 209 | Creates an entry in drive at `path` that points to the entry at `linkname`. | |
| 210 | ||
| 211 | If a blob entry currently exists at `path` then it will get overwritten and `drive.get(key)` will return `null`, while `drive.entry(key)` will return the entry with symlink information. | |
| 212 | ||
| 213 | #### `const batch = drive.batch()` | |
| 214 | ||
| 215 | Useful for atomically mutate the drive, has the same interface as Hyperdrive. | |
| 216 | ||
| 217 | #### `await batch.flush()` | |
| 218 | ||
| 219 | Commit a batch of mutations to the underlying drive. | |
| 220 | ||
| 221 | #### `const stream = drive.list(folder, [options])` | |
| 222 | ||
| 223 | Returns a stream of all entries in the drive at paths prefixed with `folder`. | |
| 224 | ||
| 225 | `options` include: | |
| 226 | ```js | |
| 227 | { | |
| 228 | recursive: true | false // Whether to descend into all subfolders or not, | |
| 229 | ignore: String || Array // Ignore files and folders by name, | |
| 230 | wait: true, // Wait for block to be downloaded. | |
| 231 | } | |
| 232 | ``` | |
| 233 | ||
| 234 | #### `const stream = drive.readdir(folder, [options])` | |
| 235 | ||
| 236 | Returns a stream of all subpaths of entries in drive stored at paths prefixed by `folder`. | |
| 237 | ||
| 238 | `options` include: | |
| 239 | ```js | |
| 240 | { | |
| 241 | wait: true, // Wait for block to be downloaded | |
| 242 | } | |
| 243 | ``` | |
| 244 | ||
| 245 | #### `const stream = await drive.entries([range], [options])` | |
| 246 | ||
| 247 | Returns a read stream of entries in the drive. | |
| 248 | ||
| 249 | `options` are the same as `Hyperbee().createReadStream([range], [options])`. | |
| 250 | ||
| 251 | #### `const mirror = drive.mirror(out, [options])` | |
| 252 | ||
| 253 | Efficiently mirror this drive into another. Returns a [`MirrorDrive`](https://github.com/holepunchto/mirror-drive#api) instance constructed with `options`. | |
| 254 | ||
| 255 | Call `await mirror.done()` to wait for the mirroring to finish. | |
| 256 | ||
| 257 | #### `const watcher = drive.watch([folder])` | |
| 258 | ||
| 259 | Returns an iterator that listens on `folder` to yield changes, by default on `/`. | |
| 260 | ||
| 261 | Usage example: | |
| 262 | ```js | |
| 263 | for await (const [current, previous] of watcher) { | |
| 264 | console.log(current.version) | |
| 265 | console.log(previous.version) | |
| 266 | } | |
| 267 | ``` | |
| 268 | ||
| 269 | Those `current` and `previous` are snapshots that are auto-closed before next value. | |
| 270 | ||
| 271 | Don't close those snapshots yourself because they're used internally, let them be auto-closed. | |
| 272 | ||
| 273 | `await watcher.ready()` | |
| 274 | ||
| 275 | Waits until the watcher is loaded and detecting changes. | |
| 276 | ||
| 277 | `await watcher.destroy()` | |
| 278 | ||
| 279 | Stops the watcher. You could also stop it by using `break` in the loop. | |
| 280 | ||
| 281 | #### `const rs = drive.createReadStream(path, [options])` | |
| 282 | ||
| 283 | Returns a stream to read out the blob stored in the drive at `path`. | |
| 284 | ||
| 285 | `options` include: | |
| 286 | ```js | |
| 287 | { | |
| 288 | start: Number, // `start` and `end` are inclusive | |
| 289 | end: Number, | |
| 290 | length: Number, // `length` overrides `end`, they're not meant to be used together | |
| 291 | wait: true, // Wait for blocks to be downloaded | |
| 292 | timeout: 0 // Wait at max some milliseconds (0 means no timeout) | |
| 293 | } | |
| 294 | ``` | |
| 295 | ||
| 296 | #### `const ws = drive.createWriteStream(path, [options])` | |
| 297 | ||
| 298 | Stream a blob into the drive at `path`. | |
| 299 | ||
| 300 | `options` include: | |
| 301 | ```js | |
| 302 | { | |
| 303 | executable: Boolean, | |
| 304 | metadata: null // Extended file information i.e. arbitrary JSON value | |
| 305 | } | |
| 306 | ``` | |
| 307 | ||
| 308 | #### `await drive.download(folder, [options])` | |
| 309 | ||
| 310 | Downloads the blobs corresponding to all entries in the drive at paths prefixed with `folder`. | |
| 311 | ||
| 312 | `options` are the same as those for `drive.list(folder, [options])`. | |
| 313 | ||
| 314 | #### `const snapshot = drive.checkout(version)` | |
| 315 | ||
| 316 | Get a read-only snapshot of a previous version. | |
| 317 | ||
| 318 | #### `const stream = drive.diff(version, folder, [options])` | |
| 319 | ||
| 320 | Efficiently create a stream of the shallow changes to `folder` between `version` and `drive.version`. | |
| 321 | ||
| 322 | Each entry is sorted by key and looks like this: | |
| 323 | ```js | |
| 324 | { | |
| 325 | left: Object, // Entry in folder at drive.version for some path | |
| 326 | right: Object, // Entry in folder at drive.checkout(version) for some path | |
| 327 | } | |
| 328 | ``` | |
| 329 | ||
| 330 | If an entry exists in `drive.version` of the `folder` but not in `version`, then `left` is set and `right` will be `null`, and vice versa. | |
| 331 | ||
| 332 | #### `await drive.downloadDiff(version, folder, [options])` | |
| 333 | ||
| 334 | Downloads all the blobs in `folder` corresponding to entries in `drive.checkout(version)` that are not in `drive.version`. | |
| 335 | ||
| 336 | In other words, downloads all the blobs added to `folder` up to `version` of the drive. | |
| 337 | ||
| 338 | #### `await drive.downloadRange(dbRanges, blobRanges)` | |
| 339 | ||
| 340 | Downloads the entries and blobs stored in the [ranges][core-range-docs] `dbRanges` and `blobRanges`. | |
| 341 | ||
| 342 | #### `const done = drive.findingPeers()` | |
| 343 | ||
| 344 | Indicate to Hyperdrive that you're finding peers in the background, requests will be on hold until this is done. | |
| 345 | ||
| 346 | Call `done()` when your current discovery iteration is done, i.e. after `swarm.flush()` finishes. | |
| 347 | ||
| 348 | #### `const stream = drive.replicate(isInitiatorOrStream)` | |
| 349 | ||
| 350 | Usage example: | |
| 351 | ```js | |
| 352 | const swarm = new Hyperswarm() | |
| 353 | const done = drive.findingPeers() | |
| 354 | swarm.on('connection', (socket) => drive.replicate(socket)) | |
| 355 | swarm.join(drive.discoveryKey) | |
| 356 | swarm.flush().then(done, done) | |
| 357 | ``` | |
| 358 | ||
| 359 | See more about how replicate works at [corestore.replicate][store-replicate-docs]. | |
| 360 | ||
| 361 | #### `const updated = await drive.update([options])` | |
| 362 | ||
| 363 | Waits for initial proof of the new drive version until all `findingPeers` are done. | |
| 364 | ||
| 365 | `options` include: | |
| 366 | ```js | |
| 367 | { | |
| 368 | wait: false | |
| 369 | } | |
| 370 | ``` | |
| 371 | ||
| 372 | Use `drive.findingPeers()` or `{ wait: true }` to make await `drive.update()` blocking. | |
| 373 | ||
| 374 | #### `const blobs = await drive.getBlobs()` | |
| 375 | ||
| 376 | Returns the [Hyperblobs](https://github.com/holepunchto/hyperblobs) instance storing the blobs indexed by drive entries. | |
| 377 | ||
| 378 | ```js | |
| 379 | await drive.put('/file.txt', Buffer.from('hi')) | |
| 380 | ||
| 381 | const buffer1 = await drive.get('/file.txt') | |
| 382 | ||
| 383 | const blobs = await drive.getBlobs() | |
| 384 | const entry = await drive.entry('/file.txt') | |
| 385 | const buffer2 = await blobs.get(entry.value.blob) | |
| 386 | ||
| 387 | // => buffer1 and buffer2 are equals | |
| 388 | ``` | |
| 389 | ||
| 390 | [core-range-docs]: https://github.com/holepunchto/hypercore#const-range--coredownloadrange | |
| 391 | [store-replicate-docs]: https://github.com/holepunchto/corestore#const-stream--storereplicateoptsorstream | |
| 392 | ||
| 393 | #### `const blobsLength = await drive.getBlobsLength(checkout)` | |
| 394 | ||
| 395 | Returns the length of the Hyperblobs instance at the time of the specified Hyperdrive version (defaults to the current version). | |
| 396 | ||
| 397 | ## License | |
| 398 | ||
| 399 | Apache-2.0 |
| … | … | @@ -0,0 +1,756 @@ |
| 1 | const Hyperbee = require('hyperbee') | |
| 2 | const Hyperblobs = require('hyperblobs') | |
| 3 | const isOptions = require('is-options') | |
| 4 | const { Writable, Readable } = require('streamx') | |
| 5 | const unixPathResolve = require('unix-path-resolve') | |
| 6 | const MirrorDrive = require('mirror-drive') | |
| 7 | const SubEncoder = require('sub-encoder') | |
| 8 | const ReadyResource = require('ready-resource') | |
| 9 | const safetyCatch = require('safety-catch') | |
| 10 | const crypto = require('hypercore-crypto') | |
| 11 | const Hypercore = require('hypercore') | |
| 12 | const { BLOCK_NOT_AVAILABLE, BAD_ARGUMENT } = require('hypercore-errors') | |
| 13 | const Monitor = require('./lib/monitor') | |
| 14 | ||
| 15 | const keyEncoding = new SubEncoder('files', 'utf-8') | |
| 16 | ||
| 17 | const [BLOBS] = crypto.namespace('hyperdrive', 1) | |
| 18 | ||
| 19 | module.exports = class Hyperdrive extends ReadyResource { | |
| 20 | constructor (corestore, key, opts = {}) { | |
| 21 | super() | |
| 22 | ||
| 23 | if (isOptions(key)) { | |
| 24 | opts = key | |
| 25 | key = null | |
| 26 | } | |
| 27 | ||
| 28 | this.corestore = corestore | |
| 29 | this.db = opts._db || makeBee(key, corestore, opts) | |
| 30 | this.core = this.db.core | |
| 31 | this.blobs = null | |
| 32 | this.supportsMetadata = true | |
| 33 | this.encryptionKey = opts.encryptionKey || null | |
| 34 | this.monitors = new Set() | |
| 35 | ||
| 36 | this._active = opts.active !== false | |
| 37 | this._openingBlobs = null | |
| 38 | this._onwait = opts.onwait || null | |
| 39 | this._batching = !!(opts._checkout === null && opts._db) | |
| 40 | this._checkout = opts._checkout || null | |
| 41 | ||
| 42 | this.ready().catch(safetyCatch) | |
| 43 | } | |
| 44 | ||
| 45 | [Symbol.asyncIterator] () { | |
| 46 | return this.entries()[Symbol.asyncIterator]() | |
| 47 | } | |
| 48 | ||
| 49 | static async getDriveKey (corestore) { | |
| 50 | const core = makeBee(undefined, corestore) | |
| 51 | await core.ready() | |
| 52 | const key = core.key | |
| 53 | await core.close() | |
| 54 | return key | |
| 55 | } | |
| 56 | ||
| 57 | static getContentKey (m, key) { | |
| 58 | if (m instanceof Hypercore) { | |
| 59 | if (m.core.compat) return null | |
| 60 | return Hyperdrive.getContentKey(m.manifest, m.key) | |
| 61 | } | |
| 62 | ||
| 63 | const manifest = generateContentManifest(m, key) | |
| 64 | if (!manifest) return null | |
| 65 | ||
| 66 | return Hypercore.key(manifest) | |
| 67 | } | |
| 68 | ||
| 69 | _generateBlobsManifest () { | |
| 70 | const m = this.db.core.manifest | |
| 71 | if (this.db.core.core.compat) return null | |
| 72 | ||
| 73 | return generateContentManifest(m, this.core.key) | |
| 74 | } | |
| 75 | ||
| 76 | get id () { | |
| 77 | return this.core.id | |
| 78 | } | |
| 79 | ||
| 80 | get key () { | |
| 81 | return this.core.key | |
| 82 | } | |
| 83 | ||
| 84 | get discoveryKey () { | |
| 85 | return this.core.discoveryKey | |
| 86 | } | |
| 87 | ||
| 88 | get contentKey () { | |
| 89 | return this.blobs?.core.key | |
| 90 | } | |
| 91 | ||
| 92 | get version () { | |
| 93 | return this.db.version | |
| 94 | } | |
| 95 | ||
| 96 | get writable () { | |
| 97 | return this.core.writable | |
| 98 | } | |
| 99 | ||
| 100 | get readable () { | |
| 101 | return this.core.readable | |
| 102 | } | |
| 103 | ||
| 104 | findingPeers () { | |
| 105 | return this.corestore.findingPeers() | |
| 106 | } | |
| 107 | ||
| 108 | async truncate (version, { blobs = -1 } = {}) { | |
| 109 | if (!this.opened) await this.ready() | |
| 110 | ||
| 111 | if (version > this.core.length) { | |
| 112 | throw BAD_ARGUMENT('Bad truncation length') | |
| 113 | } | |
| 114 | ||
| 115 | const blobsVersion = blobs === -1 ? await this.getBlobsLength(version) : blobs | |
| 116 | const bl = await this.getBlobs() | |
| 117 | ||
| 118 | if (blobsVersion > bl.core.length) { | |
| 119 | throw BAD_ARGUMENT('Bad truncation length') | |
| 120 | } | |
| 121 | ||
| 122 | await this.core.truncate(version) | |
| 123 | await bl.core.truncate(blobsVersion) | |
| 124 | } | |
| 125 | ||
| 126 | async getBlobsLength (checkout) { | |
| 127 | if (!this.opened) await this.ready() | |
| 128 | ||
| 129 | if (!checkout) checkout = this.version | |
| 130 | ||
| 131 | const c = this.db.checkout(checkout) | |
| 132 | ||
| 133 | try { | |
| 134 | return await getBlobsLength(c) | |
| 135 | } finally { | |
| 136 | await c.close() | |
| 137 | } | |
| 138 | } | |
| 139 | ||
| 140 | replicate (isInitiator, opts) { | |
| 141 | return this.corestore.replicate(isInitiator, opts) | |
| 142 | } | |
| 143 | ||
| 144 | update (opts) { | |
| 145 | return this.db.update(opts) | |
| 146 | } | |
| 147 | ||
| 148 | _makeCheckout (snapshot) { | |
| 149 | return new Hyperdrive(this.corestore, this.key, { | |
| 150 | onwait: this._onwait, | |
| 151 | encryptionKey: this.encryptionKey, | |
| 152 | _checkout: this._checkout || this, | |
| 153 | _db: snapshot | |
| 154 | }) | |
| 155 | } | |
| 156 | ||
| 157 | checkout (version) { | |
| 158 | return this._makeCheckout(this.db.checkout(version)) | |
| 159 | } | |
| 160 | ||
| 161 | batch () { | |
| 162 | return new Hyperdrive(this.corestore, this.key, { | |
| 163 | onwait: this._onwait, | |
| 164 | encryptionKey: this.encryptionKey, | |
| 165 | _checkout: null, | |
| 166 | _db: this.db.batch() | |
| 167 | }) | |
| 168 | } | |
| 169 | ||
| 170 | setActive (bool) { | |
| 171 | const active = !!bool | |
| 172 | if (active === this._active) return | |
| 173 | this._active = active | |
| 174 | this.core.setActive(active) | |
| 175 | if (this.blobs) this.blobs.core.setActive(active) | |
| 176 | } | |
| 177 | ||
| 178 | async flush () { | |
| 179 | await this.db.flush() | |
| 180 | return this.close() | |
| 181 | } | |
| 182 | ||
| 183 | async _close () { | |
| 184 | if (this.blobs && (!this._checkout || this.blobs !== this._checkout.blobs)) { | |
| 185 | await this.blobs.core.close() | |
| 186 | } | |
| 187 | ||
| 188 | await this.db.close() | |
| 189 | ||
| 190 | if (!this._checkout && !this._batching) { | |
| 191 | await this.corestore.close() | |
| 192 | } | |
| 193 | ||
| 194 | await this.closeMonitors() | |
| 195 | } | |
| 196 | ||
| 197 | async _openBlobsFromHeader (opts) { | |
| 198 | if (this.blobs) return true | |
| 199 | ||
| 200 | const header = await getBee(this.db).getHeader(opts) | |
| 201 | if (!header) return false | |
| 202 | ||
| 203 | if (this.blobs) return true | |
| 204 | ||
| 205 | const contentKey = header.metadata && header.metadata.contentFeed && header.metadata.contentFeed.subarray(0, 32) | |
| 206 | const blobsKey = contentKey || Hypercore.key(this._generateBlobsManifest()) | |
| 207 | if (!blobsKey || blobsKey.length < 32) throw new Error('Invalid or no Blob store key set') | |
| 208 | ||
| 209 | const blobsCore = this.corestore.get({ | |
| 210 | key: blobsKey, | |
| 211 | cache: false, | |
| 212 | onwait: this._onwait, | |
| 213 | encryptionKey: this.encryptionKey, | |
| 214 | keyPair: (!contentKey && this.db.core.writable) ? this.db.core.keyPair : null, | |
| 215 | active: this._active | |
| 216 | }) | |
| 217 | await blobsCore.ready() | |
| 218 | ||
| 219 | if (this.closing) { | |
| 220 | await blobsCore.close() | |
| 221 | return false | |
| 222 | } | |
| 223 | ||
| 224 | this.blobs = new Hyperblobs(blobsCore) | |
| 225 | ||
| 226 | this.emit('blobs', this.blobs) | |
| 227 | this.emit('content-key', blobsCore.key) | |
| 228 | ||
| 229 | return true | |
| 230 | } | |
| 231 | ||
| 232 | async _open () { | |
| 233 | if (this._checkout) { | |
| 234 | await this._checkout.ready() | |
| 235 | this.blobs = this._checkout.blobs | |
| 236 | return | |
| 237 | } | |
| 238 | ||
| 239 | await this._openBlobsFromHeader({ wait: false }) | |
| 240 | ||
| 241 | if (this.db.core.writable && !this.blobs) { | |
| 242 | const m = this._generateBlobsManifest() | |
| 243 | const blobsCore = this.corestore.get({ | |
| 244 | manifest: m, | |
| 245 | name: m ? null : this.db.core.id + '/blobs', // simple trick to avoid blobs clashing if no namespace is provided... | |
| 246 | cache: false, | |
| 247 | onwait: this._onwait, | |
| 248 | encryptionKey: this.encryptionKey, | |
| 249 | compat: this.db.core.core.compat, | |
| 250 | active: this._active, | |
| 251 | keyPair: (m && this.db.core.writable) ? this.db.core.keyPair : null | |
| 252 | }) | |
| 253 | await blobsCore.ready() | |
| 254 | ||
| 255 | this.blobs = new Hyperblobs(blobsCore) | |
| 256 | ||
| 257 | if (!m) getBee(this.db).metadata.contentFeed = this.blobs.core.key | |
| 258 | ||
| 259 | this.emit('blobs', this.blobs) | |
| 260 | this.emit('content-key', blobsCore.key) | |
| 261 | } | |
| 262 | ||
| 263 | await this.db.ready() | |
| 264 | ||
| 265 | if (!this.blobs) { | |
| 266 | // eagerly load the blob store.... | |
| 267 | this._openingBlobs = this._openBlobsFromHeader() | |
| 268 | this._openingBlobs.catch(safetyCatch) | |
| 269 | } | |
| 270 | } | |
| 271 | ||
| 272 | async getBlobs () { | |
| 273 | if (this.blobs) return this.blobs | |
| 274 | ||
| 275 | if (this._checkout) { | |
| 276 | this.blobs = await this._checkout.getBlobs() | |
| 277 | } else { | |
| 278 | await this.ready() | |
| 279 | await this._openingBlobs | |
| 280 | } | |
| 281 | ||
| 282 | return this.blobs | |
| 283 | } | |
| 284 | ||
| 285 | monitor (name, opts = {}) { | |
| 286 | const monitor = new Monitor(this, { name, ...opts }) | |
| 287 | this.monitors.add(monitor) | |
| 288 | return monitor | |
| 289 | } | |
| 290 | ||
| 291 | async closeMonitors () { | |
| 292 | const closing = [] | |
| 293 | for (const monitor of this.monitors) closing.push(monitor.close()) | |
| 294 | await Promise.allSettled(closing) | |
| 295 | } | |
| 296 | ||
| 297 | async get (name, opts) { | |
| 298 | const node = await this.entry(name, opts) | |
| 299 | if (!node?.value.blob) return null | |
| 300 | await this.getBlobs() | |
| 301 | const res = await this.blobs.get(node.value.blob, opts) | |
| 302 | ||
| 303 | if (res === null) throw BLOCK_NOT_AVAILABLE() | |
| 304 | return res | |
| 305 | } | |
| 306 | ||
| 307 | async put (name, buf, { executable = false, metadata = null } = {}) { | |
| 308 | await this.getBlobs() | |
| 309 | const blob = await this.blobs.put(buf) | |
| 310 | return this.db.put(std(name, false), { executable, linkname: null, blob, metadata }, { keyEncoding }) | |
| 311 | } | |
| 312 | ||
| 313 | async del (name) { | |
| 314 | return this.db.del(std(name, false), { keyEncoding }) | |
| 315 | } | |
| 316 | ||
| 317 | compare (a, b) { | |
| 318 | const diff = a.seq - b.seq | |
| 319 | return diff > 0 ? 1 : (diff < 0 ? -1 : 0) | |
| 320 | } | |
| 321 | ||
| 322 | async clear (name, opts) { | |
| 323 | if (!this.opened) await this.ready() | |
| 324 | ||
| 325 | let node = null | |
| 326 | ||
| 327 | try { | |
| 328 | node = await this.entry(name, { wait: false }) | |
| 329 | } catch { | |
| 330 | // do nothing, prop not available | |
| 331 | } | |
| 332 | ||
| 333 | if (node === null || this.blobs === null) { | |
| 334 | return (opts && opts.diff) ? { blocks: 0 } : null | |
| 335 | } | |
| 336 | ||
| 337 | return this.blobs.clear(node.value.blob, opts) | |
| 338 | } | |
| 339 | ||
| 340 | async clearAll (opts) { | |
| 341 | if (!this.opened) await this.ready() | |
| 342 | ||
| 343 | if (this.blobs === null) { | |
| 344 | return (opts && opts.diff) ? { blocks: 0 } : null | |
| 345 | } | |
| 346 | ||
| 347 | return this.blobs.core.clear(0, this.blobs.core.length, opts) | |
| 348 | } | |
| 349 | ||
| 350 | async purge () { | |
| 351 | if (this._checkout || this._batch) throw new Error('Can only purge the main session') | |
| 352 | ||
| 353 | await this.ready() // Ensure blobs loaded if present | |
| 354 | await this.close() | |
| 355 | ||
| 356 | const proms = [this.core.purge()] | |
| 357 | if (this.blobs) proms.push(this.blobs.core.purge()) | |
| 358 | await Promise.all(proms) | |
| 359 | } | |
| 360 | ||
| 361 | async symlink (name, dst, { metadata = null } = {}) { | |
| 362 | return this.db.put(std(name, false), { executable: false, linkname: dst, blob: null, metadata }, { keyEncoding }) | |
| 363 | } | |
| 364 | ||
| 365 | async entry (name, opts) { | |
| 366 | if (!opts || !opts.follow) return this._entry(name, opts) | |
| 367 | ||
| 368 | for (let i = 0; i < 16; i++) { | |
| 369 | const node = await this._entry(name, opts) | |
| 370 | if (!node || !node.value.linkname) return node | |
| 371 | ||
| 372 | name = unixPathResolve(node.key, node.value.linkname) | |
| 373 | } | |
| 374 | ||
| 375 | throw new Error('Recursive symlink') | |
| 376 | } | |
| 377 | ||
| 378 | async _entry (name, opts) { | |
| 379 | if (typeof name !== 'string') return name | |
| 380 | ||
| 381 | return this.db.get(std(name, false), { ...opts, keyEncoding }) | |
| 382 | } | |
| 383 | ||
| 384 | async exists (name) { | |
| 385 | return await this.entry(name) !== null | |
| 386 | } | |
| 387 | ||
| 388 | watch (folder) { | |
| 389 | folder = std(folder || '/', true) | |
| 390 | ||
| 391 | return this.db.watch(prefixRange(folder), { keyEncoding, map: (snap) => this._makeCheckout(snap) }) | |
| 392 | } | |
| 393 | ||
| 394 | diff (length, folder, opts) { | |
| 395 | if (typeof folder === 'object' && folder && !opts) return this.diff(length, null, folder) | |
| 396 | ||
| 397 | folder = std(folder || '/', true) | |
| 398 | ||
| 399 | return this.db.createDiffStream(length, prefixRange(folder), { ...opts, keyEncoding }) | |
| 400 | } | |
| 401 | ||
| 402 | async downloadDiff (length, folder, opts) { | |
| 403 | const dls = [] | |
| 404 | ||
| 405 | for await (const entry of this.diff(length, folder, opts)) { | |
| 406 | if (!entry.left) continue | |
| 407 | const b = entry.left.value.blob | |
| 408 | if (!b) continue | |
| 409 | const blobs = await this.getBlobs() | |
| 410 | dls.push(blobs.core.download({ start: b.blockOffset, length: b.blockLength })) | |
| 411 | } | |
| 412 | ||
| 413 | const proms = [] | |
| 414 | for (const r of dls) proms.push(r.downloaded()) | |
| 415 | ||
| 416 | await Promise.allSettled(proms) | |
| 417 | } | |
| 418 | ||
| 419 | async downloadRange (dbRanges, blobRanges) { | |
| 420 | const dls = [] | |
| 421 | ||
| 422 | await this.ready() | |
| 423 | ||
| 424 | for (const range of dbRanges) { | |
| 425 | dls.push(this.db.core.download(range)) | |
| 426 | } | |
| 427 | ||
| 428 | const blobs = await this.getBlobs() | |
| 429 | ||
| 430 | for (const range of blobRanges) { | |
| 431 | dls.push(blobs.core.download(range)) | |
| 432 | } | |
| 433 | ||
| 434 | const proms = [] | |
| 435 | for (const r of dls) proms.push(r.downloaded()) | |
| 436 | ||
| 437 | await Promise.allSettled(proms) | |
| 438 | } | |
| 439 | ||
| 440 | entries (range, opts) { | |
| 441 | const stream = this.db.createReadStream(range, { ...opts, keyEncoding }) | |
| 442 | if (opts && opts.ignore) stream._readableState.map = createStreamMapIgnore(opts.ignore) | |
| 443 | return stream | |
| 444 | } | |
| 445 | ||
| 446 | async download (folder = '/', opts) { | |
| 447 | if (typeof folder === 'object') return this.download(undefined, folder) | |
| 448 | ||
| 449 | const dls = [] | |
| 450 | const entry = (!folder || folder.endsWith('/')) ? null : await this.entry(folder) | |
| 451 | ||
| 452 | if (entry) { | |
| 453 | const b = entry.value.blob | |
| 454 | if (!b) return | |
| 455 | const blobs = await this.getBlobs() | |
| 456 | await blobs.core.download({ start: b.blockOffset, length: b.blockLength }).downloaded() | |
| 457 | return | |
| 458 | } | |
| 459 | ||
| 460 | // first preload the list so we can use the full power afterwards to actually preload everything | |
| 461 | // eslint-disable-next-line | |
| 462 | for await (const _ of this.list(folder, opts)) { | |
| 463 | // ignore | |
| 464 | } | |
| 465 | ||
| 466 | for await (const entry of this.list(folder, opts)) { | |
| 467 | const b = entry.value.blob | |
| 468 | if (!b) continue | |
| 469 | ||
| 470 | const blobs = await this.getBlobs() | |
| 471 | dls.push(blobs.core.download({ start: b.blockOffset, length: b.blockLength })) | |
| 472 | } | |
| 473 | ||
| 474 | const proms = [] | |
| 475 | for (const r of dls) proms.push(r.downloaded()) | |
| 476 | ||
| 477 | await Promise.allSettled(proms) | |
| 478 | } | |
| 479 | ||
| 480 | // atm always recursive, but we should add some depth thing to it | |
| 481 | list (folder, opts = {}) { | |
| 482 | if (typeof folder === 'object') return this.list(undefined, folder) | |
| 483 | ||
| 484 | folder = std(folder || '/', true) | |
| 485 | ||
| 486 | const ignore = opts.ignore ? normalizeIgnore(opts.ignore) : null | |
| 487 | const stream = opts && opts.recursive === false ? shallowReadStream(this.db, folder, false, ignore, opts) : this.entries(prefixRange(folder), { ...opts, ignore }) | |
| 488 | return stream | |
| 489 | } | |
| 490 | ||
| 491 | readdir (folder, opts) { | |
| 492 | folder = std(folder || '/', true) | |
| 493 | return shallowReadStream(this.db, folder, true, null, opts) | |
| 494 | } | |
| 495 | ||
| 496 | mirror (out, opts) { | |
| 497 | return new MirrorDrive(this, out, opts) | |
| 498 | } | |
| 499 | ||
| 500 | createReadStream (name, opts) { | |
| 501 | const self = this | |
| 502 | ||
| 503 | let destroyed = false | |
| 504 | let rs = null | |
| 505 | ||
| 506 | const stream = new Readable({ | |
| 507 | open (cb) { | |
| 508 | self.getBlobs().then(onblobs, cb) | |
| 509 | ||
| 510 | function onblobs () { | |
| 511 | self.entry(name).then(onnode, cb) | |
| 512 | } | |
| 513 | ||
| 514 | function onnode (node) { | |
| 515 | if (destroyed) return cb(null) | |
| 516 | if (!node) return cb(new Error('Blob does not exist')) | |
| 517 | if (self.closing) return cb(new Error('Closed')) | |
| 518 | ||
| 519 | if (!node.value.blob) { | |
| 520 | stream.push(null) | |
| 521 | return cb(null) | |
| 522 | } | |
| 523 | ||
| 524 | rs = self.blobs.createReadStream(node.value.blob, opts) | |
| 525 | ||
| 526 | rs.on('data', function (data) { | |
| 527 | if (!stream.push(data)) rs.pause() | |
| 528 | }) | |
| 529 | ||
| 530 | rs.on('end', function () { | |
| 531 | stream.push(null) | |
| 532 | }) | |
| 533 | ||
| 534 | rs.on('error', function (err) { | |
| 535 | stream.destroy(err) | |
| 536 | }) | |
| 537 | ||
| 538 | cb(null) | |
| 539 | } | |
| 540 | }, | |
| 541 | read (cb) { | |
| 542 | rs.resume() | |
| 543 | cb(null) | |
| 544 | }, | |
| 545 | predestroy () { | |
| 546 | destroyed = true | |
| 547 | if (rs) rs.destroy() | |
| 548 | } | |
| 549 | }) | |
| 550 | ||
| 551 | return stream | |
| 552 | } | |
| 553 | ||
| 554 | createWriteStream (name, { executable = false, metadata = null } = {}) { | |
| 555 | const self = this | |
| 556 | ||
| 557 | let destroyed = false | |
| 558 | let ws = null | |
| 559 | let ondrain = null | |
| 560 | let onfinish = null | |
| 561 | ||
| 562 | const stream = new Writable({ | |
| 563 | open (cb) { | |
| 564 | self.getBlobs().then(onblobs, cb) | |
| 565 | ||
| 566 | function onblobs () { | |
| 567 | if (destroyed) return cb(null) | |
| 568 | ||
| 569 | ws = self.blobs.createWriteStream() | |
| 570 | ||
| 571 | ws.on('error', function (err) { | |
| 572 | stream.destroy(err) | |
| 573 | }) | |
| 574 | ||
| 575 | ws.on('close', function () { | |
| 576 | const err = new Error('Closed') | |
| 577 | callOndrain(err) | |
| 578 | callOnfinish(err) | |
| 579 | }) | |
| 580 | ||
| 581 | ws.on('finish', function () { | |
| 582 | callOnfinish(null) | |
| 583 | }) | |
| 584 | ||
| 585 | ws.on('drain', function () { | |
| 586 | callOndrain(null) | |
| 587 | }) | |
| 588 | ||
| 589 | cb(null) | |
| 590 | } | |
| 591 | }, | |
| 592 | write (data, cb) { | |
| 593 | if (ws.write(data) === true) return cb(null) | |
| 594 | ondrain = cb | |
| 595 | }, | |
| 596 | final (cb) { | |
| 597 | onfinish = cb | |
| 598 | ws.end() | |
| 599 | }, | |
| 600 | predestroy () { | |
| 601 | destroyed = true | |
| 602 | if (ws) ws.destroy() | |
| 603 | } | |
| 604 | }) | |
| 605 | ||
| 606 | return stream | |
| 607 | ||
| 608 | function callOnfinish (err) { | |
| 609 | if (!onfinish) return | |
| 610 | ||
| 611 | const cb = onfinish | |
| 612 | onfinish = null | |
| 613 | ||
| 614 | if (err) return cb(err) | |
| 615 | self.db.put(std(name, false), { executable, linkname: null, blob: ws.id, metadata }, { keyEncoding }).then(() => cb(null), cb) | |
| 616 | } | |
| 617 | ||
| 618 | function callOndrain (err) { | |
| 619 | if (ondrain) { | |
| 620 | const cb = ondrain | |
| 621 | ondrain = null | |
| 622 | cb(err) | |
| 623 | } | |
| 624 | } | |
| 625 | } | |
| 626 | ||
| 627 | static normalizePath (name) { | |
| 628 | return std(name, false) | |
| 629 | } | |
| 630 | } | |
| 631 | ||
| 632 | function shallowReadStream (files, folder, keys, ignore, opts) { | |
| 633 | let prev = '/' | |
| 634 | let prevName = '' | |
| 635 | ||
| 636 | return new Readable({ | |
| 637 | async read (cb) { | |
| 638 | let node = null | |
| 639 | ||
| 640 | try { | |
| 641 | node = await files.peek(prefixRange(folder, prev), { ...opts, keyEncoding }) | |
| 642 | } catch (err) { | |
| 643 | return cb(err) | |
| 644 | } | |
| 645 | ||
| 646 | if (!node) { | |
| 647 | this.push(null) | |
| 648 | return cb(null) | |
| 649 | } | |
| 650 | ||
| 651 | const suffix = node.key.slice(folder.length + 1) | |
| 652 | const i = suffix.indexOf('/') | |
| 653 | const name = i === -1 ? suffix : suffix.slice(0, i) | |
| 654 | ||
| 655 | prev = '/' + name + (i === -1 ? '' : '0') | |
| 656 | ||
| 657 | // just in case someone does /foo + /foo/bar, but we should prop not even support that | |
| 658 | if (name === prevName) { | |
| 659 | this._read(cb) | |
| 660 | return | |
| 661 | } | |
| 662 | ||
| 663 | prevName = name | |
| 664 | ||
| 665 | if (ignore && isIgnored(node.key, ignore)) { | |
| 666 | this._read(cb) | |
| 667 | return | |
| 668 | } | |
| 669 | ||
| 670 | this.push(keys ? name : node) | |
| 671 | cb(null) | |
| 672 | } | |
| 673 | }) | |
| 674 | } | |
| 675 | ||
| 676 | function makeBee (key, corestore, opts = {}) { | |
| 677 | const name = key ? undefined : 'db' | |
| 678 | const core = corestore.get({ key, name, exclusive: true, onwait: opts.onwait, encryptionKey: opts.encryptionKey, compat: opts.compat, active: opts.active }) | |
| 679 | ||
| 680 | return new Hyperbee(core, { | |
| 681 | keyEncoding: 'utf-8', | |
| 682 | valueEncoding: 'json', | |
| 683 | metadata: { contentFeed: null } | |
| 684 | }) | |
| 685 | } | |
| 686 | ||
| 687 | function getBee (bee) { | |
| 688 | // A Batch instance will have a .tree property for the actual Hyperbee | |
| 689 | return bee.tree || bee | |
| 690 | } | |
| 691 | ||
| 692 | function std (name, removeSlash) { | |
| 693 | // Note: only remove slash if you're going to use it as prefix range | |
| 694 | name = unixPathResolve('/', name) | |
| 695 | if (removeSlash && name.endsWith('/')) name = name.slice(0, -1) | |
| 696 | validateFilename(name) | |
| 697 | return name | |
| 698 | } | |
| 699 | ||
| 700 | function validateFilename (name) { | |
| 701 | if (name === '/') throw new Error('Invalid filename: ' + name) | |
| 702 | } | |
| 703 | ||
| 704 | function prefixRange (name, prev = '/') { | |
| 705 | // '0' is binary +1 of / | |
| 706 | return { gt: name + prev, lt: name + '0' } | |
| 707 | } | |
| 708 | ||
| 709 | function generateContentManifest (m, key) { | |
| 710 | if (m.version < 1) return null | |
| 711 | ||
| 712 | const signers = [] | |
| 713 | ||
| 714 | if (!key) key = Hypercore.key(m) | |
| 715 | ||
| 716 | for (const s of m.signers) { | |
| 717 | const namespace = crypto.hash([BLOBS, key, s.namespace]) | |
| 718 | signers.push({ ...s, namespace }) | |
| 719 | } | |
| 720 | ||
| 721 | return { | |
| 722 | version: m.version, | |
| 723 | hash: 'blake2b', | |
| 724 | allowPatch: m.allowPatch, | |
| 725 | quorum: m.quorum, | |
| 726 | signers, | |
| 727 | prologue: null // TODO: could be configurable through the header still... | |
| 728 | } | |
| 729 | } | |
| 730 | ||
| 731 | async function getBlobsLength (db) { | |
| 732 | let length = 0 | |
| 733 | ||
| 734 | for await (const { value } of db.createReadStream()) { | |
| 735 | const b = value && value.blob | |
| 736 | if (!b) continue | |
| 737 | const len = b.blockOffset + b.blockLength | |
| 738 | if (len > length) length = len | |
| 739 | } | |
| 740 | ||
| 741 | return length | |
| 742 | } | |
| 743 | ||
| 744 | function normalizeIgnore (ignore) { | |
| 745 | return [].concat(ignore).map(e => unixPathResolve('/', e)) | |
| 746 | } | |
| 747 | ||
| 748 | function isIgnored (key, ignore) { | |
| 749 | return ignore.some(e => e === key || key.startsWith(e + '/')) | |
| 750 | } | |
| 751 | ||
| 752 | function createStreamMapIgnore (ignore) { | |
| 753 | return (node) => { | |
| 754 | return isIgnored(node.key, ignore) ? null : node | |
| 755 | } | |
| 756 | } |
| … | … | @@ -0,0 +1,123 @@ |
| 1 | const ReadyResource = require('ready-resource') | |
| 2 | const safetyCatch = require('safety-catch') | |
| 3 | const speedometer = require('speedometer') | |
| 4 | ||
| 5 | module.exports = class Monitor extends ReadyResource { | |
| 6 | constructor (drive, opts = {}) { | |
| 7 | super() | |
| 8 | this.drive = drive | |
| 9 | this.blobs = null | |
| 10 | this.name = opts.name || null | |
| 11 | this.entry = opts.entry || null | |
| 12 | this.peers = 0 | |
| 13 | ||
| 14 | this._boundOnUpload = this._onUpload.bind(this) | |
| 15 | this._boundOnDownload = this._onDownload.bind(this) | |
| 16 | this._boundPeerUpdate = this._updatePeers.bind(this) | |
| 17 | ||
| 18 | const stats = { | |
| 19 | startTime: 0, | |
| 20 | percentage: 0, | |
| 21 | peers: 0, | |
| 22 | speed: 0, | |
| 23 | blocks: 0, | |
| 24 | totalBytes: 0, // local + bytes loaded during monitoring | |
| 25 | monitoringBytes: 0, // bytes loaded during monitoring | |
| 26 | targetBytes: 0, | |
| 27 | targetBlocks: 0 | |
| 28 | } | |
| 29 | ||
| 30 | // Updated on each upload/download event | |
| 31 | this.uploadStats = { ...stats } | |
| 32 | this.downloadStats = { ...stats } | |
| 33 | ||
| 34 | this.uploadSpeedometer = null | |
| 35 | this.downloadSpeedometer = null | |
| 36 | ||
| 37 | this.ready().catch(safetyCatch) | |
| 38 | } | |
| 39 | ||
| 40 | async _open () { | |
| 41 | await this.drive.ready() | |
| 42 | this.blobs = await this.drive.getBlobs() | |
| 43 | if (!this.entry && this.name) this.entry = await this.drive.entry(this.name) | |
| 44 | if (this.entry) this._setEntryInfo() | |
| 45 | ||
| 46 | this.uploadSpeedometer = speedometer() | |
| 47 | this.downloadSpeedometer = speedometer() | |
| 48 | ||
| 49 | this._updatePeers() | |
| 50 | ||
| 51 | // Handlers | |
| 52 | this.blobs.core.on('peer-add', this._boundPeerUpdate) | |
| 53 | this.blobs.core.on('peer-remove', this._boundPeerUpdate) | |
| 54 | this.blobs.core.on('upload', this._boundOnUpload) | |
| 55 | this.blobs.core.on('download', this._boundOnDownload) | |
| 56 | } | |
| 57 | ||
| 58 | async _close () { | |
| 59 | this.blobs.core.off('peer-add', this._boundPeerUpdate) | |
| 60 | this.blobs.core.off('peer-remove', this._boundPeerUpdate) | |
| 61 | this.blobs.core.off('upload', this._boundOnUpload) | |
| 62 | this.blobs.core.off('download', this._boundOnDownload) | |
| 63 | this.drive.monitors.delete(this) | |
| 64 | } | |
| 65 | ||
| 66 | _setEntryInfo () { | |
| 67 | if (!this.downloadStats.targetBytes || !this.downloadStats.targetBlocks) { | |
| 68 | this.downloadStats.targetBytes = this.entry.value.blob.byteLength | |
| 69 | this.downloadStats.targetBlocks = this.entry.value.blob.blockLength | |
| 70 | } | |
| 71 | ||
| 72 | if (!this.uploadStats.targetBytes || !this.uploadStats.targetBlocks) { | |
| 73 | this.uploadStats.targetBytes = this.entry.value.blob.byteLength | |
| 74 | this.uploadStats.targetBlocks = this.entry.value.blob.blockLength | |
| 75 | } | |
| 76 | } | |
| 77 | ||
| 78 | _onUpload (index, bytes, from) { | |
| 79 | this._updateStats(this.uploadSpeedometer, this.uploadStats, index, bytes, from) | |
| 80 | } | |
| 81 | ||
| 82 | _onDownload (index, bytes, from) { | |
| 83 | this._updateStats(this.downloadSpeedometer, this.downloadStats, index, bytes, from) | |
| 84 | } | |
| 85 | ||
| 86 | _updatePeers () { | |
| 87 | this.uploadStats.peers = this.downloadStats.peers = this.peers = this.blobs.core.peers.length | |
| 88 | } | |
| 89 | ||
| 90 | _updateStats (speed, stats, index, bytes, from) { | |
| 91 | if (!this.entry || this.closing) return | |
| 92 | if (!isWithinRange(index, this.entry)) return | |
| 93 | ||
| 94 | if (!stats.startTime) stats.startTime = Date.now() | |
| 95 | ||
| 96 | stats.speed = speed(bytes) | |
| 97 | stats.blocks++ | |
| 98 | stats.monitoringBytes += bytes | |
| 99 | stats.totalBytes += bytes | |
| 100 | // NOTE: you should not rely on the percentage until the monitor is initialized with the local state of the file | |
| 101 | stats.percentage = toFixed(stats.blocks / stats.targetBlocks * 100) | |
| 102 | ||
| 103 | this.emit('update') | |
| 104 | } | |
| 105 | ||
| 106 | downloadSpeed () { | |
| 107 | return this.downloadSpeedometer ? this.downloadSpeedometer() : 0 | |
| 108 | } | |
| 109 | ||
| 110 | uploadSpeed () { | |
| 111 | return this.uploadSpeedometer ? this.uploadSpeedometer() : 0 | |
| 112 | } | |
| 113 | } | |
| 114 | ||
| 115 | function isWithinRange (index, entry) { | |
| 116 | if (!entry || !entry.value) return | |
| 117 | const { blockOffset, blockLength } = entry.value.blob | |
| 118 | return index >= blockOffset && index < blockOffset + blockLength | |
| 119 | } | |
| 120 | ||
| 121 | function toFixed (n) { | |
| 122 | return Math.round(n * 100) / 100 | |
| 123 | } |
| … | … | @@ -0,0 +1,48 @@ |
| 1 | { | |
| 2 | "name": "hyperdrive", | |
| 3 | "version": "11.13.4", | |
| 4 | "description": "Hyperdrive is a secure, real-time distributed file system", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js", | |
| 8 | "lib/**.js" | |
| 9 | ], | |
| 10 | "repository": { | |
| 11 | "type": "git", | |
| 12 | "url": "git+https://github.com/holepunchto/hyperdrive.git" | |
| 13 | }, | |
| 14 | "scripts": { | |
| 15 | "test": "standard && brittle test.js" | |
| 16 | }, | |
| 17 | "author": "Holepunch", | |
| 18 | "license": "Apache-2.0", | |
| 19 | "bugs": { | |
| 20 | "url": "https://github.com/holepunchto/hyperdrive/issues" | |
| 21 | }, | |
| 22 | "homepage": "https://github.com/holepunchto/hyperdrive#readme", | |
| 23 | "dependencies": { | |
| 24 | "hyperbee": "^2.11.1", | |
| 25 | "hyperblobs": "^2.3.0", | |
| 26 | "hypercore": "^10.33.0", | |
| 27 | "hypercore-errors": "^1.0.0", | |
| 28 | "is-options": "^1.0.2", | |
| 29 | "mirror-drive": "^1.2.0", | |
| 30 | "ready-resource": "^1.0.0", | |
| 31 | "safety-catch": "^1.0.2", | |
| 32 | "speedometer": "^1.1.0", | |
| 33 | "streamx": "^2.12.4", | |
| 34 | "sub-encoder": "^2.1.1", | |
| 35 | "unix-path-resolve": "^1.0.2" | |
| 36 | }, | |
| 37 | "devDependencies": { | |
| 38 | "b4a": "^1.6.0", | |
| 39 | "brittle": "^3.1.0", | |
| 40 | "corestore": "^6.8.1", | |
| 41 | "hypercore-crypto": "^3.4.0", | |
| 42 | "hyperdht": "^6.6.0", | |
| 43 | "hyperswarm": "^4.0.0", | |
| 44 | "random-access-memory": "^6.0.0", | |
| 45 | "standard": "^17.0.0", | |
| 46 | "test-tmp": "^1.3.0" | |
| 47 | } | |
| 48 | } |
| … | … | @@ -0,0 +1,7 @@ |
| 1 | language: node_js | |
| 2 | sudo: false | |
| 3 | node_js: | |
| 4 | - 4 | |
| 5 | - 6 | |
| 6 | - 8 | |
| 7 | - 9 |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2018 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,39 @@ |
| 1 | # is-options | |
| 2 | ||
| 3 | Easily check if input is an options map | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install is-options | |
| 7 | ``` | |
| 8 | ||
| 9 | [](https://travis-ci.org/mafintosh/is-options) | |
| 10 | ||
| 11 | ## Usage | |
| 12 | ||
| 13 | ``` js | |
| 14 | var isOptions = require('is-options') | |
| 15 | ||
| 16 | thing('', {foo: true}) // key='', opts={foo: true} | |
| 17 | thing({foo: true}) // key=undefined, opts={foo: true} | |
| 18 | thing(Buffer.from('buf'), {foo: true}) // key=Buffer('buf'), opts={foo: true} | |
| 19 | ||
| 20 | function thing (optionalKey, opts) { | |
| 21 | if (isOptions(optionalKey)) { | |
| 22 | opts = optionalKey | |
| 23 | optionalKey = undefined | |
| 24 | } | |
| 25 | ||
| 26 | console.log('key', optionalKey) | |
| 27 | console.log('options', opts) | |
| 28 | } | |
| 29 | ``` | |
| 30 | ||
| 31 | ## API | |
| 32 | ||
| 33 | #### `bool = isOptions(obj)` | |
| 34 | ||
| 35 | Returns true is `obj` is an object and not a buffer | |
| 36 | ||
| 37 | ## License | |
| 38 | ||
| 39 | MIT |
| … | … | @@ -0,0 +1,5 @@ |
| 1 | const b4a = require('b4a') | |
| 2 | ||
| 3 | module.exports = function isOptions (opts) { | |
| 4 | return typeof opts === 'object' && opts && !b4a.isBuffer(opts) | |
| 5 | } |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | { | |
| 2 | "name": "is-options", | |
| 3 | "version": "1.0.2", | |
| 4 | "description": "Easily check if input is an options map", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "b4a": "^1.1.1" | |
| 8 | }, | |
| 9 | "devDependencies": { | |
| 10 | "standard": "^11.0.1", | |
| 11 | "tape": "^4.9.0" | |
| 12 | }, | |
| 13 | "scripts": { | |
| 14 | "test": "standard && tape test.js" | |
| 15 | }, | |
| 16 | "repository": { | |
| 17 | "type": "git", | |
| 18 | "url": "https://github.com/mafintosh/is-options.git" | |
| 19 | }, | |
| 20 | "author": "Mathias Buus (@mafintosh)", | |
| 21 | "license": "MIT", | |
| 22 | "bugs": { | |
| 23 | "url": "https://github.com/mafintosh/is-options/issues" | |
| 24 | }, | |
| 25 | "homepage": "https://github.com/mafintosh/is-options" | |
| 26 | } |
| … | … | @@ -0,0 +1,13 @@ |
| 1 | const tape = require('tape') | |
| 2 | const isOptions = require('./') | |
| 3 | ||
| 4 | tape('works', function (t) { | |
| 5 | t.ok(isOptions({})) | |
| 6 | t.notOk(isOptions('')) | |
| 7 | t.notOk(isOptions(Buffer.from('hi'))) | |
| 8 | t.notOk(isOptions()) | |
| 9 | t.notOk(isOptions(null)) | |
| 10 | t.notOk(isOptions(42)) | |
| 11 | t.notOk(isOptions(undefined)) | |
| 12 | t.end() | |
| 13 | }) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,81 @@ |
| 1 | # mirror-drive | |
| 2 | ||
| 3 | Mirror a Hyperdrive or Localdrive into another one | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i mirror-drive | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | import MirrorDrive from 'mirror-drive' | |
| 13 | ||
| 14 | const src = new Localdrive('./src') | |
| 15 | const dst = new Hyperdrive(store) | |
| 16 | ||
| 17 | const mirror = new MirrorDrive(src, dst) | |
| 18 | console.log(mirror.count) // => { files: 0, add: 0, remove: 0, change: 0 } | |
| 19 | ||
| 20 | for await (const diff of mirror) { | |
| 21 | console.log(diff) /* { | |
| 22 | op: 'add', | |
| 23 | key: '/new-file.txt', | |
| 24 | bytesRemoved: 0, | |
| 25 | bytesAdded: 4 | |
| 26 | }*/ | |
| 27 | } | |
| 28 | ||
| 29 | console.log(mirror.count) // => { files: 1, add: 1, remove: 0, change: 0 } | |
| 30 | ``` | |
| 31 | ||
| 32 | Another example: | |
| 33 | ||
| 34 | ```js | |
| 35 | const mirror = new MirrorDrive(src, dst) | |
| 36 | ||
| 37 | console.log(mirror.count) // => { files: 0, add: 0, remove: 0, change: 0 } | |
| 38 | await mirror.done() | |
| 39 | console.log(mirror.count) // => { files: 1, add: 1, remove: 0, change: 0 } | |
| 40 | ``` | |
| 41 | ||
| 42 | ## API | |
| 43 | ||
| 44 | #### `const mirror = new MirrorDrive(src, dst, [options])` | |
| 45 | ||
| 46 | Creates a mirror instance to efficiently move `src` drive into `dst` drive. | |
| 47 | ||
| 48 | Available `options`: | |
| 49 | ||
| 50 | ```js | |
| 51 | { | |
| 52 | prefix: '/', | |
| 53 | dryRun: false, | |
| 54 | prune: true, | |
| 55 | includeEquals: false, | |
| 56 | filter: (key) => true, | |
| 57 | metadataEquals: (srcMetadata, dstMetadata) => { ... } | |
| 58 | batch: false, | |
| 59 | entries: null // Array of key entries (if you use this then prefix is ignored) | |
| 60 | ignore: String || Array // Ignore source files and folders by name. | |
| 61 | transformers: [] // Array of factory functions (key) => stream | |
| 62 | } | |
| 63 | ``` | |
| 64 | ||
| 65 | #### `mirror.count` | |
| 66 | ||
| 67 | It counts the total files proccessed, added, removed, and changed. | |
| 68 | ||
| 69 | Default value: `{ files: 0, add: 0, remove: 0, change: 0 }` | |
| 70 | ||
| 71 | #### `await mirror.done()` | |
| 72 | ||
| 73 | It starts processing all the diffing until is done. | |
| 74 | ||
| 75 | ## Transformers | |
| 76 | ||
| 77 | Apply content transformers during mirroring. Each item in `transformers` is a factory function `(key) => stream | null` that returns a new stream (or `null`) for that file. MirrorDrive always pipes through all provided transformers. If a `transformer` returns `null` it is safely skipped. | |
| 78 | ||
| 79 | ## License | |
| 80 | ||
| 81 | Apache-2.0 |
| … | … | @@ -0,0 +1,418 @@ |
| 1 | const EventEmitter = require('events') | |
| 2 | const sameData = require('same-data') | |
| 3 | const unixPathResolve = require('unix-path-resolve') | |
| 4 | const streamEquals = require('binary-stream-equals') | |
| 5 | const speedometer = require('speedometer') | |
| 6 | const { pipelinePromise, isStream } = require('streamx') | |
| 7 | const RabinStream = require('rabin-stream') | |
| 8 | ||
| 9 | const SAME = 0 | |
| 10 | const DIFF = 1 | |
| 11 | const DIFF_META = 2 | |
| 12 | ||
| 13 | class Monitor extends EventEmitter { | |
| 14 | constructor(mirror, { interval = 250 } = {}) { | |
| 15 | super() | |
| 16 | ||
| 17 | this.mirror = mirror | |
| 18 | this.interval = setInterval(this.update.bind(this), interval) | |
| 19 | this.stats = null | |
| 20 | this.index = mirror.monitors.push(this) - 1 | |
| 21 | ||
| 22 | this.update() // populate latest stats | |
| 23 | } | |
| 24 | ||
| 25 | get preloaded() { | |
| 26 | return this.mirror.preloaded | |
| 27 | } | |
| 28 | ||
| 29 | get destroyed() { | |
| 30 | return this.index === -1 | |
| 31 | } | |
| 32 | ||
| 33 | update() { | |
| 34 | if (this.index === -1) return | |
| 35 | ||
| 36 | // NOTE: immutable (append-only) data structure | |
| 37 | this.stats = { | |
| 38 | peers: this.mirror.peers.length, | |
| 39 | download: { | |
| 40 | bytes: this.mirror.downloadedBytes, | |
| 41 | blocks: this.mirror.downloadedBlocks, | |
| 42 | speed: this.mirror.downloadSpeed(), | |
| 43 | progress: this.mirror.downloadProgress | |
| 44 | }, | |
| 45 | upload: { | |
| 46 | bytes: this.mirror.uploadedBytes, | |
| 47 | blocks: this.mirror.uploadedBlocks, | |
| 48 | speed: this.mirror.uploadSpeed() | |
| 49 | } | |
| 50 | } | |
| 51 | ||
| 52 | this.emit('update', this.stats) | |
| 53 | } | |
| 54 | ||
| 55 | destroy() { | |
| 56 | if (this.index === -1) return | |
| 57 | ||
| 58 | clearInterval(this.interval) | |
| 59 | ||
| 60 | const head = this.mirror.monitors.pop() | |
| 61 | if (head !== this) { | |
| 62 | this.mirror.monitors[this.index] = head | |
| 63 | head.index = this.index | |
| 64 | } | |
| 65 | ||
| 66 | this.index = -1 | |
| 67 | this.emit('destroy') | |
| 68 | } | |
| 69 | } | |
| 70 | ||
| 71 | module.exports = class MirrorDrive { | |
| 72 | constructor(src, dst, opts = {}) { | |
| 73 | this.src = src | |
| 74 | this.dst = dst | |
| 75 | ||
| 76 | this.prefix = toArray(opts.prefix || '/') | |
| 77 | this.dedup = !!opts.dedup | |
| 78 | this.dryRun = !!opts.dryRun | |
| 79 | this.prune = opts.prune !== false | |
| 80 | this.preload = opts.preload !== false && !!src.getBlobs | |
| 81 | this.preloaded = false | |
| 82 | this.includeProgress = !!opts.progress && !!src.getBlobs | |
| 83 | this.includeEquals = !!opts.includeEquals | |
| 84 | this.filter = opts.filter || null | |
| 85 | this.metadataEquals = opts.metadataEquals || null | |
| 86 | this.batch = !!opts.batch | |
| 87 | this.entries = opts.entries || null | |
| 88 | this.transformers = opts.transformers || [] | |
| 89 | ||
| 90 | this.count = { files: 0, add: 0, remove: 0, change: 0 } | |
| 91 | this.bytesRemoved = 0 | |
| 92 | this.bytesAdded = 0 | |
| 93 | this.ignore = opts.ignore ? toIgnoreFunction(opts.ignore) : null | |
| 94 | this.finished = false | |
| 95 | ||
| 96 | this.downloadedBlocks = 0 | |
| 97 | this.downloadedBlocksEstimate = 0 | |
| 98 | this.downloadedBytes = 0 | |
| 99 | this.downloadSpeed = this.includeProgress ? speedometer() : null | |
| 100 | ||
| 101 | this.uploadedBlocks = 0 | |
| 102 | this.uploadedBytes = 0 | |
| 103 | this.uploadSpeed = this.includeProgress ? speedometer() : null | |
| 104 | ||
| 105 | this.monitors = [] | |
| 106 | this.iterator = this._init() | |
| 107 | } | |
| 108 | ||
| 109 | [Symbol.asyncIterator]() { | |
| 110 | return this.iterator | |
| 111 | } | |
| 112 | ||
| 113 | get peers() { | |
| 114 | return this.src.core?.peers || [] | |
| 115 | } | |
| 116 | ||
| 117 | get downloadProgress() { | |
| 118 | if (this.finished) return 1 | |
| 119 | if (!this.downloadedBlocksEstimate) return 0 | |
| 120 | // leave 3% incase our estimatation is wrong - then at least it wont appear done... | |
| 121 | return Math.min(0.99, this.downloadedBlocks / this.downloadedBlocksEstimate) | |
| 122 | } | |
| 123 | ||
| 124 | monitor(opts) { | |
| 125 | this.includeProgress = true | |
| 126 | if (this.downloadSpeed === null) this.downloadSpeed = speedometer() | |
| 127 | if (this.uploadSpeed === null) this.uploadSpeed = speedometer() | |
| 128 | return new Monitor(this, opts) | |
| 129 | } | |
| 130 | ||
| 131 | async done() { | |
| 132 | while (true) { | |
| 133 | const { done } = await this.iterator.next() | |
| 134 | if (done) break | |
| 135 | } | |
| 136 | } | |
| 137 | ||
| 138 | _onupload(index, byteLength) { | |
| 139 | this.uploadedBlocks++ | |
| 140 | this.uploadedBytes += byteLength | |
| 141 | this.uploadSpeed(byteLength) | |
| 142 | } | |
| 143 | ||
| 144 | _ondownload(index, byteLength) { | |
| 145 | this.downloadedBlocks++ | |
| 146 | this.downloadedBytes += byteLength | |
| 147 | this.downloadSpeed(byteLength) | |
| 148 | } | |
| 149 | ||
| 150 | async _flushPreload(entries) { | |
| 151 | const ranges = [] | |
| 152 | const blobs = await this.src.getBlobs() | |
| 153 | ||
| 154 | for (const entry of entries) { | |
| 155 | const blob = entry.value.blob | |
| 156 | if (!blob) continue | |
| 157 | const dl = blobs.core.download({ start: blob.blockOffset, length: blob.blockLength }) | |
| 158 | await dl.ready() | |
| 159 | ranges.push(dl) | |
| 160 | } | |
| 161 | ||
| 162 | this.downloadedBlocksEstimate = this.downloadedBlocks | |
| 163 | for (const dl of ranges) { | |
| 164 | if (!dl.request.context) continue | |
| 165 | this.downloadedBlocksEstimate += dl.request.context.end - dl.request.context.start | |
| 166 | } | |
| 167 | this.preloaded = true | |
| 168 | for (const m of this.monitors) { | |
| 169 | m.emit('preloaded') | |
| 170 | } | |
| 171 | ||
| 172 | for (const dl of ranges) { | |
| 173 | await dl.done() | |
| 174 | } | |
| 175 | } | |
| 176 | ||
| 177 | async *_init() { | |
| 178 | try { | |
| 179 | for await (const out of this._mirror()) yield out | |
| 180 | } finally { | |
| 181 | while (this.monitors.length) { | |
| 182 | this.monitors[this.monitors.length - 1].destroy() | |
| 183 | } | |
| 184 | } | |
| 185 | } | |
| 186 | ||
| 187 | async *_mirror() { | |
| 188 | await this.src.ready() | |
| 189 | await this.dst.ready() | |
| 190 | ||
| 191 | if (this.dst.core && !this.dst.core.writable) throw new Error('Destination must be writable') | |
| 192 | ||
| 193 | const blobs = this.includeProgress ? await this.src.getBlobs() : null | |
| 194 | const onupload = this._onupload.bind(this) | |
| 195 | const ondownload = this._ondownload.bind(this) | |
| 196 | ||
| 197 | if (blobs) { | |
| 198 | blobs.core.on('upload', onupload) | |
| 199 | blobs.core.on('download', ondownload) | |
| 200 | } | |
| 201 | ||
| 202 | const dst = this.batch ? this.dst.batch() : this.dst | |
| 203 | ||
| 204 | if (this.prune) { | |
| 205 | for await (const [key, dstEntry, srcEntry] of this._list(this.dst, this.src, null)) { | |
| 206 | if (srcEntry) continue | |
| 207 | ||
| 208 | this.count.remove++ | |
| 209 | this.bytesRemoved += blobLength(dstEntry) | |
| 210 | yield { op: 'remove', key, bytesRemoved: blobLength(dstEntry), bytesAdded: 0 } | |
| 211 | ||
| 212 | if (!this.dryRun) await dst.del(key) | |
| 213 | } | |
| 214 | } | |
| 215 | ||
| 216 | if (this.preload) { | |
| 217 | const entries = [] | |
| 218 | ||
| 219 | for await (const [, srcEntry] of this._list(this.src, null, this.filter)) { | |
| 220 | entries.push(srcEntry) | |
| 221 | } | |
| 222 | ||
| 223 | // flush in bg | |
| 224 | this._flushPreload(entries).catch(noop) | |
| 225 | } | |
| 226 | ||
| 227 | for await (const [key, srcEntry, dstEntry] of this._list(this.src, dst, this.filter)) { | |
| 228 | if (!srcEntry) continue // Due entries option, src entry might not exist probably because it was pruned | |
| 229 | ||
| 230 | this.count.files++ | |
| 231 | ||
| 232 | // If transformers are provided, we can't know if same before running them | |
| 233 | const hasTransformers = this.transformers && this.transformers.length > 0 | |
| 234 | ||
| 235 | const s = hasTransformers === false ? await same(this, srcEntry, dstEntry) : DIFF | |
| 236 | ||
| 237 | if (s === SAME) { | |
| 238 | if (this.includeEquals) { | |
| 239 | yield { op: 'equal', key, bytesRemoved: 0, bytesAdded: 0 } | |
| 240 | } | |
| 241 | continue | |
| 242 | } | |
| 243 | ||
| 244 | const onlyMetadata = s === DIFF_META && !!dst.putEntry | |
| 245 | const dedup = !!dst.putEntry && this.dedup | |
| 246 | ||
| 247 | if (dstEntry) { | |
| 248 | const removed = onlyMetadata ? 0 : blobLength(dstEntry) | |
| 249 | const added = onlyMetadata ? 0 : blobLength(srcEntry) | |
| 250 | this.count.change++ | |
| 251 | this.bytesRemoved += removed | |
| 252 | this.bytesAdded += added | |
| 253 | yield { | |
| 254 | op: 'change', | |
| 255 | key, | |
| 256 | bytesRemoved: removed, | |
| 257 | bytesAdded: added | |
| 258 | } | |
| 259 | } else { | |
| 260 | this.count.add++ | |
| 261 | this.bytesAdded += blobLength(srcEntry) | |
| 262 | yield { op: 'add', key, bytesRemoved: 0, bytesAdded: blobLength(srcEntry) } | |
| 263 | } | |
| 264 | ||
| 265 | if (this.dryRun) { | |
| 266 | continue | |
| 267 | } | |
| 268 | ||
| 269 | const transformers = [] | |
| 270 | ||
| 271 | for (const transformer of this.transformers) { | |
| 272 | if (typeof transformer !== 'function') throw new Error('transformer must be a function') | |
| 273 | ||
| 274 | const stream = transformer(key) | |
| 275 | ||
| 276 | if (stream === null) continue | |
| 277 | if (!isStream(stream)) throw new Error('transformer must return a stream') | |
| 278 | ||
| 279 | transformers.push(stream) | |
| 280 | } | |
| 281 | ||
| 282 | if (srcEntry.value.linkname) { | |
| 283 | await dst.symlink(key, srcEntry.value.linkname) | |
| 284 | } else if (!onlyMetadata) { | |
| 285 | if (dedup) transformers.push(new RabinStream()) | |
| 286 | await pipelinePromise( | |
| 287 | this.src.createReadStream(srcEntry), | |
| 288 | ...transformers, | |
| 289 | dst.createWriteStream(key, { | |
| 290 | dedup, | |
| 291 | executable: srcEntry.value.executable, | |
| 292 | metadata: srcEntry.value.metadata | |
| 293 | }) | |
| 294 | ) | |
| 295 | } else { | |
| 296 | await dst.putEntry(key, { | |
| 297 | executable: srcEntry.value.executable, | |
| 298 | linkname: srcEntry.value.linkname, | |
| 299 | blob: dstEntry.value.blob, | |
| 300 | metadata: srcEntry.value.metadata | |
| 301 | }) | |
| 302 | } | |
| 303 | } | |
| 304 | ||
| 305 | if (this.batch) await dst.flush() | |
| 306 | ||
| 307 | if (blobs) { | |
| 308 | blobs.core.off('upload', onupload) | |
| 309 | blobs.core.off('download', ondownload) | |
| 310 | } | |
| 311 | ||
| 312 | this.finished = true | |
| 313 | } | |
| 314 | ||
| 315 | async *_list(a, b, filter) { | |
| 316 | const lists = [] | |
| 317 | ||
| 318 | for (const prefix of this.prefix) { | |
| 319 | if (this.entries) { | |
| 320 | lists.push(this.entries) | |
| 321 | } else { | |
| 322 | const stream = a.list(prefix, { ignore: this.ignore }) | |
| 323 | if (stream.on) { | |
| 324 | stream.on('error', noop) | |
| 325 | stream.resume() | |
| 326 | stream.pause() | |
| 327 | } | |
| 328 | lists.push(stream) | |
| 329 | } | |
| 330 | } | |
| 331 | ||
| 332 | for (let i = 0; i < this.prefix.length; i++) { | |
| 333 | const prefix = this.prefix[i] | |
| 334 | const list = lists[i] | |
| 335 | ||
| 336 | for await (const entry of list) { | |
| 337 | const key = typeof entry === 'object' ? entry.key : entry | |
| 338 | ||
| 339 | if (filter && !filter(key)) continue | |
| 340 | ||
| 341 | const entryA = await a.entry(entry) | |
| 342 | const entryB = b ? await b.entry(key) : null | |
| 343 | ||
| 344 | yield [key, entryA, entryB] | |
| 345 | } | |
| 346 | ||
| 347 | if (prefix !== '/' && (!filter || filter(prefix))) { | |
| 348 | const entryA = await a.entry(prefix) | |
| 349 | const entryB = b ? await b.entry(prefix) : null | |
| 350 | ||
| 351 | if (!entryA && !entryB) continue | |
| 352 | ||
| 353 | yield [prefix, entryA, entryB] | |
| 354 | } | |
| 355 | } | |
| 356 | } | |
| 357 | } | |
| 358 | ||
| 359 | function blobLength(entry) { | |
| 360 | return entry.value.blob ? entry.value.blob.byteLength : 0 | |
| 361 | } | |
| 362 | ||
| 363 | async function same(m, srcEntry, dstEntry) { | |
| 364 | if (!dstEntry) return DIFF | |
| 365 | ||
| 366 | if (srcEntry.value.linkname || dstEntry.value.linkname) { | |
| 367 | return srcEntry.value.linkname === dstEntry.value.linkname ? SAME : DIFF | |
| 368 | } | |
| 369 | ||
| 370 | if (!sizeEquals(srcEntry, dstEntry)) return DIFF | |
| 371 | ||
| 372 | const eq = await streamEquals(m.src.createReadStream(srcEntry), m.dst.createReadStream(dstEntry)) | |
| 373 | const diff = eq ? DIFF_META : DIFF | |
| 374 | ||
| 375 | if (srcEntry.value.executable !== dstEntry.value.executable) return diff | |
| 376 | if (!metadataEquals(m, srcEntry, dstEntry)) return diff | |
| 377 | ||
| 378 | return eq ? SAME : DIFF | |
| 379 | } | |
| 380 | ||
| 381 | function sizeEquals(srcEntry, dstEntry) { | |
| 382 | const srcBlob = srcEntry.value.blob | |
| 383 | const dstBlob = dstEntry.value.blob | |
| 384 | ||
| 385 | if (!srcBlob && !dstBlob) return true | |
| 386 | if (!srcBlob || !dstBlob) return false | |
| 387 | ||
| 388 | return srcBlob.byteLength === dstBlob.byteLength | |
| 389 | } | |
| 390 | ||
| 391 | function metadataEquals(m, srcEntry, dstEntry) { | |
| 392 | if (!m.src.supportsMetadata || !m.dst.supportsMetadata) return true | |
| 393 | ||
| 394 | const srcMetadata = srcEntry.value.metadata | |
| 395 | const dstMetadata = dstEntry.value.metadata | |
| 396 | ||
| 397 | if (m.metadataEquals) { | |
| 398 | return m.metadataEquals(srcMetadata, dstMetadata) | |
| 399 | } | |
| 400 | ||
| 401 | const noMetadata = !srcMetadata && !dstMetadata | |
| 402 | const identicalMetadata = !!(srcMetadata && dstMetadata && sameData(srcMetadata, dstMetadata)) | |
| 403 | ||
| 404 | return noMetadata || identicalMetadata | |
| 405 | } | |
| 406 | ||
| 407 | function toIgnoreFunction(ignore) { | |
| 408 | if (typeof ignore === 'function') return ignore | |
| 409 | ||
| 410 | const all = [].concat(ignore).map((e) => unixPathResolve('/', e)) | |
| 411 | return (key) => all.some((path) => path === key || key.startsWith(path + '/')) | |
| 412 | } | |
| 413 | ||
| 414 | function toArray(prefix) { | |
| 415 | return Array.isArray(prefix) ? prefix : [prefix] | |
| 416 | } | |
| 417 | ||
| 418 | function noop() {} |
| … | … | @@ -0,0 +1,61 @@ |
| 1 | { | |
| 2 | "name": "mirror-drive", | |
| 3 | "version": "1.13.0", | |
| 4 | "description": "Mirror a hyperdrive or localdrive into another one", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js", | |
| 8 | "lib/**.js" | |
| 9 | ], | |
| 10 | "imports": { | |
| 11 | "events": { | |
| 12 | "bare": "bare-events" | |
| 13 | }, | |
| 14 | "os": { | |
| 15 | "bare": "bare-os", | |
| 16 | "default": "os" | |
| 17 | }, | |
| 18 | "stream": { | |
| 19 | "bare": "bare-stream", | |
| 20 | "default": "stream" | |
| 21 | } | |
| 22 | }, | |
| 23 | "scripts": { | |
| 24 | "format": "prettier --write .", | |
| 25 | "lint": "prettier --check . && lunte", | |
| 26 | "test": "npm run test:node && npm run test:bare", | |
| 27 | "test:node": "brittle-node test/*.js", | |
| 28 | "test:bare": "brittle-bare test/*.js" | |
| 29 | }, | |
| 30 | "repository": { | |
| 31 | "type": "git", | |
| 32 | "url": "https://github.com/holepunchto/mirror-drive.git" | |
| 33 | }, | |
| 34 | "author": "Lucas Barrena (@LuKks)", | |
| 35 | "license": "Apache-2.0", | |
| 36 | "bugs": { | |
| 37 | "url": "https://github.com/holepunchto/mirror-drive/issues" | |
| 38 | }, | |
| 39 | "homepage": "https://github.com/holepunchto/mirror-drive", | |
| 40 | "devDependencies": { | |
| 41 | "b4a": "^1.6.0", | |
| 42 | "bare-os": "^3.0.1", | |
| 43 | "bare-stream": "^2.6.5", | |
| 44 | "brittle": "^3.1.0", | |
| 45 | "corestore": "^6.0.6", | |
| 46 | "hyperdrive": "^13.0.0", | |
| 47 | "localdrive": "^2.2.0", | |
| 48 | "lunte": "^1.0.0", | |
| 49 | "prettier": "^3.6.2", | |
| 50 | "prettier-config-holepunch": "^2.0.0" | |
| 51 | }, | |
| 52 | "dependencies": { | |
| 53 | "bare-events": "^2.8.2", | |
| 54 | "binary-stream-equals": "^1.0.0", | |
| 55 | "rabin-stream": "^2.0.0", | |
| 56 | "same-data": "^1.0.0", | |
| 57 | "speedometer": "^1.1.0", | |
| 58 | "streamx": "^2.22.1", | |
| 59 | "unix-path-resolve": "^1.0.2" | |
| 60 | } | |
| 61 | } |
| … | … | @@ -0,0 +1,7 @@ |
| 1 | language: node_js | |
| 2 | node_js: | |
| 3 | - '10' | |
| 4 | - '12' | |
| 5 | - '14' | |
| 6 | cache: | |
| 7 | npm: false |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2014 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,72 @@ |
| 1 | # mutexify | |
| 2 | ||
| 3 | Bike shed mutex lock implementation in node.js | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install mutexify | |
| 7 | ``` | |
| 8 | ||
| 9 | [](http://travis-ci.org/mafintosh/mutexify) | |
| 10 | ||
| 11 | Hasn't this been done before? Yes, but the specific semantics of this made some of my code simpler. | |
| 12 | ||
| 13 | ## Usage | |
| 14 | ||
| 15 | ||
| 16 | ``` js | |
| 17 | var mutexify = require('mutexify') | |
| 18 | var lock = mutexify() | |
| 19 | ||
| 20 | lock(function(release) { | |
| 21 | console.log('i am now locked') | |
| 22 | setTimeout(function() { | |
| 23 | release() | |
| 24 | }, 1000) | |
| 25 | }) | |
| 26 | ||
| 27 | lock(function(release) { | |
| 28 | console.log('1 second later') | |
| 29 | release() | |
| 30 | }) | |
| 31 | ``` | |
| 32 | ||
| 33 | A common pattern is to call a callback after you release the lock. | |
| 34 | To do this in a one-liner pass the callback and the value to `release(cb, err, value)` | |
| 35 | ||
| 36 | ``` js | |
| 37 | var write = function(data, cb) { | |
| 38 | lock(function(release) { | |
| 39 | fs.writeFile('locked-file.txt', data, release.bind(null, cb)) | |
| 40 | }) | |
| 41 | } | |
| 42 | ``` | |
| 43 | ||
| 44 | `mutexify` guarantees that the order that a mutex was requested in is the order that access will be given. | |
| 45 | ||
| 46 | You can read the lock's current state on the `lock.locked` property. | |
| 47 | ||
| 48 | ### Usage with promises | |
| 49 | ||
| 50 | `mutexify` provides a Promise-based alternative. | |
| 51 | ||
| 52 | ```js | |
| 53 | const mutexify = require('mutexify/promise') | |
| 54 | ||
| 55 | ;(async () => { | |
| 56 | var lock = mutexify() | |
| 57 | ||
| 58 | var release = await lock() | |
| 59 | console.log('i am now locked') | |
| 60 | setTimeout(function () { | |
| 61 | release() | |
| 62 | }, 1000) | |
| 63 | ||
| 64 | release = await lock() | |
| 65 | console.log('1 second later') | |
| 66 | release() | |
| 67 | })() | |
| 68 | ``` | |
| 69 | ||
| 70 | ## License | |
| 71 | ||
| 72 | MIT |
| … | … | @@ -0,0 +1,31 @@ |
| 1 | var queueTick = require('queue-tick') | |
| 2 | ||
| 3 | var mutexify = function () { | |
| 4 | var queue = [] | |
| 5 | var used = null | |
| 6 | ||
| 7 | var call = function () { | |
| 8 | used(release) | |
| 9 | } | |
| 10 | ||
| 11 | var acquire = function (fn) { | |
| 12 | if (used) return queue.push(fn) | |
| 13 | used = fn | |
| 14 | acquire.locked = true | |
| 15 | queueTick(call) | |
| 16 | return 0 | |
| 17 | } | |
| 18 | ||
| 19 | acquire.locked = false | |
| 20 | ||
| 21 | var release = function (fn, err, value) { | |
| 22 | used = null | |
| 23 | acquire.locked = false | |
| 24 | if (queue.length) acquire(queue.shift()) | |
| 25 | if (fn) fn(err, value) | |
| 26 | } | |
| 27 | ||
| 28 | return acquire | |
| 29 | } | |
| 30 | ||
| 31 | module.exports = mutexify |
| … | … | @@ -0,0 +1,32 @@ |
| 1 | { | |
| 2 | "name": "mutexify", | |
| 3 | "version": "1.4.0", | |
| 4 | "description": "mutex lock for javascript", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "queue-tick": "^1.0.0" | |
| 8 | }, | |
| 9 | "devDependencies": { | |
| 10 | "standard": "^14.3.3", | |
| 11 | "tape": "^3.0.2" | |
| 12 | }, | |
| 13 | "scripts": { | |
| 14 | "test": "tape test.js", | |
| 15 | "posttest": "npm run lint", | |
| 16 | "lint": "standard" | |
| 17 | }, | |
| 18 | "repository": { | |
| 19 | "type": "git", | |
| 20 | "url": "https://github.com/mafintosh/mutexify.git" | |
| 21 | }, | |
| 22 | "author": "Mathias Buus (@mafintosh)", | |
| 23 | "license": "MIT", | |
| 24 | "bugs": { | |
| 25 | "url": "https://github.com/mafintosh/mutexify/issues" | |
| 26 | }, | |
| 27 | "keywords": [ | |
| 28 | "mutex", | |
| 29 | "lock" | |
| 30 | ], | |
| 31 | "homepage": "https://github.com/mafintosh/mutexify" | |
| 32 | } |
| … | … | @@ -0,0 +1,18 @@ |
| 1 | var mutexify = require('.') | |
| 2 | ||
| 3 | var mutexifyPromise = function () { | |
| 4 | var lock = mutexify() | |
| 5 | ||
| 6 | var acquire = function () { | |
| 7 | return new Promise(lock) | |
| 8 | } | |
| 9 | ||
| 10 | Object.defineProperty(acquire, 'locked', { | |
| 11 | get: function () { return lock.locked }, | |
| 12 | enumerable: true | |
| 13 | }) | |
| 14 | ||
| 15 | return acquire | |
| 16 | } | |
| 17 | ||
| 18 | module.exports = mutexifyPromise |
| … | … | @@ -0,0 +1,78 @@ |
| 1 | var tape = require('tape') | |
| 2 | var mutexify = require('./') | |
| 3 | var mutexifyPromise = require('./promise') | |
| 4 | ||
| 5 | tape('locks', function (t) { | |
| 6 | t.plan(21) | |
| 7 | ||
| 8 | var lock = mutexify() | |
| 9 | var used = false | |
| 10 | t.ok(!lock.locked, 'not locked') | |
| 11 | ||
| 12 | for (var i = 0; i < 10; i++) { | |
| 13 | lock(function (release) { | |
| 14 | t.ok(!used, 'one at the time') | |
| 15 | t.ok(lock.locked, 'locked') | |
| 16 | used = true | |
| 17 | setImmediate(function () { | |
| 18 | used = false | |
| 19 | release() | |
| 20 | }) | |
| 21 | }) | |
| 22 | } | |
| 23 | }) | |
| 24 | ||
| 25 | tape('calls callback', function (t) { | |
| 26 | var lock = mutexify() | |
| 27 | ||
| 28 | var cb = function (err, value) { | |
| 29 | t.same(err, null) | |
| 30 | t.same(value, 'hello world') | |
| 31 | t.end() | |
| 32 | } | |
| 33 | ||
| 34 | lock(function (release) { | |
| 35 | release(cb, null, 'hello world') | |
| 36 | }) | |
| 37 | }) | |
| 38 | ||
| 39 | tape('calls the locking callbacks in a different stack', function (t) { | |
| 40 | t.plan(2) | |
| 41 | ||
| 42 | var lock = mutexify() | |
| 43 | ||
| 44 | var topScopeFinished = false | |
| 45 | var secondScopeFinished = false | |
| 46 | ||
| 47 | lock(function (release) { | |
| 48 | t.ok(topScopeFinished, 'the test function has already finished running') | |
| 49 | release() | |
| 50 | secondScopeFinished = true | |
| 51 | }) | |
| 52 | ||
| 53 | lock(function (release) { | |
| 54 | t.ok(secondScopeFinished, 'the last lock\'s call stack is done') | |
| 55 | release() | |
| 56 | t.end() | |
| 57 | }) | |
| 58 | ||
| 59 | topScopeFinished = true | |
| 60 | }) | |
| 61 | ||
| 62 | tape('locks with promises', async function (t) { | |
| 63 | t.plan(21) | |
| 64 | ||
| 65 | var lock = mutexifyPromise() | |
| 66 | var used = false | |
| 67 | t.ok(!lock.locked, 'not locked') | |
| 68 | for (var i = 0; i < 10; i++) { | |
| 69 | var release = await lock() | |
| 70 | t.ok(!used, 'one at the time') | |
| 71 | t.ok(lock.locked, 'locked') | |
| 72 | used = true | |
| 73 | setImmediate(function () { | |
| 74 | used = false | |
| 75 | release() | |
| 76 | }) | |
| 77 | } | |
| 78 | }) |
| … | … | @@ -0,0 +1,18 @@ |
| 1 | sudo: false | |
| 2 | language: node_js | |
| 3 | node_js: | |
| 4 | - lts/* | |
| 5 | jobs: | |
| 6 | include: | |
| 7 | - stage: npm release | |
| 8 | node_js: node | |
| 9 | script: echo "Deploying to npm ..." | |
| 10 | deploy: | |
| 11 | provider: npm | |
| 12 | email: | |
| 13 | secure: "Xxe2IEoT27tWFCCN3suwAtioeWSy5IhTEqqAIy6I6MbFAbqq4npU0lQ6mL5+kI5H6S2luFZwf1Eex1lXNfNAdGFI0sSnjOcKyw7HUsU/D7fNpQmtnnlSAJmz0z7qtWGzrNM/ihTFxAo5CYRJq2uemZ7tZTah0ePcXfiQxK3ZcnI2kS5yR+bQqZQVMy0yT0zHhTGoTsWaF3yAaZrlqLK5VNwIMahmRbz+BTtzNPgB9c9RA5tDla+GPevF3jaFw+Fwuv8M38idRERzJMJ7nYnBOC7XXssk25d+2ErsX61wqAxrXTaPmV9Mg6Njp5hLdl6PA2wMOSSHMfbqrRmjB2yvKzqvcB/Z34lhpd7/0PT2iWVA78aSGwLuTDIxcyeRNdYLMhVrMt/mPHTtzw2xckdg1NNkXEwyXowTLqMEB+8AnRt7Vuy/wOgw60l1AvYB3VJsmwkOR/bL3p8Q1weRj4hYJgB0W4YWeAh7d0NcvE4zwML849n5t37k1tHtcOY0ZVqea1ENJfja1IjFsNN4ki99y8wCm+ba5d+AJaqn0+ChluZfnQTqSqFHxfEEEy8v+TGXl+F8j6YT9iYfnyPHwYLjXx4GcFzlhSDiDF+Dxr155TU2XsSVB9zrL6Mv8TzOiazgosBFZO2Jz3EnWjEr21oFuKPHyb0FJ0nC5UMLHx3youU=" | |
| 14 | api_key: # 1ed6 | |
| 15 | secure: "zX7inYgOYTPpbMMU2BHPcGwbuPRadcFUfgANuvFk81ZWTgmoZkGSl/YUbacQmpg8QcuSvw6m5Gpv518hNKGHIlvKgf02PADJNoMoHNK+vpCzwhHxAEddAYO7Up18vNPs+erDlnDBa9LhnTbNeGh9bUvb1ylDDlvdsNCbDIU+dFsTxJCqG9jma0P5UJIbj6S+oMB+pVYZ64e82k7H3PE4PFctUPuahMcKhuO/rt9t/62IXzC7f1CIPwpm6bOfhkwtpO9UswyLkpscERIEWjLY5GgwIi/t4Zp3wV1J0EjczhA1+ZnUvCM9HelSAiWxqQrkCZSEFDI3Q9x9ZOgBCjwBt7BcNOb68UMASYFpVJZ4iVu+BmdhKPpSYqb+fIxxSFJuXOmND2wSNkxM/ETKkYRGu3rG9g52TMegtYYZXfD/oYLtozfdBVtCymjZ9Pt4Bw/IcpmcuS8gfWJqziXIlD+7hOaZHAwcTg7zpSQvnUN0pxcumztWCgSSUpIWffn2YdQRuM3ih7Za6Qeve7ZkuKdi6uzvuoLB8WXt6xy7X+KIG7Ng3LTvCWvIkBuOXNAV9sRyF5BNwm0QbVMAwYO5MeK2acUxOFxLFNQLEkl+2pKHWcDPAXFuX0sgRfCc336cCUAauN7B83IAScHGx50BjG4ldRPOPlXWQawRDcsBQm5h7W0=" | |
| 16 | on: | |
| 17 | tags: true | |
| 18 | node: node |
| … | … | @@ -0,0 +1,13 @@ |
| 1 | Copyright (c) 2017, Emil Bay <github@tixz.dk> | |
| 2 | ||
| 3 | Permission to use, copy, modify, and/or distribute this software for any | |
| 4 | purpose with or without fee is hereby granted, provided that the above | |
| 5 | copyright notice and this permission notice appear in all copies. | |
| 6 | ||
| 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
| 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
| 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
| 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
| 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
| 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
| 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| … | … | @@ -0,0 +1,44 @@ |
| 1 | # `nanoassert` | |
| 2 | ||
| 3 | [](https://travis-ci.org/emilbayes/nanoassert) | |
| 4 | ||
| 5 | > Nanoscale assertion module | |
| 6 | ||
| 7 | ## Usage | |
| 8 | ||
| 9 | ```js | |
| 10 | var assert = require('nanoassert') | |
| 11 | ||
| 12 | assert(a !== b, `${a} !== ${b}`) | |
| 13 | ``` | |
| 14 | ||
| 15 | ## API | |
| 16 | ||
| 17 | ### `assert(declaration, [message])` | |
| 18 | ||
| 19 | Assert that `declaration` is truthy otherwise throw `AssertionError` with | |
| 20 | optional `message`. In Javascript runtimes that use v8, you will get a nicer | |
| 21 | stack trace with this error. | |
| 22 | If you want friendlier messages you can use template strings to show the | |
| 23 | assertion made like in the example above. | |
| 24 | ||
| 25 | ## Why | |
| 26 | ||
| 27 | I like to write public facing code very defensively, but have reservations about | |
| 28 | the size incurred by the `assert` module. I only use the top-level `assert` | |
| 29 | method anyway. | |
| 30 | ||
| 31 | ## `nanoassert@^1.1.0` | |
| 32 | ||
| 33 | Docs for the previous version, which is used by many modules on npm, can be | |
| 34 | [found here](https://github.com/emilbayes/nanoassert/tree/v1.1.0) | |
| 35 | ||
| 36 | ## Install | |
| 37 | ||
| 38 | ```sh | |
| 39 | npm install nanoassert | |
| 40 | ``` | |
| 41 | ||
| 42 | ## License | |
| 43 | ||
| 44 | [ISC](LICENSE) |
| … | … | @@ -0,0 +1,5 @@ |
| 1 | var assert = require('.') | |
| 2 | ||
| 3 | var a = 1 | |
| 4 | var b = 1 | |
| 5 | assert(a !== b, `${a} !== ${b}`) |
| … | … | @@ -0,0 +1,18 @@ |
| 1 | module.exports = assert | |
| 2 | ||
| 3 | class AssertionError extends Error {} | |
| 4 | AssertionError.prototype.name = 'AssertionError' | |
| 5 | ||
| 6 | /** | |
| 7 | * Minimal assert function | |
| 8 | * @param {any} t Value to check if falsy | |
| 9 | * @param {string=} m Optional assertion error message | |
| 10 | * @throws {AssertionError} | |
| 11 | */ | |
| 12 | function assert (t, m) { | |
| 13 | if (!t) { | |
| 14 | var err = new AssertionError(m) | |
| 15 | if (Error.captureStackTrace) Error.captureStackTrace(err, assert) | |
| 16 | throw err | |
| 17 | } | |
| 18 | } |
| … | … | @@ -0,0 +1,31 @@ |
| 1 | { | |
| 2 | "name": "nanoassert", | |
| 3 | "version": "2.0.0", | |
| 4 | "description": "Nanoscale assertion module", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": {}, | |
| 7 | "devDependencies": { | |
| 8 | "tape": "^4.9.1" | |
| 9 | }, | |
| 10 | "scripts": { | |
| 11 | "test": "tape test.js" | |
| 12 | }, | |
| 13 | "repository": { | |
| 14 | "type": "git", | |
| 15 | "url": "git+https://github.com/emilbayes/nanoassert.git" | |
| 16 | }, | |
| 17 | "keywords": [ | |
| 18 | "assert", | |
| 19 | "unassert", | |
| 20 | "power-assert", | |
| 21 | "tiny", | |
| 22 | "nano", | |
| 23 | "pico" | |
| 24 | ], | |
| 25 | "author": "Emil Bay <github@tixz.dk>", | |
| 26 | "license": "ISC", | |
| 27 | "bugs": { | |
| 28 | "url": "https://github.com/emilbayes/nanoassert/issues" | |
| 29 | }, | |
| 30 | "homepage": "https://github.com/emilbayes/nanoassert#readme" | |
| 31 | } |
| … | … | @@ -0,0 +1,27 @@ |
| 1 | var assert = require('.') | |
| 2 | var test = require('tape') | |
| 3 | ||
| 4 | test('test', function (t) { | |
| 5 | try { | |
| 6 | assert(true === true) // test that it doesn't throw | |
| 7 | t.pass('does not throw on truthy') | |
| 8 | } catch (e) { | |
| 9 | t.fail() | |
| 10 | } | |
| 11 | ||
| 12 | t.throws(assert.bind(null, false), 'throws on falsy') | |
| 13 | ||
| 14 | try { | |
| 15 | assert(false) | |
| 16 | } catch (e) { | |
| 17 | t.equal(e.message, '', 'default message') | |
| 18 | } | |
| 19 | ||
| 20 | try { | |
| 21 | assert(false, 'hello world') | |
| 22 | } catch (e) { | |
| 23 | t.equal(e.message, 'hello world', 'custom message') | |
| 24 | } | |
| 25 | ||
| 26 | t.end() | |
| 27 | }) |
| … | … | @@ -0,0 +1,36 @@ |
| 1 | # noise-curve-ed | |
| 2 | ||
| 3 | Ed25519 elliptic curve operations for [`noise-handshake`](https://github.com/chm-diederichs/noise-handshake) | |
| 4 | ||
| 5 | ## Usage | |
| 6 | ```js | |
| 7 | const curve = require('noise-curve-ed') | |
| 8 | const Noise = require('noise-handshake') | |
| 9 | ||
| 10 | const handshake = new Noise(pattern, initiator, staticKeyPair, { curve }) | |
| 11 | ``` | |
| 12 | ||
| 13 | ## API | |
| 14 | ||
| 15 | #### constants | |
| 16 | ||
| 17 | `DHLEN` = 32 | |
| 18 | `PKLEN` = 32 | |
| 19 | `SCALARLEN` = 32 | |
| 20 | `SKLEN` = 64 | |
| 21 | `ALG` = 'Ed25519' | |
| 22 | ||
| 23 | #### `generateKeyPair([privKey])` | |
| 24 | ||
| 25 | Generate a new keypair, optionally pass in a preexisting `privKey`. Return value is of the form: | |
| 26 | ||
| 27 | ``` | |
| 28 | { | |
| 29 | publicKey, | |
| 30 | secretKey, | |
| 31 | } | |
| 32 | ``` | |
| 33 | ||
| 34 | #### `dh(publicKey, { secretKey, scalar })` | |
| 35 | ||
| 36 | Perform DH between `publicKey` and `secretKey`/`scalar` and return the result. |
| … | … | @@ -0,0 +1,71 @@ |
| 1 | /* eslint-disable camelcase */ | |
| 2 | const sodium = require('sodium-universal') | |
| 3 | const assert = require('nanoassert') | |
| 4 | const b4a = require('b4a') | |
| 5 | ||
| 6 | const DHLEN = sodium.crypto_scalarmult_ed25519_BYTES | |
| 7 | const PKLEN = sodium.crypto_scalarmult_ed25519_BYTES | |
| 8 | const SCALARLEN = sodium.crypto_scalarmult_ed25519_BYTES | |
| 9 | const SKLEN = sodium.crypto_sign_SECRETKEYBYTES | |
| 10 | const ALG = 'Ed25519' | |
| 11 | ||
| 12 | module.exports = { | |
| 13 | DHLEN, | |
| 14 | PKLEN, | |
| 15 | SCALARLEN, | |
| 16 | SKLEN, | |
| 17 | ALG, | |
| 18 | name: ALG, | |
| 19 | generateKeyPair, | |
| 20 | dh | |
| 21 | } | |
| 22 | ||
| 23 | function generateKeyPair (privKey) { | |
| 24 | if (privKey) return generateSeedKeyPair(privKey.subarray(0, 32)) | |
| 25 | ||
| 26 | const keyPair = {} | |
| 27 | keyPair.secretKey = b4a.alloc(SKLEN) | |
| 28 | keyPair.publicKey = b4a.alloc(PKLEN) | |
| 29 | ||
| 30 | sodium.crypto_sign_keypair(keyPair.publicKey, keyPair.secretKey) | |
| 31 | return keyPair | |
| 32 | } | |
| 33 | ||
| 34 | function generateSeedKeyPair (seed) { | |
| 35 | const keyPair = {} | |
| 36 | keyPair.secretKey = b4a.alloc(SKLEN) | |
| 37 | keyPair.publicKey = b4a.alloc(PKLEN) | |
| 38 | ||
| 39 | sodium.crypto_sign_seed_keypair(keyPair.publicKey, keyPair.secretKey, seed) | |
| 40 | return keyPair | |
| 41 | } | |
| 42 | ||
| 43 | function dh (publicKey, { scalar, secretKey }) { | |
| 44 | // tweaked keys expose scalar directly | |
| 45 | if (!scalar) { | |
| 46 | assert(secretKey.byteLength === SKLEN) | |
| 47 | ||
| 48 | // libsodium stores seed not actual scalar | |
| 49 | const sk = b4a.alloc(64) | |
| 50 | sodium.crypto_hash_sha512(sk, secretKey.subarray(0, 32)) | |
| 51 | sk[0] &= 248 | |
| 52 | sk[31] &= 127 | |
| 53 | sk[31] |= 64 | |
| 54 | ||
| 55 | scalar = sk.subarray(0, 32) | |
| 56 | } | |
| 57 | ||
| 58 | assert(scalar.byteLength === SCALARLEN) | |
| 59 | assert(publicKey.byteLength === PKLEN) | |
| 60 | ||
| 61 | const output = b4a.alloc(DHLEN) | |
| 62 | ||
| 63 | // we clamp if necessary above | |
| 64 | sodium.crypto_scalarmult_ed25519_noclamp( | |
| 65 | output, | |
| 66 | scalar, | |
| 67 | publicKey | |
| 68 | ) | |
| 69 | ||
| 70 | return output | |
| 71 | } |
| … | … | @@ -0,0 +1,279 @@ |
| 1 | cmake_minimum_required(VERSION 3.25) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) | |
| 5 | find_package(cmake-napi REQUIRED PATHS node_modules/cmake-napi) | |
| 6 | find_package(cmake-npm REQUIRED PATHS node_modules/cmake-npm) | |
| 7 | ||
| 8 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) | |
| 9 | ||
| 10 | project(sodium_native C ASM CXX) | |
| 11 | ||
| 12 | fetch_package("github:jedisct1/libsodium#e18eee6" SOURCE_DIR sodium) | |
| 13 | ||
| 14 | fetch_package("github:holepunchto/libjstl#098664c") | |
| 15 | ||
| 16 | bare_target(target) | |
| 17 | ||
| 18 | if(target MATCHES "win32") | |
| 19 | add_compile_options(/MT$<$<CONFIG:Debug>:d>) | |
| 20 | endif() | |
| 21 | ||
| 22 | file(COPY_FILE "${sodium}/builds/msvc/version.h" "${sodium}/src/libsodium/include/sodium/version.h") | |
| 23 | ||
| 24 | file(GLOB_RECURSE sodium_headers CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.h") | |
| 25 | file(GLOB_RECURSE sodium_sources CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.c") | |
| 26 | file(GLOB_RECURSE sodium_asm_sources CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.S") | |
| 27 | ||
| 28 | add_library(sodium OBJECT) | |
| 29 | ||
| 30 | target_sources( | |
| 31 | sodium | |
| 32 | INTERFACE | |
| 33 | ${sodium_headers} | |
| 34 | PRIVATE | |
| 35 | ${sodium_sources} | |
| 36 | ) | |
| 37 | ||
| 38 | target_include_directories( | |
| 39 | sodium | |
| 40 | INTERFACE | |
| 41 | "${sodium}/src/libsodium/include" | |
| 42 | PRIVATE | |
| 43 | "${sodium}/src/libsodium/include/sodium" | |
| 44 | ) | |
| 45 | ||
| 46 | if(NOT target MATCHES "win32") | |
| 47 | target_compile_options( | |
| 48 | sodium | |
| 49 | PRIVATE | |
| 50 | -fvisibility=hidden | |
| 51 | -fno-strict-aliasing | |
| 52 | -fwrapv | |
| 53 | -flax-vector-conversions | |
| 54 | ) | |
| 55 | endif() | |
| 56 | ||
| 57 | target_compile_definitions( | |
| 58 | sodium | |
| 59 | PUBLIC | |
| 60 | SODIUM_STATIC=1 | |
| 61 | PRIVATE | |
| 62 | _GNU_SOURCE=1 | |
| 63 | CONFIGURED=1 | |
| 64 | DEV_MODE=1 | |
| 65 | HAVE_ATOMIC_OPS=1 | |
| 66 | HAVE_C11_MEMORY_FENCES=1 | |
| 67 | HAVE_CET_H=1 | |
| 68 | HAVE_GCC_MEMORY_FENCES=1 | |
| 69 | HAVE_INLINE_ASM=1 | |
| 70 | HAVE_INTTYPES_H=1 | |
| 71 | HAVE_STDINT_H=1 | |
| 72 | HAVE_TI_MODE=1 | |
| 73 | ) | |
| 74 | ||
| 75 | if(target MATCHES "darwin|ios") | |
| 76 | target_compile_definitions( | |
| 77 | sodium | |
| 78 | PRIVATE | |
| 79 | ASM_HIDE_SYMBOL=.private_extern | |
| 80 | TLS=_Thread_local | |
| 81 | HAVE_ARC4RANDOM=1 | |
| 82 | HAVE_ARC4RANDOM_BUF=1 | |
| 83 | HAVE_CATCHABLE_ABRT=1 | |
| 84 | HAVE_CATCHABLE_SEGV=1 | |
| 85 | HAVE_CLOCK_GETTIME=1 | |
| 86 | HAVE_GETPID=1 | |
| 87 | HAVE_MADVISE=1 | |
| 88 | HAVE_MEMSET_S=1 | |
| 89 | HAVE_MLOCK=1 | |
| 90 | HAVE_MMAP=1 | |
| 91 | HAVE_MPROTECT=1 | |
| 92 | HAVE_NANOSLEEP=1 | |
| 93 | HAVE_POSIX_MEMALIGN=1 | |
| 94 | HAVE_PTHREAD=1 | |
| 95 | HAVE_PTHREAD_PRIO_INHERIT=1 | |
| 96 | HAVE_RAISE=1 | |
| 97 | HAVE_SYSCONF=1 | |
| 98 | HAVE_SYS_MMAN_H=1 | |
| 99 | HAVE_SYS_PARAM_H=1 | |
| 100 | HAVE_WEAK_SYMBOLS=1 | |
| 101 | ) | |
| 102 | ||
| 103 | if(NOT target MATCHES "ios") | |
| 104 | target_compile_definitions( | |
| 105 | sodium | |
| 106 | PRIVATE | |
| 107 | HAVE_GETENTROPY=1 | |
| 108 | HAVE_SYS_RANDOM_H=1 | |
| 109 | ) | |
| 110 | endif() | |
| 111 | endif() | |
| 112 | ||
| 113 | if(target MATCHES "linux") | |
| 114 | target_compile_definitions( | |
| 115 | sodium | |
| 116 | PRIVATE | |
| 117 | ASM_HIDE_SYMBOL=.hidden | |
| 118 | TLS=_Thread_local | |
| 119 | HAVE_CATCHABLE_ABRT=1 | |
| 120 | HAVE_CATCHABLE_SEGV=1 | |
| 121 | HAVE_CLOCK_GETTIME=1 | |
| 122 | HAVE_GETPID=1 | |
| 123 | HAVE_MADVISE=1 | |
| 124 | HAVE_MLOCK=1 | |
| 125 | HAVE_MMAP=1 | |
| 126 | HAVE_MPROTECT=1 | |
| 127 | HAVE_NANOSLEEP=1 | |
| 128 | HAVE_POSIX_MEMALIGN=1 | |
| 129 | HAVE_PTHREAD_PRIO_INHERIT=1 | |
| 130 | HAVE_PTHREAD=1 | |
| 131 | HAVE_RAISE=1 | |
| 132 | HAVE_SYSCONF=1 | |
| 133 | HAVE_SYS_AUXV_H=1 | |
| 134 | HAVE_SYS_MMAN_H=1 | |
| 135 | HAVE_SYS_PARAM_H=1 | |
| 136 | HAVE_SYS_RANDOM_H=1 | |
| 137 | HAVE_WEAK_SYMBOLS=1 | |
| 138 | ) | |
| 139 | endif() | |
| 140 | ||
| 141 | if(target MATCHES "win32") | |
| 142 | target_compile_definitions( | |
| 143 | sodium | |
| 144 | PRIVATE | |
| 145 | _CRT_SECURE_NO_WARNINGS=1 | |
| 146 | HAVE_RAISE=1 | |
| 147 | ) | |
| 148 | endif() | |
| 149 | ||
| 150 | if(target MATCHES "x64") | |
| 151 | target_compile_definitions( | |
| 152 | sodium | |
| 153 | PRIVATE | |
| 154 | HAVE_CPUID=1 | |
| 155 | HAVE_RDRAND=1 | |
| 156 | HAVE_EMMINTRIN_H=1 # SSE2 | |
| 157 | HAVE_PMMINTRIN_H=1 # SSE3 | |
| 158 | HAVE_TMMINTRIN_H=1 # SSSE3 | |
| 159 | HAVE_SMMINTRIN_H=1 # SSE4.1 | |
| 160 | HAVE_WMMINTRIN_H=1 # AES | |
| 161 | HAVE_AVXINTRIN_H=1 # AVX | |
| 162 | HAVE_AVX2INTRIN_H=1 # AVX2 | |
| 163 | HAVE_AVX512FINTRIN_H # AVX512F | |
| 164 | ) | |
| 165 | ||
| 166 | if(NOT target MATCHES "win32") | |
| 167 | target_compile_definitions( | |
| 168 | sodium | |
| 169 | PRIVATE | |
| 170 | HAVE_AMD64_ASM=1 | |
| 171 | HAVE_AVX_ASM=1 | |
| 172 | ) | |
| 173 | ||
| 174 | target_sources( | |
| 175 | sodium | |
| 176 | PRIVATE | |
| 177 | ${sodium_asm_sources} | |
| 178 | ) | |
| 179 | endif() | |
| 180 | endif() | |
| 181 | ||
| 182 | if(target MATCHES "arm64") | |
| 183 | target_compile_definitions( | |
| 184 | sodium | |
| 185 | PRIVATE | |
| 186 | HAVE_ARMCRYPTO=1 | |
| 187 | ) | |
| 188 | endif() | |
| 189 | ||
| 190 | if(CMAKE_C_BYTE_ORDER MATCHES "BIG_ENDIAN") | |
| 191 | target_compile_definitions( | |
| 192 | sodium | |
| 193 | PRIVATE | |
| 194 | NATIVE_BIG_ENDIAN=1 | |
| 195 | ) | |
| 196 | else() | |
| 197 | target_compile_definitions( | |
| 198 | sodium | |
| 199 | PRIVATE | |
| 200 | NATIVE_LITTLE_ENDIAN=1 | |
| 201 | ) | |
| 202 | endif() | |
| 203 | ||
| 204 | if(target MATCHES "linux|android") | |
| 205 | target_link_options( | |
| 206 | sodium | |
| 207 | PUBLIC | |
| 208 | -Wl,-z,noexecstack | |
| 209 | ) | |
| 210 | endif() | |
| 211 | ||
| 212 | add_library(sodium_extensions OBJECT) | |
| 213 | ||
| 214 | target_sources( | |
| 215 | sodium_extensions | |
| 216 | PRIVATE | |
| 217 | extensions/tweak/tweak.c | |
| 218 | extensions/tweak/tweak.h | |
| 219 | extensions/pbkdf2/pbkdf2.c | |
| 220 | extensions/pbkdf2/pbkdf2.h | |
| 221 | ) | |
| 222 | ||
| 223 | target_link_libraries( | |
| 224 | sodium_extensions | |
| 225 | PUBLIC | |
| 226 | sodium | |
| 227 | ) | |
| 228 | ||
| 229 | add_bare_module(sodium_native_bare) | |
| 230 | ||
| 231 | target_sources( | |
| 232 | ${sodium_native_bare} | |
| 233 | PRIVATE | |
| 234 | binding.cc | |
| 235 | ) | |
| 236 | ||
| 237 | target_link_libraries( | |
| 238 | ${sodium_native_bare} | |
| 239 | PRIVATE | |
| 240 | $<TARGET_OBJECTS:sodium> | |
| 241 | $<TARGET_OBJECTS:sodium_extensions> | |
| 242 | jstl | |
| 243 | PUBLIC | |
| 244 | sodium | |
| 245 | sodium_extensions | |
| 246 | ) | |
| 247 | ||
| 248 | add_napi_module(sodium_native_node) | |
| 249 | ||
| 250 | target_sources( | |
| 251 | ${sodium_native_node} | |
| 252 | PRIVATE | |
| 253 | binding.cc | |
| 254 | ) | |
| 255 | ||
| 256 | target_compile_definitions( | |
| 257 | ${sodium_native_node} | |
| 258 | PRIVATE | |
| 259 | NAPI_VERSION=9 | |
| 260 | ) | |
| 261 | ||
| 262 | target_link_libraries( | |
| 263 | ${sodium_native_node} | |
| 264 | PRIVATE | |
| 265 | $<TARGET_OBJECTS:sodium> | |
| 266 | $<TARGET_OBJECTS:sodium_extensions> | |
| 267 | jstl | |
| 268 | PUBLIC | |
| 269 | sodium | |
| 270 | sodium_extensions | |
| 271 | ) | |
| 272 | ||
| 273 | resolve_node_module(bare-compat-napi compat) | |
| 274 | ||
| 275 | target_include_directories( | |
| 276 | ${sodium_native_node} | |
| 277 | PRIVATE | |
| 278 | "${compat}/include" | |
| 279 | ) |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2016 Mathias Buus and Emil Bay | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,51 @@ |
| 1 | # sodium-native | |
| 2 | ||
| 3 | Low level bindings for [libsodium](https://github.com/jedisct1/libsodium). | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install sodium-native | |
| 7 | ``` | |
| 8 | ||
| 9 | The goal of this project is to be thin, stable, unopionated wrapper around libsodium. | |
| 10 | ||
| 11 | All methods exposed are more or less a direct translation of the libsodium c-api. | |
| 12 | This means that most data types are buffers and you have to manage allocating return values and passing them in as arguments instead of receiving them as return values. | |
| 13 | ||
| 14 | This makes this API harder to use than other libsodium wrappers out there, but also means that you'll be able to get a lot of perf / memory improvements as you can do stuff like inline encryption / decryption, reuse buffers etc. | |
| 15 | ||
| 16 | This also makes this library useful as a foundation for more high level crypto abstractions that you want to make. | |
| 17 | ||
| 18 | ## Usage | |
| 19 | ||
| 20 | ```js | |
| 21 | var sodium = require('sodium-native') | |
| 22 | ||
| 23 | var nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES) | |
| 24 | var key = sodium.sodium_malloc(sodium.crypto_secretbox_KEYBYTES) // secure buffer | |
| 25 | var message = Buffer.from('Hello, World!') | |
| 26 | var ciphertext = Buffer.alloc(message.length + sodium.crypto_secretbox_MACBYTES) | |
| 27 | ||
| 28 | sodium.randombytes_buf(nonce) // insert random data into nonce | |
| 29 | sodium.randombytes_buf(key) // insert random data into key | |
| 30 | ||
| 31 | // encrypted message is stored in ciphertext. | |
| 32 | sodium.crypto_secretbox_easy(ciphertext, message, nonce, key) | |
| 33 | ||
| 34 | console.log('Encrypted message:', ciphertext) | |
| 35 | ||
| 36 | var plainText = Buffer.alloc(ciphertext.length - sodium.crypto_secretbox_MACBYTES) | |
| 37 | ||
| 38 | if (!sodium.crypto_secretbox_open_easy(plainText, ciphertext, nonce, key)) { | |
| 39 | console.log('Decryption failed!') | |
| 40 | } else { | |
| 41 | console.log('Decrypted message:', plainText, '(' + plainText.toString() + ')') | |
| 42 | } | |
| 43 | ``` | |
| 44 | ||
| 45 | ## Documentation | |
| 46 | ||
| 47 | Complete documentation may be found on the [sodium-friends website](https://sodium-friends.github.io/docs/docs/getstarted) | |
| 48 | ||
| 49 | ## License | |
| 50 | ||
| 51 | MIT |
| … | … | @@ -0,0 +1,4094 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <bare.h> | |
| 3 | #include <js.h> | |
| 4 | #include <jstl.h> | |
| 5 | #include <sodium.h> | |
| 6 | #include <stdbool.h> | |
| 7 | #include <stdint.h> | |
| 8 | #include <string.h> | |
| 9 | #include <uv.h> | |
| 10 | ||
| 11 | #include "extensions/pbkdf2/pbkdf2.h" | |
| 12 | #include "extensions/tweak/tweak.h" | |
| 13 | ||
| 14 | #define assert_bounds(arraybuffer) \ | |
| 15 | assert(arraybuffer##_offset + arraybuffer##_len <= arraybuffer.size()) | |
| 16 | ||
| 17 | static inline void | |
| 18 | sn_sodium_memzero( | |
| 19 | js_env_t *, | |
| 20 | js_receiver_t, | |
| 21 | js_typedarray_span_t<> buf | |
| 22 | ) { | |
| 23 | sodium_memzero(buf.data(), buf.size_bytes()); | |
| 24 | } | |
| 25 | ||
| 26 | static inline int | |
| 27 | sn_sodium_mlock( | |
| 28 | js_env_t *, | |
| 29 | js_receiver_t, | |
| 30 | js_typedarray_span_t<> buf | |
| 31 | ) { | |
| 32 | return sodium_mlock(buf.data(), buf.size_bytes()); | |
| 33 | } | |
| 34 | ||
| 35 | static inline int | |
| 36 | sn_sodium_munlock( | |
| 37 | js_env_t *, | |
| 38 | js_receiver_t, | |
| 39 | js_typedarray_span_t<> buf | |
| 40 | ) { | |
| 41 | return sodium_munlock(buf.data(), buf.size_bytes()); | |
| 42 | } | |
| 43 | ||
| 44 | static void | |
| 45 | sn_external_arraybuffer_finalize(js_env_t *env, uint8_t *ptr) { | |
| 46 | sodium_free(ptr); | |
| 47 | } | |
| 48 | ||
| 49 | static js_arraybuffer_t | |
| 50 | sn_sodium_malloc(js_env_t *env, js_receiver_t, uint32_t len) { | |
| 51 | int err; | |
| 52 | ||
| 53 | auto ptr = reinterpret_cast<uint8_t *>(sodium_malloc(len)); | |
| 54 | ||
| 55 | js_arraybuffer_t buffer; | |
| 56 | ||
| 57 | err = js_create_external_arraybuffer<sn_external_arraybuffer_finalize>(env, ptr, len, buffer); | |
| 58 | assert(err == 0); | |
| 59 | ||
| 60 | return buffer; | |
| 61 | } | |
| 62 | ||
| 63 | static void | |
| 64 | sn_sodium_free(js_env_t *env, js_receiver_t, js_arraybuffer_t buf) { | |
| 65 | int err; | |
| 66 | ||
| 67 | std::span<uint8_t> view; | |
| 68 | err = js_get_arraybuffer_info(env, buf, view); | |
| 69 | assert(err == 0); | |
| 70 | ||
| 71 | if (view.empty()) return; | |
| 72 | ||
| 73 | err = js_detach_arraybuffer(env, buf); | |
| 74 | assert(err == 0); | |
| 75 | } | |
| 76 | ||
| 77 | static inline int | |
| 78 | sn_sodium_mprotect_noaccess(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 79 | return sodium_mprotect_noaccess(buf.data()); | |
| 80 | } | |
| 81 | ||
| 82 | static inline int | |
| 83 | sn_sodium_mprotect_readonly(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 84 | return sodium_mprotect_readonly(buf.data()); | |
| 85 | } | |
| 86 | ||
| 87 | static inline int | |
| 88 | sn_sodium_mprotect_readwrite(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 89 | return sodium_mprotect_readwrite(buf.data()); | |
| 90 | } | |
| 91 | ||
| 92 | static inline uint32_t // TODO: test envless | |
| 93 | sn_randombytes_random(js_env_t *env, js_receiver_t) { | |
| 94 | return randombytes_random(); | |
| 95 | } | |
| 96 | ||
| 97 | static inline uint32_t // TODO: test envless | |
| 98 | sn_randombytes_uniform(js_env_t *env, js_receiver_t, uint32_t upper_bound) { | |
| 99 | return randombytes_uniform(upper_bound); | |
| 100 | } | |
| 101 | ||
| 102 | static inline void | |
| 103 | sn_randombytes_buf( | |
| 104 | js_env_t *env, | |
| 105 | js_receiver_t, | |
| 106 | js_arraybuffer_span_t buf, | |
| 107 | uint32_t buf_offset, | |
| 108 | uint32_t buf_len | |
| 109 | ) { | |
| 110 | assert_bounds(buf); | |
| 111 | ||
| 112 | randombytes_buf(&buf[buf_offset], buf_len); | |
| 113 | } | |
| 114 | ||
| 115 | static inline void | |
| 116 | sn_randombytes_buf_deterministic( | |
| 117 | js_env_t *env, | |
| 118 | js_receiver_t, | |
| 119 | ||
| 120 | js_arraybuffer_span_t buf, | |
| 121 | uint32_t buf_offset, | |
| 122 | uint32_t buf_len, | |
| 123 | ||
| 124 | js_arraybuffer_span_t seed, | |
| 125 | uint32_t seed_offset, | |
| 126 | uint32_t seed_len | |
| 127 | ) { | |
| 128 | assert_bounds(buf); | |
| 129 | assert_bounds(seed); | |
| 130 | ||
| 131 | assert(seed_len == randombytes_SEEDBYTES); | |
| 132 | ||
| 133 | randombytes_buf_deterministic(&buf[buf_offset], buf_len, &seed[seed_offset]); | |
| 134 | } | |
| 135 | ||
| 136 | static inline bool | |
| 137 | sn_sodium_memcmp(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 138 | if (a.size_bytes() != b.size_bytes()) return false; | |
| 139 | ||
| 140 | return sodium_memcmp(a.data(), b.data(), a.size_bytes()) == 0; | |
| 141 | } | |
| 142 | ||
| 143 | static inline void | |
| 144 | sn_sodium_increment(js_env_t *, js_receiver_t, js_typedarray_span_t<> n) { | |
| 145 | sodium_increment(n.data(), n.size_bytes()); | |
| 146 | } | |
| 147 | ||
| 148 | static inline void | |
| 149 | sn_sodium_add(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 150 | sodium_add(a.data(), b.data(), a.size_bytes()); | |
| 151 | } | |
| 152 | ||
| 153 | static inline void | |
| 154 | sn_sodium_sub(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 155 | sodium_sub(a.data(), b.data(), a.size_bytes()); | |
| 156 | } | |
| 157 | ||
| 158 | static inline int32_t | |
| 159 | sn_sodium_compare(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 160 | return sodium_compare(a.data(), b.data(), a.size_bytes()); | |
| 161 | } | |
| 162 | ||
| 163 | static inline bool | |
| 164 | sn_sodium_is_zero(js_env_t *, js_receiver_t, js_typedarray_span_t<> buffer, uint32_t len) { | |
| 165 | assert(len <= buffer.size_bytes()); | |
| 166 | ||
| 167 | return sodium_is_zero(buffer.data(), len) != 0; | |
| 168 | } | |
| 169 | ||
| 170 | static inline uint32_t | |
| 171 | sn_sodium_pad(js_env_t *, js_receiver_t, js_typedarray_span_t<> buf, uint32_t unpadded_buflen, uint32_t blocksize) { | |
| 172 | size_t padded_buflen; | |
| 173 | ||
| 174 | sodium_pad(&padded_buflen, buf.data(), unpadded_buflen, blocksize, buf.size_bytes()); | |
| 175 | ||
| 176 | return padded_buflen; | |
| 177 | } | |
| 178 | ||
| 179 | static inline uint32_t | |
| 180 | sn_sodium_unpad(js_env_t *, js_receiver_t, js_typedarray_span_t<> buf, uint32_t padded_buflen, uint32_t blocksize) { | |
| 181 | size_t unpadded_buflen; | |
| 182 | ||
| 183 | sodium_unpad(&unpadded_buflen, buf.data(), padded_buflen, blocksize); | |
| 184 | ||
| 185 | return unpadded_buflen; | |
| 186 | } | |
| 187 | ||
| 188 | static inline int | |
| 189 | sn_crypto_sign_keypair(js_env_t *, js_receiver_t, js_typedarray_span_t<> pk, js_typedarray_span_t<> sk) { | |
| 190 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 191 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 192 | ||
| 193 | return crypto_sign_keypair(pk.data(), sk.data()); | |
| 194 | } | |
| 195 | ||
| 196 | static inline int | |
| 197 | sn_crypto_sign_seed_keypair( | |
| 198 | js_env_t *, | |
| 199 | js_receiver_t, | |
| 200 | js_typedarray_span_t<> pk, | |
| 201 | js_typedarray_span_t<> sk, | |
| 202 | js_typedarray_span_t<> seed | |
| 203 | ) { | |
| 204 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 205 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 206 | assert(seed.size_bytes() == crypto_sign_SEEDBYTES); | |
| 207 | ||
| 208 | return crypto_sign_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 209 | } | |
| 210 | ||
| 211 | static inline int | |
| 212 | sn_crypto_sign( | |
| 213 | js_env_t *, | |
| 214 | js_receiver_t, | |
| 215 | js_typedarray_span_t<> sm, | |
| 216 | js_typedarray_span_t<> m, | |
| 217 | js_typedarray_span_t<> sk | |
| 218 | ) { | |
| 219 | assert(sm.size_bytes() == crypto_sign_BYTES + m.size_bytes()); | |
| 220 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 221 | ||
| 222 | return crypto_sign(sm.data(), NULL, m.data(), m.size_bytes(), sk.data()); | |
| 223 | } | |
| 224 | ||
| 225 | static inline bool | |
| 226 | sn_crypto_sign_open( | |
| 227 | js_env_t *, | |
| 228 | js_receiver_t, | |
| 229 | js_typedarray_span_t<> m, | |
| 230 | js_typedarray_span_t<> sm, | |
| 231 | js_typedarray_span_t<> pk | |
| 232 | ) { | |
| 233 | assert(m.size_bytes() == sm.size_bytes() - crypto_sign_BYTES); | |
| 234 | assert(sm.size_bytes() >= crypto_sign_BYTES); | |
| 235 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 236 | ||
| 237 | return crypto_sign_open(m.data(), NULL, sm.data(), sm.size_bytes(), pk.data()) == 0; | |
| 238 | } | |
| 239 | ||
| 240 | static inline int | |
| 241 | sn_crypto_sign_detached( | |
| 242 | js_env_t *env, | |
| 243 | js_receiver_t, | |
| 244 | js_typedarray_span_t<> sig, | |
| 245 | js_typedarray_span_t<> m, | |
| 246 | js_typedarray_span_t<> sk | |
| 247 | ) { | |
| 248 | assert(sig.size_bytes() == crypto_sign_BYTES); | |
| 249 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 250 | ||
| 251 | return crypto_sign_detached(sig.data(), NULL, m.data(), m.size_bytes(), sk.data()); | |
| 252 | } | |
| 253 | ||
| 254 | static inline bool | |
| 255 | sn_crypto_sign_verify_detached( | |
| 256 | js_env_t *env, | |
| 257 | js_receiver_t, | |
| 258 | ||
| 259 | js_arraybuffer_span_t sig, | |
| 260 | uint32_t sig_offset, | |
| 261 | uint32_t sig_len, | |
| 262 | ||
| 263 | js_arraybuffer_span_t m, | |
| 264 | uint32_t m_offset, | |
| 265 | uint32_t m_len, | |
| 266 | ||
| 267 | js_arraybuffer_span_t pk, | |
| 268 | uint32_t pk_offset, | |
| 269 | uint32_t pk_len | |
| 270 | ) { | |
| 271 | assert_bounds(sig); | |
| 272 | assert_bounds(m); | |
| 273 | assert_bounds(pk); | |
| 274 | ||
| 275 | assert(sig_len >= crypto_sign_BYTES); | |
| 276 | assert(pk_len == crypto_sign_PUBLICKEYBYTES); | |
| 277 | ||
| 278 | return crypto_sign_verify_detached(&sig[sig_offset], &m[m_offset], m_len, &pk[pk_offset]) == 0; | |
| 279 | } | |
| 280 | ||
| 281 | static inline int | |
| 282 | sn_crypto_sign_ed25519_sk_to_pk( | |
| 283 | js_env_t *, | |
| 284 | js_receiver_t, | |
| 285 | js_typedarray_span_t<> pk, | |
| 286 | js_typedarray_span_t<> sk | |
| 287 | ) { | |
| 288 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 289 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 290 | ||
| 291 | return crypto_sign_ed25519_sk_to_pk(pk.data(), sk.data()); | |
| 292 | } | |
| 293 | ||
| 294 | static inline int | |
| 295 | sn_crypto_sign_ed25519_pk_to_curve25519( | |
| 296 | js_env_t *, | |
| 297 | js_receiver_t, | |
| 298 | js_typedarray_span_t<> x25519_pk, | |
| 299 | js_typedarray_span_t<> ed25519_pk | |
| 300 | ) { | |
| 301 | assert(x25519_pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 302 | assert(ed25519_pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 303 | ||
| 304 | return crypto_sign_ed25519_pk_to_curve25519(x25519_pk.data(), ed25519_pk.data()); | |
| 305 | } | |
| 306 | ||
| 307 | static inline int | |
| 308 | sn_crypto_sign_ed25519_sk_to_curve25519( | |
| 309 | js_env_t *, | |
| 310 | js_receiver_t, | |
| 311 | js_typedarray_span_t<> x25519_sk, | |
| 312 | js_typedarray_span_t<> ed25519_sk | |
| 313 | ) { | |
| 314 | assert(x25519_sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 315 | assert( | |
| 316 | ed25519_sk.size_bytes() == crypto_sign_SECRETKEYBYTES || | |
| 317 | ed25519_sk.size_bytes() == crypto_box_SECRETKEYBYTES | |
| 318 | ); | |
| 319 | ||
| 320 | return crypto_sign_ed25519_sk_to_curve25519(x25519_sk.data(), ed25519_sk.data()); | |
| 321 | } | |
| 322 | ||
| 323 | static inline int | |
| 324 | sn_crypto_generichash( | |
| 325 | js_env_t *env, | |
| 326 | js_receiver_t, | |
| 327 | ||
| 328 | js_arraybuffer_span_t out, | |
| 329 | uint32_t out_offset, | |
| 330 | uint32_t out_len, | |
| 331 | ||
| 332 | js_arraybuffer_span_t in, | |
| 333 | uint32_t in_offset, | |
| 334 | uint32_t in_len, | |
| 335 | ||
| 336 | js_arraybuffer_span_t key, | |
| 337 | uint32_t key_offset, | |
| 338 | uint32_t key_len | |
| 339 | ) { | |
| 340 | assert_bounds(out); | |
| 341 | assert( | |
| 342 | out_len >= crypto_generichash_BYTES_MIN && | |
| 343 | out_len <= crypto_generichash_BYTES_MAX | |
| 344 | ); | |
| 345 | ||
| 346 | assert_bounds(in); | |
| 347 | ||
| 348 | uint8_t *key_data = NULL; | |
| 349 | if (key_len) { | |
| 350 | assert_bounds(key); | |
| 351 | assert( | |
| 352 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 353 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 354 | ); | |
| 355 | key_data = &key[key_offset]; | |
| 356 | } | |
| 357 | ||
| 358 | return crypto_generichash(&out[out_offset], out_len, &in[in_offset], in_len, key_data, key_len); | |
| 359 | } | |
| 360 | ||
| 361 | static inline int | |
| 362 | sn_crypto_generichash_batch( | |
| 363 | js_env_t *env, | |
| 364 | js_receiver_t, | |
| 365 | js_typedarray_t<uint8_t> out, | |
| 366 | std::vector<js_typedarray_span_t<>> batch, | |
| 367 | bool use_key, | |
| 368 | js_typedarray_t<uint8_t> key | |
| 369 | ) { | |
| 370 | int err; | |
| 371 | ||
| 372 | uint8_t *out_data; | |
| 373 | size_t out_len; | |
| 374 | err = js_get_typedarray_info(env, out, out_data, out_len); | |
| 375 | assert(err == 0); | |
| 376 | assert( | |
| 377 | out_len >= crypto_generichash_BYTES_MIN && | |
| 378 | out_len <= crypto_generichash_BYTES_MAX | |
| 379 | ); | |
| 380 | ||
| 381 | uint8_t *key_data = NULL; | |
| 382 | size_t key_len = 0; | |
| 383 | if (use_key) { | |
| 384 | err = js_get_typedarray_info(env, key, key_data, key_len); | |
| 385 | assert(err == 0); | |
| 386 | assert( | |
| 387 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 388 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 389 | ); | |
| 390 | } | |
| 391 | ||
| 392 | crypto_generichash_state state; | |
| 393 | err = crypto_generichash_init(&state, key_data, key_len, out_len); | |
| 394 | if (err != 0) return err; | |
| 395 | ||
| 396 | for (auto &buf : batch) { | |
| 397 | err = crypto_generichash_update(&state, buf.data(), buf.size()); | |
| 398 | if (err != 0) return err; | |
| 399 | } | |
| 400 | ||
| 401 | return crypto_generichash_final(&state, out_data, out_len); | |
| 402 | } | |
| 403 | ||
| 404 | static inline void | |
| 405 | sn_crypto_generichash_keygen( | |
| 406 | js_env_t *env, | |
| 407 | js_receiver_t, | |
| 408 | ||
| 409 | js_arraybuffer_span_t key, | |
| 410 | uint32_t key_offset, | |
| 411 | uint32_t key_len | |
| 412 | ) { | |
| 413 | assert_bounds(key); | |
| 414 | assert(key_len == crypto_generichash_KEYBYTES); | |
| 415 | ||
| 416 | crypto_generichash_keygen(&key[key_offset]); | |
| 417 | } | |
| 418 | ||
| 419 | static inline int | |
| 420 | sn_crypto_generichash_init( | |
| 421 | js_env_t *env, | |
| 422 | js_receiver_t, | |
| 423 | ||
| 424 | js_arraybuffer_span_t state, | |
| 425 | uint32_t state_offset, | |
| 426 | uint32_t state_len, | |
| 427 | ||
| 428 | js_arraybuffer_span_t key, | |
| 429 | uint32_t key_offset, | |
| 430 | uint32_t key_len, | |
| 431 | ||
| 432 | uint32_t out_len | |
| 433 | ) { | |
| 434 | assert_bounds(state); | |
| 435 | ||
| 436 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 437 | ||
| 438 | uint8_t *key_data = NULL; | |
| 439 | if (key_len) { | |
| 440 | assert_bounds(key); | |
| 441 | assert( | |
| 442 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 443 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 444 | ); | |
| 445 | key_data = &key[key_offset]; | |
| 446 | } | |
| 447 | ||
| 448 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 449 | ||
| 450 | return crypto_generichash_init(state_data, key_data, key_len, out_len); | |
| 451 | } | |
| 452 | ||
| 453 | static inline int | |
| 454 | sn_crypto_generichash_update( | |
| 455 | js_env_t *env, | |
| 456 | js_receiver_t, | |
| 457 | ||
| 458 | js_arraybuffer_span_t state, | |
| 459 | uint32_t state_offset, | |
| 460 | uint32_t state_len, | |
| 461 | ||
| 462 | js_arraybuffer_span_t in, | |
| 463 | uint32_t in_offset, | |
| 464 | uint32_t in_len | |
| 465 | ) { | |
| 466 | assert_bounds(state); | |
| 467 | assert_bounds(in); | |
| 468 | ||
| 469 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 470 | ||
| 471 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 472 | ||
| 473 | return crypto_generichash_update(state_data, &in[in_offset], in_len); | |
| 474 | } | |
| 475 | ||
| 476 | static inline int | |
| 477 | sn_crypto_generichash_final( | |
| 478 | js_env_t *env, | |
| 479 | js_receiver_t, | |
| 480 | ||
| 481 | js_arraybuffer_span_t state, | |
| 482 | uint32_t state_offset, | |
| 483 | uint32_t state_len, | |
| 484 | ||
| 485 | js_arraybuffer_span_t out, | |
| 486 | uint32_t out_offset, | |
| 487 | uint32_t out_len | |
| 488 | ) { | |
| 489 | assert_bounds(state); | |
| 490 | assert_bounds(out); | |
| 491 | ||
| 492 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 493 | ||
| 494 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 495 | ||
| 496 | return crypto_generichash_final(state_data, &out[out_offset], out_len); | |
| 497 | } | |
| 498 | ||
| 499 | static inline int | |
| 500 | sn_crypto_box_keypair(js_env_t *, js_receiver_t, js_typedarray_span_t<> pk, js_typedarray_span_t<> sk) { | |
| 501 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 502 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 503 | ||
| 504 | return crypto_box_keypair(pk.data(), sk.data()); | |
| 505 | } | |
| 506 | ||
| 507 | static inline int | |
| 508 | sn_crypto_box_seed_keypair( | |
| 509 | js_env_t *, | |
| 510 | js_receiver_t, | |
| 511 | js_typedarray_span_t<> pk, | |
| 512 | js_typedarray_span_t<> sk, | |
| 513 | js_typedarray_span_t<> seed | |
| 514 | ) { | |
| 515 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 516 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 517 | assert(seed.size_bytes() == crypto_box_SEEDBYTES); | |
| 518 | ||
| 519 | return crypto_box_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 520 | } | |
| 521 | ||
| 522 | static inline int | |
| 523 | sn_crypto_box_easy( | |
| 524 | js_env_t *, | |
| 525 | js_receiver_t, | |
| 526 | js_typedarray_span_t<> c, | |
| 527 | js_typedarray_span_t<> m, | |
| 528 | js_typedarray_span_t<> n, | |
| 529 | js_typedarray_span_t<> pk, | |
| 530 | js_typedarray_span_t<> sk | |
| 531 | ) { | |
| 532 | assert(c.size_bytes() == m.size_bytes() + crypto_box_MACBYTES); | |
| 533 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 534 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 535 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 536 | ||
| 537 | return crypto_box_easy(c.data(), m.data(), m.size_bytes(), n.data(), pk.data(), sk.data()); | |
| 538 | } | |
| 539 | ||
| 540 | static inline bool | |
| 541 | sn_crypto_box_open_easy( | |
| 542 | js_env_t *, | |
| 543 | js_receiver_t, | |
| 544 | js_typedarray_span_t<> m, | |
| 545 | js_typedarray_span_t<> c, | |
| 546 | js_typedarray_span_t<> n, | |
| 547 | js_typedarray_span_t<> pk, | |
| 548 | js_typedarray_span_t<> sk | |
| 549 | ) { | |
| 550 | assert(c.size_bytes() >= crypto_box_MACBYTES); | |
| 551 | assert(m.size_bytes() == c.size_bytes() - crypto_box_MACBYTES); | |
| 552 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 553 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 554 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 555 | ||
| 556 | return crypto_box_open_easy(m.data(), c.data(), c.size_bytes(), n.data(), pk.data(), sk.data()) == 0; | |
| 557 | } | |
| 558 | ||
| 559 | static inline int | |
| 560 | sn_crypto_box_detached( | |
| 561 | js_env_t *, | |
| 562 | js_receiver_t, | |
| 563 | js_typedarray_span_t<> c, | |
| 564 | js_typedarray_span_t<> mac, | |
| 565 | js_typedarray_span_t<> m, | |
| 566 | js_typedarray_span_t<> n, | |
| 567 | js_typedarray_span_t<> pk, | |
| 568 | js_typedarray_span_t<> sk | |
| 569 | ) { | |
| 570 | assert(c.size_bytes() == m.size_bytes()); | |
| 571 | assert(mac.size_bytes() == crypto_box_MACBYTES); | |
| 572 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 573 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 574 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 575 | ||
| 576 | return crypto_box_detached(c.data(), mac.data(), m.data(), m.size_bytes(), n.data(), pk.data(), sk.data()); | |
| 577 | } | |
| 578 | ||
| 579 | static inline bool | |
| 580 | sn_crypto_box_open_detached( | |
| 581 | js_env_t *, | |
| 582 | js_receiver_t, | |
| 583 | js_typedarray_span_t<> m, | |
| 584 | js_typedarray_span_t<> c, | |
| 585 | js_typedarray_span_t<> mac, | |
| 586 | js_typedarray_span_t<> n, | |
| 587 | js_typedarray_span_t<> pk, | |
| 588 | js_typedarray_span_t<> sk | |
| 589 | ) { | |
| 590 | assert(m.size_bytes() == c.size_bytes()); | |
| 591 | assert(mac.size_bytes() == crypto_box_MACBYTES); | |
| 592 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 593 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 594 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 595 | ||
| 596 | return crypto_box_open_detached(m.data(), c.data(), mac.data(), c.size_bytes(), n.data(), pk.data(), sk.data()) == 0; | |
| 597 | } | |
| 598 | ||
| 599 | static inline int | |
| 600 | sn_crypto_box_seal( | |
| 601 | js_env_t *, | |
| 602 | js_receiver_t, | |
| 603 | js_typedarray_span_t<> c, | |
| 604 | js_typedarray_span_t<> m, | |
| 605 | js_typedarray_span_t<> pk | |
| 606 | ) { | |
| 607 | assert(c.size_bytes() == m.size_bytes() + crypto_box_SEALBYTES); | |
| 608 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 609 | ||
| 610 | return crypto_box_seal(c.data(), m.data(), m.size_bytes(), pk.data()); | |
| 611 | } | |
| 612 | ||
| 613 | static inline bool | |
| 614 | sn_crypto_box_seal_open( | |
| 615 | js_env_t *env, | |
| 616 | js_receiver_t, | |
| 617 | ||
| 618 | js_arraybuffer_span_t m, | |
| 619 | uint32_t m_offset, | |
| 620 | uint32_t m_len, | |
| 621 | ||
| 622 | js_arraybuffer_span_t c, | |
| 623 | uint32_t c_offset, | |
| 624 | uint32_t c_len, | |
| 625 | ||
| 626 | js_arraybuffer_span_t pk, | |
| 627 | uint32_t pk_offset, | |
| 628 | uint32_t pk_len, | |
| 629 | ||
| 630 | js_arraybuffer_span_t sk, | |
| 631 | uint32_t sk_offset, | |
| 632 | uint32_t sk_len | |
| 633 | ) { | |
| 634 | assert_bounds(m); | |
| 635 | assert_bounds(c); | |
| 636 | assert_bounds(pk); | |
| 637 | assert_bounds(sk); | |
| 638 | ||
| 639 | assert(m_len == c_len - crypto_box_SEALBYTES); | |
| 640 | assert(c_len >= crypto_box_SEALBYTES); | |
| 641 | assert(sk_len == crypto_box_SECRETKEYBYTES); | |
| 642 | assert(pk_len == crypto_box_PUBLICKEYBYTES); | |
| 643 | ||
| 644 | return crypto_box_seal_open(&m[m_offset], &c[c_offset], c_len, &pk[pk_offset], &sk[sk_offset]) == 0; | |
| 645 | } | |
| 646 | ||
| 647 | static inline int | |
| 648 | sn_crypto_secretbox_easy( | |
| 649 | js_env_t *, | |
| 650 | js_receiver_t, | |
| 651 | js_typedarray_span_t<> c, | |
| 652 | js_typedarray_span_t<> m, | |
| 653 | js_typedarray_span_t<> n, | |
| 654 | js_typedarray_span_t<> k | |
| 655 | ) { | |
| 656 | assert(c.size_bytes() == m.size_bytes() + crypto_secretbox_MACBYTES); | |
| 657 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 658 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 659 | ||
| 660 | return crypto_secretbox_easy(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 661 | } | |
| 662 | ||
| 663 | static inline bool | |
| 664 | sn_crypto_secretbox_open_easy( | |
| 665 | js_env_t *, | |
| 666 | js_receiver_t, | |
| 667 | js_typedarray_span_t<> m, | |
| 668 | js_typedarray_span_t<> c, | |
| 669 | js_typedarray_span_t<> n, | |
| 670 | js_typedarray_span_t<> k | |
| 671 | ) { | |
| 672 | assert(m.size_bytes() == c.size_bytes() - crypto_secretbox_MACBYTES); | |
| 673 | assert(c.size_bytes() >= crypto_secretbox_MACBYTES); | |
| 674 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 675 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 676 | ||
| 677 | return crypto_secretbox_open_easy(m.data(), c.data(), c.size_bytes(), n.data(), k.data()) == 0; | |
| 678 | } | |
| 679 | ||
| 680 | static inline int | |
| 681 | sn_crypto_secretbox_detached( | |
| 682 | js_env_t *, | |
| 683 | js_receiver_t, | |
| 684 | js_typedarray_span_t<> c, | |
| 685 | js_typedarray_span_t<> mac, | |
| 686 | js_typedarray_span_t<> m, | |
| 687 | js_typedarray_span_t<> n, | |
| 688 | js_typedarray_span_t<> k | |
| 689 | ) { | |
| 690 | assert(c.size_bytes() == m.size_bytes()); | |
| 691 | assert(mac.size_bytes() == crypto_secretbox_MACBYTES); | |
| 692 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 693 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 694 | ||
| 695 | return crypto_secretbox_detached(c.data(), mac.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 696 | } | |
| 697 | ||
| 698 | static inline bool | |
| 699 | sn_crypto_secretbox_open_detached( | |
| 700 | js_env_t *, | |
| 701 | js_receiver_t, | |
| 702 | js_typedarray_span_t<> m, | |
| 703 | js_typedarray_span_t<> c, | |
| 704 | js_typedarray_span_t<> mac, | |
| 705 | js_typedarray_span_t<> n, | |
| 706 | js_typedarray_span_t<> k | |
| 707 | ) { | |
| 708 | assert(m.size_bytes() == c.size_bytes()); | |
| 709 | assert(mac.size_bytes() == crypto_secretbox_MACBYTES); | |
| 710 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 711 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 712 | ||
| 713 | return crypto_secretbox_open_detached(m.data(), c.data(), mac.data(), c.size_bytes(), n.data(), k.data()) == 0; | |
| 714 | } | |
| 715 | ||
| 716 | static inline int | |
| 717 | sn_crypto_stream( | |
| 718 | js_env_t *, | |
| 719 | js_receiver_t, | |
| 720 | js_typedarray_span_t<> c, | |
| 721 | js_typedarray_span_t<> n, | |
| 722 | js_typedarray_span_t<> k | |
| 723 | ) { | |
| 724 | assert(n.size_bytes() == crypto_stream_NONCEBYTES); | |
| 725 | assert(k.size_bytes() == crypto_stream_KEYBYTES); | |
| 726 | ||
| 727 | return crypto_stream(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 728 | } | |
| 729 | ||
| 730 | static inline int | |
| 731 | sn_crypto_stream_xor( | |
| 732 | js_env_t *env, | |
| 733 | js_receiver_t, | |
| 734 | ||
| 735 | js_arraybuffer_span_t c, | |
| 736 | uint32_t c_offset, | |
| 737 | uint32_t c_len, | |
| 738 | ||
| 739 | js_arraybuffer_span_t m, | |
| 740 | uint32_t m_offset, | |
| 741 | uint32_t m_len, | |
| 742 | ||
| 743 | js_arraybuffer_span_t n, | |
| 744 | uint32_t n_offset, | |
| 745 | uint32_t n_len, | |
| 746 | ||
| 747 | js_arraybuffer_span_t k, | |
| 748 | uint32_t k_offset, | |
| 749 | uint32_t k_len | |
| 750 | ) { | |
| 751 | assert_bounds(c); | |
| 752 | assert_bounds(m); | |
| 753 | assert_bounds(n); | |
| 754 | assert_bounds(k); | |
| 755 | ||
| 756 | assert(c_len == m_len); | |
| 757 | assert(n_len == crypto_stream_NONCEBYTES); | |
| 758 | assert(k_len == crypto_stream_KEYBYTES); | |
| 759 | ||
| 760 | return crypto_stream_xor(&c[c_offset], &m[m_offset], m_len, &n[n_offset], &k[k_offset]); | |
| 761 | } | |
| 762 | ||
| 763 | static inline int | |
| 764 | sn_crypto_stream_chacha20( | |
| 765 | js_env_t *, | |
| 766 | js_receiver_t, | |
| 767 | js_typedarray_span_t<> c, | |
| 768 | js_typedarray_span_t<> n, | |
| 769 | js_typedarray_span_t<> k | |
| 770 | ) { | |
| 771 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 772 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 773 | ||
| 774 | return crypto_stream_chacha20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 775 | } | |
| 776 | ||
| 777 | static inline int | |
| 778 | sn_crypto_stream_chacha20_xor( | |
| 779 | js_env_t *, | |
| 780 | js_receiver_t, | |
| 781 | js_typedarray_span_t<> c, | |
| 782 | js_typedarray_span_t<> m, | |
| 783 | js_typedarray_span_t<> n, | |
| 784 | js_typedarray_span_t<> k | |
| 785 | ) { | |
| 786 | assert(c.size_bytes() == m.size_bytes()); | |
| 787 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 788 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 789 | ||
| 790 | return crypto_stream_chacha20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 791 | } | |
| 792 | ||
| 793 | static inline int | |
| 794 | sn_crypto_stream_chacha20_xor_ic( | |
| 795 | js_env_t *, | |
| 796 | js_receiver_t, | |
| 797 | js_typedarray_span_t<> c, | |
| 798 | js_typedarray_span_t<> m, | |
| 799 | js_typedarray_span_t<> n, | |
| 800 | uint32_t ic, | |
| 801 | js_typedarray_span_t<> k | |
| 802 | ) { | |
| 803 | assert(c.size_bytes() == m.size_bytes()); | |
| 804 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 805 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 806 | ||
| 807 | return crypto_stream_chacha20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 808 | } | |
| 809 | ||
| 810 | static inline int | |
| 811 | sn_crypto_stream_chacha20_ietf( | |
| 812 | js_env_t *, | |
| 813 | js_receiver_t, | |
| 814 | js_typedarray_span_t<> c, | |
| 815 | js_typedarray_span_t<> n, | |
| 816 | js_typedarray_span_t<> k | |
| 817 | ) { | |
| 818 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 819 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 820 | ||
| 821 | return crypto_stream_chacha20_ietf(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 822 | } | |
| 823 | ||
| 824 | static inline int | |
| 825 | sn_crypto_stream_chacha20_ietf_xor( | |
| 826 | js_env_t *, | |
| 827 | js_receiver_t, | |
| 828 | js_typedarray_span_t<> c, | |
| 829 | js_typedarray_span_t<> m, | |
| 830 | js_typedarray_span_t<> n, | |
| 831 | js_typedarray_span_t<> k | |
| 832 | ) { | |
| 833 | assert(c.size_bytes() == m.size_bytes()); | |
| 834 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 835 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 836 | ||
| 837 | return crypto_stream_chacha20_ietf_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 838 | } | |
| 839 | ||
| 840 | static inline int | |
| 841 | sn_crypto_stream_chacha20_ietf_xor_ic( | |
| 842 | js_env_t *, | |
| 843 | js_receiver_t, | |
| 844 | js_typedarray_span_t<> c, | |
| 845 | js_typedarray_span_t<> m, | |
| 846 | js_typedarray_span_t<> n, | |
| 847 | uint32_t ic, | |
| 848 | js_typedarray_span_t<> k | |
| 849 | ) { | |
| 850 | assert(c.size_bytes() == m.size_bytes()); | |
| 851 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 852 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 853 | ||
| 854 | return crypto_stream_chacha20_ietf_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 855 | } | |
| 856 | ||
| 857 | static inline int | |
| 858 | sn_crypto_stream_xchacha20( | |
| 859 | js_env_t *, | |
| 860 | js_receiver_t, | |
| 861 | js_typedarray_span_t<> c, | |
| 862 | js_typedarray_span_t<> n, | |
| 863 | js_typedarray_span_t<> k | |
| 864 | ) { | |
| 865 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 866 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 867 | ||
| 868 | return crypto_stream_xchacha20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 869 | } | |
| 870 | ||
| 871 | static inline int | |
| 872 | sn_crypto_stream_xchacha20_xor( | |
| 873 | js_env_t *, | |
| 874 | js_receiver_t, | |
| 875 | js_typedarray_span_t<> c, | |
| 876 | js_typedarray_span_t<> m, | |
| 877 | js_typedarray_span_t<> n, | |
| 878 | js_typedarray_span_t<> k | |
| 879 | ) { | |
| 880 | assert(c.size_bytes() == m.size_bytes()); | |
| 881 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 882 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 883 | ||
| 884 | return crypto_stream_xchacha20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 885 | } | |
| 886 | ||
| 887 | static inline int | |
| 888 | sn_crypto_stream_xchacha20_xor_ic( | |
| 889 | js_env_t *, | |
| 890 | js_receiver_t, | |
| 891 | js_typedarray_span_t<> c, | |
| 892 | js_typedarray_span_t<> m, | |
| 893 | js_typedarray_span_t<> n, | |
| 894 | uint32_t ic, | |
| 895 | js_typedarray_span_t<> k | |
| 896 | ) { | |
| 897 | assert(c.size_bytes() == m.size_bytes()); | |
| 898 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 899 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 900 | ||
| 901 | return crypto_stream_xchacha20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 902 | } | |
| 903 | ||
| 904 | static inline int | |
| 905 | sn_crypto_stream_salsa20( | |
| 906 | js_env_t *, | |
| 907 | js_receiver_t, | |
| 908 | js_typedarray_span_t<> c, | |
| 909 | js_typedarray_span_t<> n, | |
| 910 | js_typedarray_span_t<> k | |
| 911 | ) { | |
| 912 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 913 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 914 | ||
| 915 | return crypto_stream_salsa20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 916 | } | |
| 917 | ||
| 918 | static inline int | |
| 919 | sn_crypto_stream_salsa20_xor( | |
| 920 | js_env_t *, | |
| 921 | js_receiver_t, | |
| 922 | js_typedarray_span_t<> c, | |
| 923 | js_typedarray_span_t<> m, | |
| 924 | js_typedarray_span_t<> n, | |
| 925 | js_typedarray_span_t<> k | |
| 926 | ) { | |
| 927 | assert(c.size_bytes() == m.size_bytes()); | |
| 928 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 929 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 930 | ||
| 931 | return crypto_stream_salsa20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 932 | } | |
| 933 | ||
| 934 | static inline int | |
| 935 | sn_crypto_stream_salsa20_xor_ic( | |
| 936 | js_env_t *, | |
| 937 | js_receiver_t, | |
| 938 | js_typedarray_span_t<> c, | |
| 939 | js_typedarray_span_t<> m, | |
| 940 | js_typedarray_span_t<> n, | |
| 941 | uint32_t ic, | |
| 942 | js_typedarray_span_t<> k | |
| 943 | ) { | |
| 944 | assert(c.size_bytes() == m.size_bytes()); | |
| 945 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 946 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 947 | ||
| 948 | return crypto_stream_salsa20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 949 | } | |
| 950 | ||
| 951 | static inline int | |
| 952 | sn_crypto_auth( | |
| 953 | js_env_t *, | |
| 954 | js_receiver_t, | |
| 955 | js_typedarray_span_t<> out, | |
| 956 | js_typedarray_span_t<> in, | |
| 957 | js_typedarray_span_t<> k | |
| 958 | ) { | |
| 959 | assert(out.size_bytes() == crypto_auth_BYTES); | |
| 960 | assert(k.size_bytes() == crypto_auth_KEYBYTES); | |
| 961 | ||
| 962 | return crypto_auth(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 963 | } | |
| 964 | ||
| 965 | static inline bool | |
| 966 | sn_crypto_auth_verify( | |
| 967 | js_env_t *, | |
| 968 | js_receiver_t, | |
| 969 | js_typedarray_span_t<> h, | |
| 970 | js_typedarray_span_t<> in, | |
| 971 | js_typedarray_span_t<> k | |
| 972 | ) { | |
| 973 | assert(h.size_bytes() == crypto_auth_BYTES); | |
| 974 | assert(k.size_bytes() == crypto_auth_KEYBYTES); | |
| 975 | ||
| 976 | return crypto_auth_verify(h.data(), in.data(), in.size_bytes(), k.data()) == 0; | |
| 977 | } | |
| 978 | ||
| 979 | static inline int | |
| 980 | sn_crypto_onetimeauth( | |
| 981 | js_env_t *, | |
| 982 | js_receiver_t, | |
| 983 | js_typedarray_span_t<> out, | |
| 984 | js_typedarray_span_t<> in, | |
| 985 | js_typedarray_span_t<> k | |
| 986 | ) { | |
| 987 | assert(out.size_bytes() == crypto_onetimeauth_BYTES); | |
| 988 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 989 | ||
| 990 | return crypto_onetimeauth(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 991 | } | |
| 992 | ||
| 993 | static inline int | |
| 994 | sn_crypto_onetimeauth_init( | |
| 995 | js_env_t *, | |
| 996 | js_receiver_t, | |
| 997 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 998 | js_typedarray_span_t<> k | |
| 999 | ) { | |
| 1000 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 1001 | ||
| 1002 | return crypto_onetimeauth_init(&*state, k.data()); | |
| 1003 | } | |
| 1004 | ||
| 1005 | static inline int | |
| 1006 | sn_crypto_onetimeauth_update( | |
| 1007 | js_env_t *, | |
| 1008 | js_receiver_t, | |
| 1009 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 1010 | js_typedarray_span_t<> in | |
| 1011 | ) { | |
| 1012 | return crypto_onetimeauth_update(&*state, in.data(), in.size_bytes()); | |
| 1013 | } | |
| 1014 | ||
| 1015 | static inline int | |
| 1016 | sn_crypto_onetimeauth_final( | |
| 1017 | js_env_t *, | |
| 1018 | js_receiver_t, | |
| 1019 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 1020 | js_typedarray_span_t<> out | |
| 1021 | ) { | |
| 1022 | assert(out.size_bytes() == crypto_onetimeauth_BYTES); | |
| 1023 | ||
| 1024 | return crypto_onetimeauth_final(&*state, out.data()); | |
| 1025 | } | |
| 1026 | ||
| 1027 | static inline bool | |
| 1028 | sn_crypto_onetimeauth_verify( | |
| 1029 | js_env_t *, | |
| 1030 | js_receiver_t, | |
| 1031 | js_typedarray_span_t<> h, | |
| 1032 | js_typedarray_span_t<> in, | |
| 1033 | js_typedarray_span_t<> k | |
| 1034 | ) { | |
| 1035 | assert(h.size_bytes() == crypto_onetimeauth_BYTES); | |
| 1036 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 1037 | ||
| 1038 | return crypto_onetimeauth_verify(h.data(), in.data(), in.size_bytes(), k.data()) == 0; | |
| 1039 | } | |
| 1040 | ||
| 1041 | // CHECK: memlimit can be >32bit | |
| 1042 | static inline int | |
| 1043 | sn_crypto_pwhash( | |
| 1044 | js_env_t *, | |
| 1045 | js_receiver_t, | |
| 1046 | js_typedarray_span_t<> out, | |
| 1047 | js_typedarray_span_of_t<char> passwd, | |
| 1048 | js_typedarray_span_t<> salt, | |
| 1049 | uint64_t opslimit, | |
| 1050 | uint64_t memlimit, | |
| 1051 | int32_t alg | |
| 1052 | ) { | |
| 1053 | assert(out.size_bytes() >= crypto_pwhash_BYTES_MIN); | |
| 1054 | assert(out.size_bytes() <= crypto_pwhash_BYTES_MAX); | |
| 1055 | assert(salt.size_bytes() == crypto_pwhash_SALTBYTES); | |
| 1056 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1057 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1058 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1059 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1060 | assert(alg == 1 || alg == 2); // Argon2i or Argon2id | |
| 1061 | ||
| 1062 | return crypto_pwhash( | |
| 1063 | out.data(), | |
| 1064 | out.size_bytes(), | |
| 1065 | passwd.data(), | |
| 1066 | passwd.size_bytes(), | |
| 1067 | salt.data(), | |
| 1068 | opslimit, | |
| 1069 | memlimit, | |
| 1070 | alg | |
| 1071 | ); | |
| 1072 | } | |
| 1073 | ||
| 1074 | static inline int | |
| 1075 | sn_crypto_pwhash_str( | |
| 1076 | js_env_t *, | |
| 1077 | js_receiver_t, | |
| 1078 | js_typedarray_span_of_t<char> out, | |
| 1079 | js_typedarray_span_of_t<char> passwd, | |
| 1080 | uint64_t opslimit, | |
| 1081 | uint64_t memlimit | |
| 1082 | ) { | |
| 1083 | assert(out.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1084 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1085 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1086 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1087 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1088 | ||
| 1089 | return crypto_pwhash_str( | |
| 1090 | out.data(), | |
| 1091 | passwd.data(), | |
| 1092 | passwd.size_bytes(), | |
| 1093 | opslimit, | |
| 1094 | memlimit | |
| 1095 | ); | |
| 1096 | } | |
| 1097 | ||
| 1098 | static inline bool | |
| 1099 | sn_crypto_pwhash_str_verify( | |
| 1100 | js_env_t *, | |
| 1101 | js_receiver_t, | |
| 1102 | js_typedarray_span_of_t<char> str, | |
| 1103 | js_typedarray_span_of_t<char> passwd | |
| 1104 | ) { | |
| 1105 | assert(str.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1106 | ||
| 1107 | return crypto_pwhash_str_verify(str.data(), passwd.data(), passwd.size_bytes()) == 0; | |
| 1108 | } | |
| 1109 | ||
| 1110 | // CHECK: returns 1, 0, -1 | |
| 1111 | static inline bool | |
| 1112 | sn_crypto_pwhash_str_needs_rehash( | |
| 1113 | js_env_t *, | |
| 1114 | js_receiver_t, | |
| 1115 | js_typedarray_span_of_t<char> str, | |
| 1116 | uint64_t opslimit, | |
| 1117 | uint64_t memlimit | |
| 1118 | ) { | |
| 1119 | assert(str.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1120 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1121 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1122 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1123 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1124 | ||
| 1125 | return crypto_pwhash_str_needs_rehash(str.data(), opslimit, memlimit) != 0; | |
| 1126 | } | |
| 1127 | ||
| 1128 | // CHECK: memlimit can be >32bit | |
| 1129 | static inline int | |
| 1130 | sn_crypto_pwhash_scryptsalsa208sha256( | |
| 1131 | js_env_t *, | |
| 1132 | js_receiver_t, | |
| 1133 | js_typedarray_span_t<> out, | |
| 1134 | js_typedarray_span_of_t<char> passwd, | |
| 1135 | js_typedarray_span_t<> salt, | |
| 1136 | uint64_t opslimit, | |
| 1137 | uint64_t memlimit | |
| 1138 | ) { | |
| 1139 | assert(out.size_bytes() >= crypto_pwhash_scryptsalsa208sha256_BYTES_MIN); | |
| 1140 | assert(out.size_bytes() <= crypto_pwhash_scryptsalsa208sha256_BYTES_MAX); | |
| 1141 | assert(salt.size_bytes() == crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 1142 | assert(opslimit >= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 1143 | assert(opslimit <= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 1144 | assert(memlimit >= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 1145 | assert(memlimit <= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 1146 | ||
| 1147 | return crypto_pwhash_scryptsalsa208sha256( | |
| 1148 | out.data(), | |
| 1149 | out.size_bytes(), | |
| 1150 | passwd.data(), | |
| 1151 | passwd.size_bytes(), | |
| 1152 | salt.data(), | |
| 1153 | opslimit, | |
| 1154 | memlimit | |
| 1155 | ); | |
| 1156 | } | |
| 1157 | ||
| 1158 | static inline int | |
| 1159 | sn_crypto_pwhash_scryptsalsa208sha256_str( | |
| 1160 | js_env_t *, | |
| 1161 | js_receiver_t, | |
| 1162 | js_typedarray_span_of_t<char> out, | |
| 1163 | js_typedarray_span_of_t<char> passwd, | |
| 1164 | uint64_t opslimit, | |
| 1165 | uint64_t memlimit | |
| 1166 | ) { | |
| 1167 | assert(out.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1168 | assert(opslimit >= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 1169 | assert(opslimit <= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 1170 | assert(memlimit >= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 1171 | assert(memlimit <= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 1172 | ||
| 1173 | return crypto_pwhash_scryptsalsa208sha256_str( | |
| 1174 | out.data(), | |
| 1175 | passwd.data(), | |
| 1176 | passwd.size_bytes(), | |
| 1177 | opslimit, | |
| 1178 | memlimit | |
| 1179 | ); | |
| 1180 | } | |
| 1181 | ||
| 1182 | static inline bool | |
| 1183 | sn_crypto_pwhash_scryptsalsa208sha256_str_verify( | |
| 1184 | js_env_t *, | |
| 1185 | js_receiver_t, | |
| 1186 | js_typedarray_span_of_t<char> str, | |
| 1187 | js_typedarray_span_of_t<char> passwd | |
| 1188 | ) { | |
| 1189 | assert(str.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1190 | ||
| 1191 | return crypto_pwhash_scryptsalsa208sha256_str_verify(str.data(), passwd.data(), passwd.size_bytes()) == 0; | |
| 1192 | } | |
| 1193 | ||
| 1194 | static inline bool | |
| 1195 | sn_crypto_pwhash_scryptsalsa208sha256_str_needs_rehash( | |
| 1196 | js_env_t *, | |
| 1197 | js_receiver_t, | |
| 1198 | js_typedarray_span_of_t<char> str, | |
| 1199 | uint64_t opslimit, | |
| 1200 | uint64_t memlimit | |
| 1201 | ) { | |
| 1202 | assert(str.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1203 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1204 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1205 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1206 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1207 | ||
| 1208 | return crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(str.data(), opslimit, memlimit) != 0; | |
| 1209 | } | |
| 1210 | ||
| 1211 | static inline int | |
| 1212 | sn_crypto_kx_keypair( | |
| 1213 | js_env_t *, | |
| 1214 | js_receiver_t, | |
| 1215 | js_typedarray_span_t<> pk, | |
| 1216 | js_typedarray_span_t<> sk | |
| 1217 | ) { | |
| 1218 | assert(pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1219 | assert(sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1220 | ||
| 1221 | return crypto_kx_keypair(pk.data(), sk.data()); | |
| 1222 | } | |
| 1223 | ||
| 1224 | static inline int | |
| 1225 | sn_crypto_kx_seed_keypair( | |
| 1226 | js_env_t *, | |
| 1227 | js_receiver_t, | |
| 1228 | js_typedarray_span_t<> pk, | |
| 1229 | js_typedarray_span_t<> sk, | |
| 1230 | js_typedarray_span_t<> seed | |
| 1231 | ) { | |
| 1232 | assert(pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1233 | assert(sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1234 | assert(seed.size_bytes() == crypto_kx_SEEDBYTES); | |
| 1235 | ||
| 1236 | return crypto_kx_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 1237 | } | |
| 1238 | ||
| 1239 | static inline int | |
| 1240 | sn_crypto_kx_client_session_keys( | |
| 1241 | js_env_t *, | |
| 1242 | js_receiver_t, | |
| 1243 | std::optional<js_typedarray_span_t<>> rx, | |
| 1244 | std::optional<js_typedarray_span_t<>> tx, | |
| 1245 | js_typedarray_span_t<> client_pk, | |
| 1246 | js_typedarray_span_t<> client_sk, | |
| 1247 | js_typedarray_span_t<> server_pk | |
| 1248 | ) { | |
| 1249 | assert(rx.has_value() || tx.has_value()); | |
| 1250 | ||
| 1251 | if (rx) assert(rx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1252 | if (tx) assert(tx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1253 | ||
| 1254 | assert(client_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1255 | assert(client_sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1256 | assert(server_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1257 | ||
| 1258 | return crypto_kx_client_session_keys( | |
| 1259 | rx ? rx->data() : nullptr, | |
| 1260 | tx ? tx->data() : nullptr, | |
| 1261 | client_pk.data(), | |
| 1262 | client_sk.data(), | |
| 1263 | server_pk.data() | |
| 1264 | ); | |
| 1265 | } | |
| 1266 | ||
| 1267 | static inline int | |
| 1268 | sn_crypto_kx_server_session_keys( | |
| 1269 | js_env_t *, | |
| 1270 | js_receiver_t, | |
| 1271 | std::optional<js_typedarray_span_t<>> rx, | |
| 1272 | std::optional<js_typedarray_span_t<>> tx, | |
| 1273 | js_typedarray_span_t<> server_pk, | |
| 1274 | js_typedarray_span_t<> server_sk, | |
| 1275 | js_typedarray_span_t<> client_pk | |
| 1276 | ) { | |
| 1277 | assert(rx.has_value() || tx.has_value()); | |
| 1278 | ||
| 1279 | if (rx) assert(rx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1280 | if (tx) assert(tx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1281 | ||
| 1282 | assert(server_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1283 | assert(server_sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1284 | assert(client_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1285 | ||
| 1286 | return crypto_kx_server_session_keys( | |
| 1287 | rx ? rx->data() : nullptr, | |
| 1288 | tx ? tx->data() : nullptr, | |
| 1289 | server_pk.data(), | |
| 1290 | server_sk.data(), | |
| 1291 | client_pk.data() | |
| 1292 | ); | |
| 1293 | } | |
| 1294 | ||
| 1295 | static inline int | |
| 1296 | sn_crypto_scalarmult_base( | |
| 1297 | js_env_t *, | |
| 1298 | js_receiver_t, | |
| 1299 | js_typedarray_span_t<> q, | |
| 1300 | js_typedarray_span_t<> n | |
| 1301 | ) { | |
| 1302 | assert(q.size_bytes() == crypto_scalarmult_BYTES); | |
| 1303 | assert(n.size_bytes() == crypto_scalarmult_SCALARBYTES); | |
| 1304 | ||
| 1305 | return crypto_scalarmult_base(q.data(), n.data()); | |
| 1306 | } | |
| 1307 | ||
| 1308 | static inline int | |
| 1309 | sn_crypto_scalarmult( | |
| 1310 | js_env_t *, | |
| 1311 | js_receiver_t, | |
| 1312 | js_typedarray_span_t<> q, | |
| 1313 | js_typedarray_span_t<> n, | |
| 1314 | js_typedarray_span_t<> p | |
| 1315 | ) { | |
| 1316 | assert(q.size_bytes() == crypto_scalarmult_BYTES); | |
| 1317 | assert(n.size_bytes() == crypto_scalarmult_SCALARBYTES); | |
| 1318 | assert(p.size_bytes() == crypto_scalarmult_BYTES); | |
| 1319 | ||
| 1320 | return crypto_scalarmult(q.data(), n.data(), p.data()); | |
| 1321 | } | |
| 1322 | ||
| 1323 | static inline int | |
| 1324 | sn_crypto_scalarmult_ed25519_base( | |
| 1325 | js_env_t *, | |
| 1326 | js_receiver_t, | |
| 1327 | js_typedarray_span_t<> q, | |
| 1328 | js_typedarray_span_t<> n | |
| 1329 | ) { | |
| 1330 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1331 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1332 | ||
| 1333 | return crypto_scalarmult_ed25519_base(q.data(), n.data()); | |
| 1334 | } | |
| 1335 | ||
| 1336 | static inline int | |
| 1337 | sn_crypto_scalarmult_ed25519( | |
| 1338 | js_env_t *, | |
| 1339 | js_receiver_t, | |
| 1340 | js_typedarray_span_t<> q, | |
| 1341 | js_typedarray_span_t<> n, | |
| 1342 | js_typedarray_span_t<> p | |
| 1343 | ) { | |
| 1344 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1345 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1346 | assert(p.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1347 | ||
| 1348 | return crypto_scalarmult_ed25519(q.data(), n.data(), p.data()); | |
| 1349 | } | |
| 1350 | ||
| 1351 | static inline bool | |
| 1352 | sn_crypto_core_ed25519_is_valid_point( | |
| 1353 | js_env_t *, | |
| 1354 | js_receiver_t, | |
| 1355 | js_typedarray_span_t<> p | |
| 1356 | ) { | |
| 1357 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1358 | ||
| 1359 | return crypto_core_ed25519_is_valid_point(p.data()) != 0; | |
| 1360 | } | |
| 1361 | ||
| 1362 | static inline int | |
| 1363 | sn_crypto_core_ed25519_from_uniform( | |
| 1364 | js_env_t *, | |
| 1365 | js_receiver_t, | |
| 1366 | js_typedarray_span_t<> p, | |
| 1367 | js_typedarray_span_t<> r | |
| 1368 | ) { | |
| 1369 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1370 | assert(r.size_bytes() == crypto_core_ed25519_UNIFORMBYTES); | |
| 1371 | ||
| 1372 | return crypto_core_ed25519_from_uniform(p.data(), r.data()); | |
| 1373 | } | |
| 1374 | ||
| 1375 | static inline int | |
| 1376 | sn_crypto_scalarmult_ed25519_base_noclamp( | |
| 1377 | js_env_t *, | |
| 1378 | js_receiver_t, | |
| 1379 | js_typedarray_span_t<> q, | |
| 1380 | js_typedarray_span_t<> n | |
| 1381 | ) { | |
| 1382 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1383 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1384 | ||
| 1385 | return crypto_scalarmult_ed25519_base_noclamp(q.data(), n.data()); | |
| 1386 | } | |
| 1387 | ||
| 1388 | static inline int | |
| 1389 | sn_crypto_scalarmult_ed25519_noclamp( | |
| 1390 | js_env_t *, | |
| 1391 | js_receiver_t, | |
| 1392 | js_typedarray_span_t<> q, | |
| 1393 | js_typedarray_span_t<> n, | |
| 1394 | js_typedarray_span_t<> p | |
| 1395 | ) { | |
| 1396 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1397 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1398 | assert(p.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1399 | ||
| 1400 | return crypto_scalarmult_ed25519_noclamp(q.data(), n.data(), p.data()); | |
| 1401 | } | |
| 1402 | ||
| 1403 | static inline int | |
| 1404 | sn_crypto_core_ed25519_add( | |
| 1405 | js_env_t *, | |
| 1406 | js_receiver_t, | |
| 1407 | js_typedarray_span_t<> r, | |
| 1408 | js_typedarray_span_t<> p, | |
| 1409 | js_typedarray_span_t<> q | |
| 1410 | ) { | |
| 1411 | assert(r.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1412 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1413 | assert(q.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1414 | ||
| 1415 | return crypto_core_ed25519_add(r.data(), p.data(), q.data()); | |
| 1416 | } | |
| 1417 | ||
| 1418 | static inline int | |
| 1419 | sn_crypto_core_ed25519_sub( | |
| 1420 | js_env_t *, | |
| 1421 | js_receiver_t, | |
| 1422 | js_typedarray_span_t<> r, | |
| 1423 | js_typedarray_span_t<> p, | |
| 1424 | js_typedarray_span_t<> q | |
| 1425 | ) { | |
| 1426 | assert(r.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1427 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1428 | assert(q.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1429 | ||
| 1430 | return crypto_core_ed25519_sub(r.data(), p.data(), q.data()); | |
| 1431 | } | |
| 1432 | ||
| 1433 | static inline void | |
| 1434 | sn_crypto_core_ed25519_scalar_random( | |
| 1435 | js_env_t *, | |
| 1436 | js_receiver_t, | |
| 1437 | js_typedarray_span_t<> r | |
| 1438 | ) { | |
| 1439 | assert(r.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1440 | ||
| 1441 | crypto_core_ed25519_scalar_random(r.data()); | |
| 1442 | } | |
| 1443 | ||
| 1444 | static inline void | |
| 1445 | sn_crypto_core_ed25519_scalar_reduce( | |
| 1446 | js_env_t *, | |
| 1447 | js_receiver_t, | |
| 1448 | js_typedarray_span_t<> r, | |
| 1449 | js_typedarray_span_t<> s | |
| 1450 | ) { | |
| 1451 | assert(r.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1452 | assert(s.size_bytes() == crypto_core_ed25519_NONREDUCEDSCALARBYTES); | |
| 1453 | ||
| 1454 | crypto_core_ed25519_scalar_reduce(r.data(), s.data()); | |
| 1455 | } | |
| 1456 | ||
| 1457 | static inline void | |
| 1458 | sn_crypto_core_ed25519_scalar_invert( | |
| 1459 | js_env_t *, | |
| 1460 | js_receiver_t, | |
| 1461 | js_typedarray_span_t<> recip, | |
| 1462 | js_typedarray_span_t<> s | |
| 1463 | ) { | |
| 1464 | assert(recip.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1465 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1466 | ||
| 1467 | crypto_core_ed25519_scalar_invert(recip.data(), s.data()); | |
| 1468 | } | |
| 1469 | ||
| 1470 | static inline void | |
| 1471 | sn_crypto_core_ed25519_scalar_negate( | |
| 1472 | js_env_t *, | |
| 1473 | js_receiver_t, | |
| 1474 | js_typedarray_span_t<> neg, | |
| 1475 | js_typedarray_span_t<> s | |
| 1476 | ) { | |
| 1477 | assert(neg.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1478 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1479 | ||
| 1480 | crypto_core_ed25519_scalar_negate(neg.data(), s.data()); | |
| 1481 | } | |
| 1482 | ||
| 1483 | static inline void | |
| 1484 | sn_crypto_core_ed25519_scalar_complement( | |
| 1485 | js_env_t *, | |
| 1486 | js_receiver_t, | |
| 1487 | js_typedarray_span_t<> comp, | |
| 1488 | js_typedarray_span_t<> s | |
| 1489 | ) { | |
| 1490 | assert(comp.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1491 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1492 | ||
| 1493 | crypto_core_ed25519_scalar_complement(comp.data(), s.data()); | |
| 1494 | } | |
| 1495 | ||
| 1496 | static inline void | |
| 1497 | sn_crypto_core_ed25519_scalar_add( | |
| 1498 | js_env_t *, | |
| 1499 | js_receiver_t, | |
| 1500 | js_typedarray_span_t<> z, | |
| 1501 | js_typedarray_span_t<> x, | |
| 1502 | js_typedarray_span_t<> y | |
| 1503 | ) { | |
| 1504 | assert(z.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1505 | assert(x.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1506 | assert(y.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1507 | ||
| 1508 | crypto_core_ed25519_scalar_add(z.data(), x.data(), y.data()); | |
| 1509 | } | |
| 1510 | ||
| 1511 | static inline void | |
| 1512 | sn_crypto_core_ed25519_scalar_sub( | |
| 1513 | js_env_t *, | |
| 1514 | js_receiver_t, | |
| 1515 | js_typedarray_span_t<> z, | |
| 1516 | js_typedarray_span_t<> x, | |
| 1517 | js_typedarray_span_t<> y | |
| 1518 | ) { | |
| 1519 | assert(z.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1520 | assert(x.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1521 | assert(y.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1522 | ||
| 1523 | crypto_core_ed25519_scalar_sub(z.data(), x.data(), y.data()); | |
| 1524 | } | |
| 1525 | ||
| 1526 | static inline int | |
| 1527 | sn_crypto_shorthash( | |
| 1528 | js_env_t *, | |
| 1529 | js_receiver_t, | |
| 1530 | js_typedarray_span_t<> out, | |
| 1531 | js_typedarray_span_t<> in, | |
| 1532 | js_typedarray_span_t<> k | |
| 1533 | ) { | |
| 1534 | assert(out.size_bytes() == crypto_shorthash_BYTES); | |
| 1535 | assert(k.size_bytes() == crypto_shorthash_KEYBYTES); | |
| 1536 | ||
| 1537 | return crypto_shorthash(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 1538 | } | |
| 1539 | ||
| 1540 | static inline void | |
| 1541 | sn_crypto_kdf_keygen( | |
| 1542 | js_env_t *, | |
| 1543 | js_receiver_t, | |
| 1544 | js_typedarray_span_t<> key | |
| 1545 | ) { | |
| 1546 | assert(key.size_bytes() == crypto_kdf_KEYBYTES); | |
| 1547 | ||
| 1548 | crypto_kdf_keygen(key.data()); | |
| 1549 | } | |
| 1550 | ||
| 1551 | static inline int | |
| 1552 | sn_crypto_kdf_derive_from_key( | |
| 1553 | js_env_t *, | |
| 1554 | js_receiver_t, | |
| 1555 | js_typedarray_span_t<> subkey, | |
| 1556 | uint64_t subkey_id, | |
| 1557 | js_typedarray_span_of_t<char> ctx, | |
| 1558 | js_typedarray_span_t<> key | |
| 1559 | ) { | |
| 1560 | assert(subkey.size_bytes() >= crypto_kdf_BYTES_MIN); | |
| 1561 | assert(subkey.size_bytes() <= crypto_kdf_BYTES_MAX); | |
| 1562 | assert(ctx.size_bytes() == crypto_kdf_CONTEXTBYTES); | |
| 1563 | assert(key.size_bytes() == crypto_kdf_KEYBYTES); | |
| 1564 | ||
| 1565 | return crypto_kdf_derive_from_key( | |
| 1566 | subkey.data(), | |
| 1567 | subkey.size_bytes(), | |
| 1568 | subkey_id, | |
| 1569 | ctx.data(), | |
| 1570 | key.data() | |
| 1571 | ); | |
| 1572 | } | |
| 1573 | ||
| 1574 | static inline int | |
| 1575 | sn_crypto_hash( | |
| 1576 | js_env_t *, | |
| 1577 | js_receiver_t, | |
| 1578 | js_typedarray_span_t<> out, | |
| 1579 | js_typedarray_span_t<> in | |
| 1580 | ) { | |
| 1581 | assert(out.size_bytes() == crypto_hash_BYTES); | |
| 1582 | ||
| 1583 | return crypto_hash(out.data(), in.data(), in.size_bytes()); | |
| 1584 | } | |
| 1585 | ||
| 1586 | static inline int | |
| 1587 | sn_crypto_hash_sha256( | |
| 1588 | js_env_t *, | |
| 1589 | js_receiver_t, | |
| 1590 | js_typedarray_span_t<> out, | |
| 1591 | js_typedarray_span_t<> in | |
| 1592 | ) { | |
| 1593 | assert(out.size_bytes() == crypto_hash_sha256_BYTES); | |
| 1594 | ||
| 1595 | return crypto_hash_sha256(out.data(), in.data(), in.size_bytes()); | |
| 1596 | } | |
| 1597 | ||
| 1598 | static inline int | |
| 1599 | sn_crypto_hash_sha256_init( | |
| 1600 | js_env_t *, | |
| 1601 | js_receiver_t, | |
| 1602 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state | |
| 1603 | ) { | |
| 1604 | return crypto_hash_sha256_init(&*state); | |
| 1605 | } | |
| 1606 | ||
| 1607 | static inline int | |
| 1608 | sn_crypto_hash_sha256_update( | |
| 1609 | js_env_t *, | |
| 1610 | js_receiver_t, | |
| 1611 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state, | |
| 1612 | js_typedarray_span_t<> in | |
| 1613 | ) { | |
| 1614 | return crypto_hash_sha256_update(&*state, in.data(), in.size_bytes()); | |
| 1615 | } | |
| 1616 | ||
| 1617 | static inline int | |
| 1618 | sn_crypto_hash_sha256_final( | |
| 1619 | js_env_t *, | |
| 1620 | js_receiver_t, | |
| 1621 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state, | |
| 1622 | js_typedarray_span_t<> out | |
| 1623 | ) { | |
| 1624 | assert(out.size_bytes() == crypto_hash_sha256_BYTES); | |
| 1625 | ||
| 1626 | return crypto_hash_sha256_final(&*state, out.data()); | |
| 1627 | } | |
| 1628 | ||
| 1629 | static inline int | |
| 1630 | sn_crypto_hash_sha512( | |
| 1631 | js_env_t *, | |
| 1632 | js_receiver_t, | |
| 1633 | js_typedarray_span_t<> out, | |
| 1634 | js_typedarray_span_t<> in | |
| 1635 | ) { | |
| 1636 | assert(out.size_bytes() == crypto_hash_sha512_BYTES); | |
| 1637 | ||
| 1638 | return crypto_hash_sha512(out.data(), in.data(), in.size_bytes()); | |
| 1639 | } | |
| 1640 | ||
| 1641 | static inline int | |
| 1642 | sn_crypto_hash_sha512_init( | |
| 1643 | js_env_t *, | |
| 1644 | js_receiver_t, | |
| 1645 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state | |
| 1646 | ) { | |
| 1647 | return crypto_hash_sha512_init(&*state); | |
| 1648 | } | |
| 1649 | ||
| 1650 | static inline int | |
| 1651 | sn_crypto_hash_sha512_update( | |
| 1652 | js_env_t *, | |
| 1653 | js_receiver_t, | |
| 1654 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state, | |
| 1655 | js_typedarray_span_t<> in | |
| 1656 | ) { | |
| 1657 | return crypto_hash_sha512_update(&*state, in.data(), in.size_bytes()); | |
| 1658 | } | |
| 1659 | ||
| 1660 | static inline int | |
| 1661 | sn_crypto_hash_sha512_final( | |
| 1662 | js_env_t *, | |
| 1663 | js_receiver_t, | |
| 1664 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state, | |
| 1665 | js_typedarray_span_t<> out | |
| 1666 | ) { | |
| 1667 | assert(out.size_bytes() == crypto_hash_sha512_BYTES); | |
| 1668 | ||
| 1669 | return crypto_hash_sha512_final(&*state, out.data()); | |
| 1670 | } | |
| 1671 | ||
| 1672 | static inline void | |
| 1673 | sn_crypto_aead_xchacha20poly1305_ietf_keygen( | |
| 1674 | js_env_t *, | |
| 1675 | js_receiver_t, | |
| 1676 | js_typedarray_span_t<> k | |
| 1677 | ) { | |
| 1678 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1679 | ||
| 1680 | crypto_aead_xchacha20poly1305_ietf_keygen(k.data()); | |
| 1681 | } | |
| 1682 | ||
| 1683 | static inline uint64_t | |
| 1684 | sn_crypto_aead_xchacha20poly1305_ietf_encrypt( | |
| 1685 | js_env_t *, | |
| 1686 | js_receiver_t, | |
| 1687 | js_typedarray_span_t<> c, | |
| 1688 | js_typedarray_span_t<> m, | |
| 1689 | std::optional<js_typedarray_span_t<>> ad, | |
| 1690 | js_typedarray_span_t<> npub, | |
| 1691 | js_typedarray_span_t<> k | |
| 1692 | ) { | |
| 1693 | assert(c.size_bytes() == m.size_bytes() + crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1694 | assert(c.size_bytes() <= 0xffffffff); | |
| 1695 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1696 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1697 | ||
| 1698 | uint8_t *ad_data = nullptr; | |
| 1699 | size_t ad_size = 0; | |
| 1700 | if (ad) { | |
| 1701 | ad_data = ad->data(); | |
| 1702 | ad_size = ad->size_bytes(); | |
| 1703 | } | |
| 1704 | ||
| 1705 | unsigned long long clen = 0; | |
| 1706 | int status = crypto_aead_xchacha20poly1305_ietf_encrypt( | |
| 1707 | c.data(), | |
| 1708 | &clen, | |
| 1709 | m.data(), | |
| 1710 | m.size_bytes(), | |
| 1711 | ad_data, | |
| 1712 | ad_size, | |
| 1713 | nullptr, | |
| 1714 | npub.data(), | |
| 1715 | k.data() | |
| 1716 | ); | |
| 1717 | ||
| 1718 | if (status < 0) return status; | |
| 1719 | ||
| 1720 | return clen; | |
| 1721 | } | |
| 1722 | ||
| 1723 | static inline uint64_t | |
| 1724 | sn_crypto_aead_xchacha20poly1305_ietf_decrypt( | |
| 1725 | js_env_t *, | |
| 1726 | js_receiver_t, | |
| 1727 | js_typedarray_span_t<> m, | |
| 1728 | js_typedarray_span_t<> c, | |
| 1729 | std::optional<js_typedarray_span_t<>> ad, | |
| 1730 | js_typedarray_span_t<> npub, | |
| 1731 | js_typedarray_span_t<> k | |
| 1732 | ) { | |
| 1733 | assert(m.size_bytes() == c.size_bytes() - crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1734 | assert(m.size_bytes() <= 0xffffffff); | |
| 1735 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1736 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1737 | ||
| 1738 | uint8_t *ad_data = nullptr; | |
| 1739 | size_t ad_size = 0; | |
| 1740 | if (ad) { | |
| 1741 | ad_data = ad->data(); | |
| 1742 | ad_size = ad->size_bytes(); | |
| 1743 | } | |
| 1744 | ||
| 1745 | unsigned long long mlen = 0; | |
| 1746 | int status = crypto_aead_xchacha20poly1305_ietf_decrypt( | |
| 1747 | m.data(), | |
| 1748 | &mlen, | |
| 1749 | nullptr, | |
| 1750 | c.data(), | |
| 1751 | c.size_bytes(), | |
| 1752 | ad_data, | |
| 1753 | ad_size, | |
| 1754 | npub.data(), | |
| 1755 | k.data() | |
| 1756 | ); | |
| 1757 | ||
| 1758 | if (status < 0) return status; | |
| 1759 | ||
| 1760 | return mlen; | |
| 1761 | } | |
| 1762 | ||
| 1763 | static inline uint64_t | |
| 1764 | sn_crypto_aead_xchacha20poly1305_ietf_encrypt_detached( | |
| 1765 | js_env_t *, | |
| 1766 | js_receiver_t, | |
| 1767 | js_typedarray_span_t<> c, | |
| 1768 | js_typedarray_span_t<> mac, | |
| 1769 | js_typedarray_span_t<> m, | |
| 1770 | std::optional<js_typedarray_span_t<>> ad, | |
| 1771 | js_typedarray_span_t<> npub, | |
| 1772 | js_typedarray_span_t<> k | |
| 1773 | ) { | |
| 1774 | assert(c.size_bytes() == m.size_bytes()); | |
| 1775 | assert(mac.size_bytes() == crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1776 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1777 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1778 | ||
| 1779 | uint8_t *ad_data = nullptr; | |
| 1780 | size_t ad_size = 0; | |
| 1781 | if (ad) { | |
| 1782 | ad_data = ad->data(); | |
| 1783 | ad_size = ad->size_bytes(); | |
| 1784 | } | |
| 1785 | ||
| 1786 | unsigned long long maclen = 0; | |
| 1787 | int status = crypto_aead_xchacha20poly1305_ietf_encrypt_detached( | |
| 1788 | c.data(), | |
| 1789 | mac.data(), | |
| 1790 | &maclen, | |
| 1791 | m.data(), | |
| 1792 | m.size_bytes(), | |
| 1793 | ad_data, | |
| 1794 | ad_size, | |
| 1795 | nullptr, | |
| 1796 | npub.data(), | |
| 1797 | k.data() | |
| 1798 | ); | |
| 1799 | ||
| 1800 | if (status < 0) return status; | |
| 1801 | ||
| 1802 | return maclen; | |
| 1803 | } | |
| 1804 | ||
| 1805 | static inline int | |
| 1806 | sn_crypto_aead_xchacha20poly1305_ietf_decrypt_detached( | |
| 1807 | js_env_t *, | |
| 1808 | js_receiver_t, | |
| 1809 | js_typedarray_span_t<> m, | |
| 1810 | js_typedarray_span_t<> c, | |
| 1811 | js_typedarray_span_t<> mac, | |
| 1812 | std::optional<js_typedarray_span_t<>> ad, | |
| 1813 | js_typedarray_span_t<> npub, | |
| 1814 | js_typedarray_span_t<> k | |
| 1815 | ) { | |
| 1816 | assert(m.size_bytes() == c.size_bytes()); | |
| 1817 | assert(mac.size_bytes() == crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1818 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1819 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1820 | ||
| 1821 | uint8_t *ad_data = nullptr; | |
| 1822 | size_t ad_size = 0; | |
| 1823 | if (ad) { | |
| 1824 | ad_data = ad->data(); | |
| 1825 | ad_size = ad->size_bytes(); | |
| 1826 | } | |
| 1827 | ||
| 1828 | return crypto_aead_xchacha20poly1305_ietf_decrypt_detached( | |
| 1829 | m.data(), | |
| 1830 | nullptr, | |
| 1831 | c.data(), | |
| 1832 | c.size_bytes(), | |
| 1833 | mac.data(), | |
| 1834 | ad_data, | |
| 1835 | ad_size, | |
| 1836 | npub.data(), | |
| 1837 | k.data() | |
| 1838 | ); | |
| 1839 | } | |
| 1840 | ||
| 1841 | static inline void | |
| 1842 | sn_crypto_aead_chacha20poly1305_ietf_keygen( | |
| 1843 | js_env_t *, | |
| 1844 | js_receiver_t, | |
| 1845 | js_typedarray_span_t<> k | |
| 1846 | ) { | |
| 1847 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1848 | ||
| 1849 | crypto_aead_chacha20poly1305_ietf_keygen(k.data()); | |
| 1850 | } | |
| 1851 | ||
| 1852 | static inline uint64_t | |
| 1853 | sn_crypto_aead_chacha20poly1305_ietf_encrypt( | |
| 1854 | js_env_t *, | |
| 1855 | js_receiver_t, | |
| 1856 | js_typedarray_span_t<> c, | |
| 1857 | js_typedarray_span_t<> m, | |
| 1858 | std::optional<js_typedarray_span_t<>> ad, | |
| 1859 | js_typedarray_span_t<> npub, | |
| 1860 | js_typedarray_span_t<> k | |
| 1861 | ) { | |
| 1862 | assert(c.size_bytes() == m.size_bytes() + crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1863 | assert(c.size_bytes() <= 0xffffffff); | |
| 1864 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1865 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1866 | ||
| 1867 | uint8_t *ad_data = nullptr; | |
| 1868 | size_t ad_size = 0; | |
| 1869 | if (ad) { | |
| 1870 | ad_data = ad->data(); | |
| 1871 | ad_size = ad->size_bytes(); | |
| 1872 | } | |
| 1873 | ||
| 1874 | unsigned long long clen = 0; | |
| 1875 | int status = crypto_aead_chacha20poly1305_ietf_encrypt( | |
| 1876 | c.data(), | |
| 1877 | &clen, | |
| 1878 | m.data(), | |
| 1879 | m.size_bytes(), | |
| 1880 | ad_data, | |
| 1881 | ad_size, | |
| 1882 | nullptr, | |
| 1883 | npub.data(), | |
| 1884 | k.data() | |
| 1885 | ); | |
| 1886 | ||
| 1887 | if (status < 0) return status; | |
| 1888 | ||
| 1889 | return clen; | |
| 1890 | } | |
| 1891 | ||
| 1892 | static inline uint64_t | |
| 1893 | sn_crypto_aead_chacha20poly1305_ietf_decrypt( | |
| 1894 | js_env_t *, | |
| 1895 | js_receiver_t, | |
| 1896 | js_typedarray_span_t<> m, | |
| 1897 | js_typedarray_span_t<> c, | |
| 1898 | std::optional<js_typedarray_span_t<>> ad, | |
| 1899 | js_typedarray_span_t<> npub, | |
| 1900 | js_typedarray_span_t<> k | |
| 1901 | ) { | |
| 1902 | assert(m.size_bytes() == c.size_bytes() - crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1903 | assert(m.size_bytes() <= 0xffffffff); | |
| 1904 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1905 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1906 | ||
| 1907 | uint8_t *ad_data = nullptr; | |
| 1908 | size_t ad_size = 0; | |
| 1909 | if (ad) { | |
| 1910 | ad_data = ad->data(); | |
| 1911 | ad_size = ad->size_bytes(); | |
| 1912 | } | |
| 1913 | ||
| 1914 | unsigned long long mlen = 0; | |
| 1915 | int status = crypto_aead_chacha20poly1305_ietf_decrypt( | |
| 1916 | m.data(), | |
| 1917 | &mlen, | |
| 1918 | nullptr, | |
| 1919 | c.data(), | |
| 1920 | c.size_bytes(), | |
| 1921 | ad_data, | |
| 1922 | ad_size, | |
| 1923 | npub.data(), | |
| 1924 | k.data() | |
| 1925 | ); | |
| 1926 | ||
| 1927 | if (status < 0) return status; | |
| 1928 | ||
| 1929 | return mlen; | |
| 1930 | } | |
| 1931 | ||
| 1932 | static inline uint64_t | |
| 1933 | sn_crypto_aead_chacha20poly1305_ietf_encrypt_detached( | |
| 1934 | js_env_t *, | |
| 1935 | js_receiver_t, | |
| 1936 | js_typedarray_span_t<> c, | |
| 1937 | js_typedarray_span_t<> mac, | |
| 1938 | js_typedarray_span_t<> m, | |
| 1939 | std::optional<js_typedarray_span_t<>> ad, | |
| 1940 | js_typedarray_span_t<> npub, | |
| 1941 | js_typedarray_span_t<> k | |
| 1942 | ) { | |
| 1943 | assert(c.size_bytes() == m.size_bytes()); | |
| 1944 | assert(mac.size_bytes() == crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1945 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1946 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1947 | ||
| 1948 | uint8_t *ad_data = nullptr; | |
| 1949 | size_t ad_size = 0; | |
| 1950 | if (ad) { | |
| 1951 | ad_data = ad->data(); | |
| 1952 | ad_size = ad->size_bytes(); | |
| 1953 | } | |
| 1954 | ||
| 1955 | unsigned long long maclen = 0; | |
| 1956 | int status = crypto_aead_chacha20poly1305_ietf_encrypt_detached( | |
| 1957 | c.data(), | |
| 1958 | mac.data(), | |
| 1959 | &maclen, | |
| 1960 | m.data(), | |
| 1961 | m.size_bytes(), | |
| 1962 | ad_data, | |
| 1963 | ad_size, | |
| 1964 | nullptr, | |
| 1965 | npub.data(), | |
| 1966 | k.data() | |
| 1967 | ); | |
| 1968 | ||
| 1969 | if (status < 0) return status; | |
| 1970 | ||
| 1971 | return maclen; | |
| 1972 | } | |
| 1973 | ||
| 1974 | static inline int | |
| 1975 | sn_crypto_aead_chacha20poly1305_ietf_decrypt_detached( | |
| 1976 | js_env_t *, | |
| 1977 | js_receiver_t, | |
| 1978 | js_typedarray_span_t<> m, | |
| 1979 | js_typedarray_span_t<> c, | |
| 1980 | js_typedarray_span_t<> mac, | |
| 1981 | std::optional<js_typedarray_span_t<>> ad, | |
| 1982 | js_typedarray_span_t<> npub, | |
| 1983 | js_typedarray_span_t<> k | |
| 1984 | ) { | |
| 1985 | assert(m.size_bytes() == c.size_bytes()); | |
| 1986 | assert(mac.size_bytes() == crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1987 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1988 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1989 | ||
| 1990 | uint8_t *ad_data = nullptr; | |
| 1991 | size_t ad_size = 0; | |
| 1992 | if (ad) { | |
| 1993 | ad_data = ad->data(); | |
| 1994 | ad_size = ad->size_bytes(); | |
| 1995 | } | |
| 1996 | ||
| 1997 | return crypto_aead_chacha20poly1305_ietf_decrypt_detached( | |
| 1998 | m.data(), | |
| 1999 | nullptr, | |
| 2000 | c.data(), | |
| 2001 | c.size_bytes(), | |
| 2002 | mac.data(), | |
| 2003 | ad_data, | |
| 2004 | ad_size, | |
| 2005 | npub.data(), | |
| 2006 | k.data() | |
| 2007 | ); | |
| 2008 | } | |
| 2009 | ||
| 2010 | static inline void | |
| 2011 | sn_crypto_secretstream_xchacha20poly1305_keygen( | |
| 2012 | js_env_t *env, | |
| 2013 | js_receiver_t, | |
| 2014 | ||
| 2015 | js_arraybuffer_span_t k, | |
| 2016 | uint32_t k_offset, | |
| 2017 | uint32_t k_len | |
| 2018 | ) { | |
| 2019 | assert_bounds(k); | |
| 2020 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2021 | ||
| 2022 | crypto_secretstream_xchacha20poly1305_keygen(&k[k_offset]); | |
| 2023 | } | |
| 2024 | ||
| 2025 | static inline int | |
| 2026 | sn_crypto_secretstream_xchacha20poly1305_init_push( | |
| 2027 | js_env_t *env, | |
| 2028 | js_receiver_t, | |
| 2029 | ||
| 2030 | js_arraybuffer_span_t state, | |
| 2031 | uint32_t state_offset, | |
| 2032 | uint32_t state_len, | |
| 2033 | ||
| 2034 | js_arraybuffer_span_t header, | |
| 2035 | uint32_t header_offset, | |
| 2036 | uint32_t header_len, | |
| 2037 | ||
| 2038 | js_arraybuffer_span_t k, | |
| 2039 | uint32_t k_offset, | |
| 2040 | uint32_t k_len | |
| 2041 | ) { | |
| 2042 | assert_bounds(state); | |
| 2043 | assert_bounds(header); | |
| 2044 | assert_bounds(k); | |
| 2045 | ||
| 2046 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2047 | assert(header_len == crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 2048 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2049 | ||
| 2050 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2051 | ||
| 2052 | return crypto_secretstream_xchacha20poly1305_init_push(state_data, &header[header_offset], &k[k_offset]); | |
| 2053 | } | |
| 2054 | ||
| 2055 | static inline uint64_t | |
| 2056 | sn_crypto_secretstream_xchacha20poly1305_push( | |
| 2057 | js_env_t *env, | |
| 2058 | js_receiver_t, | |
| 2059 | ||
| 2060 | js_arraybuffer_span_t state, | |
| 2061 | uint32_t state_offset, | |
| 2062 | uint32_t state_len, | |
| 2063 | ||
| 2064 | js_arraybuffer_span_t c, | |
| 2065 | uint32_t c_offset, | |
| 2066 | uint32_t c_len, | |
| 2067 | ||
| 2068 | js_arraybuffer_span_t m, | |
| 2069 | uint32_t m_offset, | |
| 2070 | uint32_t m_len, | |
| 2071 | ||
| 2072 | js_arraybuffer_span_t ad, | |
| 2073 | uint32_t ad_offset, | |
| 2074 | uint32_t ad_len, | |
| 2075 | ||
| 2076 | uint32_t tag | |
| 2077 | ) { | |
| 2078 | assert_bounds(state); | |
| 2079 | assert_bounds(c); | |
| 2080 | assert_bounds(m); | |
| 2081 | ||
| 2082 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2083 | assert(c_len == m_len + crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2084 | assert(c_len <= 0xffffffff && "32bit integer"); | |
| 2085 | ||
| 2086 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2087 | ||
| 2088 | uint8_t *ad_data = NULL; | |
| 2089 | if (ad_len) { | |
| 2090 | assert_bounds(ad); | |
| 2091 | ad_data = &ad[ad_offset]; | |
| 2092 | } | |
| 2093 | ||
| 2094 | unsigned long long clen = 0; | |
| 2095 | int status = crypto_secretstream_xchacha20poly1305_push( | |
| 2096 | state_data, | |
| 2097 | &c[c_offset], | |
| 2098 | &clen, | |
| 2099 | &m[m_offset], | |
| 2100 | m_len, | |
| 2101 | ad_data, | |
| 2102 | ad_len, | |
| 2103 | tag | |
| 2104 | ); | |
| 2105 | ||
| 2106 | if (status < 0) return status; | |
| 2107 | ||
| 2108 | return clen; | |
| 2109 | } | |
| 2110 | ||
| 2111 | static inline int | |
| 2112 | sn_crypto_secretstream_xchacha20poly1305_init_pull( | |
| 2113 | js_env_t *, | |
| 2114 | js_receiver_t, | |
| 2115 | ||
| 2116 | js_arraybuffer_span_t state, | |
| 2117 | uint32_t state_offset, | |
| 2118 | uint32_t state_len, | |
| 2119 | ||
| 2120 | js_arraybuffer_span_t header, | |
| 2121 | uint32_t header_offset, | |
| 2122 | uint32_t header_len, | |
| 2123 | ||
| 2124 | js_arraybuffer_span_t k, | |
| 2125 | uint32_t k_offset, | |
| 2126 | uint32_t k_len | |
| 2127 | ) { | |
| 2128 | assert_bounds(state); | |
| 2129 | assert_bounds(header); | |
| 2130 | assert_bounds(k); | |
| 2131 | ||
| 2132 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2133 | assert(header_len == crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 2134 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2135 | ||
| 2136 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2137 | ||
| 2138 | return crypto_secretstream_xchacha20poly1305_init_pull(state_data, &header[header_offset], &k[k_offset]); | |
| 2139 | } | |
| 2140 | ||
| 2141 | static inline uint64_t | |
| 2142 | sn_crypto_secretstream_xchacha20poly1305_pull( | |
| 2143 | js_env_t *env, | |
| 2144 | js_receiver_t, | |
| 2145 | ||
| 2146 | js_arraybuffer_span_t state, | |
| 2147 | uint32_t state_offset, | |
| 2148 | uint32_t state_len, | |
| 2149 | ||
| 2150 | js_arraybuffer_span_t m, | |
| 2151 | uint32_t m_offset, | |
| 2152 | uint32_t m_len, | |
| 2153 | ||
| 2154 | js_arraybuffer_span_t tag, | |
| 2155 | uint32_t tag_offset, | |
| 2156 | uint32_t tag_len, | |
| 2157 | ||
| 2158 | js_arraybuffer_span_t c, | |
| 2159 | uint32_t c_offset, | |
| 2160 | uint32_t c_len, | |
| 2161 | ||
| 2162 | js_arraybuffer_span_t ad, | |
| 2163 | uint32_t ad_offset, | |
| 2164 | uint32_t ad_len | |
| 2165 | ) { | |
| 2166 | assert_bounds(state); | |
| 2167 | assert_bounds(m); | |
| 2168 | assert_bounds(tag); | |
| 2169 | assert_bounds(c); | |
| 2170 | ||
| 2171 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2172 | assert(c_len >= crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2173 | assert(tag_len == 1); | |
| 2174 | assert(m_len == c_len - crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2175 | assert(m_len <= 0xffffffff); | |
| 2176 | ||
| 2177 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2178 | ||
| 2179 | uint8_t *ad_data = NULL; | |
| 2180 | if (ad_len) { | |
| 2181 | assert_bounds(ad); | |
| 2182 | ad_data = &ad[ad_offset]; | |
| 2183 | } | |
| 2184 | ||
| 2185 | unsigned long long mlen = 0; | |
| 2186 | int status = crypto_secretstream_xchacha20poly1305_pull( | |
| 2187 | state_data, | |
| 2188 | &m[m_offset], | |
| 2189 | &mlen, | |
| 2190 | &tag[tag_offset], | |
| 2191 | &c[c_offset], | |
| 2192 | c_len, | |
| 2193 | ad_data, | |
| 2194 | ad_len | |
| 2195 | ); | |
| 2196 | ||
| 2197 | if (status < 0) return status; | |
| 2198 | ||
| 2199 | return mlen; | |
| 2200 | } | |
| 2201 | ||
| 2202 | static inline void | |
| 2203 | sn_crypto_secretstream_xchacha20poly1305_rekey( | |
| 2204 | js_env_t *, | |
| 2205 | js_receiver_t, | |
| 2206 | ||
| 2207 | js_arraybuffer_span_t state, | |
| 2208 | uint32_t state_offset, | |
| 2209 | uint32_t state_len | |
| 2210 | ) { | |
| 2211 | assert_bounds(state); | |
| 2212 | ||
| 2213 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2214 | ||
| 2215 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2216 | ||
| 2217 | crypto_secretstream_xchacha20poly1305_rekey(state_data); | |
| 2218 | } | |
| 2219 | ||
| 2220 | struct sn_async_task { | |
| 2221 | uv_work_t task; | |
| 2222 | js_env_t *env; | |
| 2223 | js_persistent_t<js_function_t<void, int>> cb; | |
| 2224 | js_deferred_teardown_t *teardown; | |
| 2225 | int code = 0; | |
| 2226 | bool exiting = false; | |
| 2227 | ||
| 2228 | sn_async_task(js_env_t *env) : env(env) { | |
| 2229 | int err; | |
| 2230 | err = js_add_teardown_callback<on_teardown, sn_async_task>(env, this, teardown); | |
| 2231 | assert(err == 0); | |
| 2232 | } | |
| 2233 | ||
| 2234 | ~sn_async_task() { | |
| 2235 | int err; | |
| 2236 | err = js_finish_teardown_callback(teardown); | |
| 2237 | assert(err == 0); | |
| 2238 | } | |
| 2239 | ||
| 2240 | private: | |
| 2241 | static void | |
| 2242 | on_teardown(js_deferred_teardown_t *teardown, sn_async_task *req) { | |
| 2243 | req->exiting = true; | |
| 2244 | } | |
| 2245 | }; | |
| 2246 | ||
| 2247 | struct sn_async_pwhash_request : sn_async_task { | |
| 2248 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2249 | std::span<uint8_t> out; | |
| 2250 | ||
| 2251 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2252 | std::span<char> pwd; | |
| 2253 | ||
| 2254 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 2255 | std::span<uint8_t> salt; | |
| 2256 | ||
| 2257 | uint64_t opslimit; | |
| 2258 | size_t memlimit; | |
| 2259 | int alg; | |
| 2260 | ||
| 2261 | sn_async_pwhash_request(js_env_t *env) : sn_async_task(env) {} | |
| 2262 | }; | |
| 2263 | ||
| 2264 | static void | |
| 2265 | async_pwhash_execute(uv_work_t *uv_req) { | |
| 2266 | auto req = reinterpret_cast<sn_async_pwhash_request *>(uv_req); | |
| 2267 | ||
| 2268 | req->code = crypto_pwhash( | |
| 2269 | req->out.data(), | |
| 2270 | req->out.size(), | |
| 2271 | req->pwd.data(), | |
| 2272 | req->pwd.size(), | |
| 2273 | req->salt.data(), | |
| 2274 | req->opslimit, | |
| 2275 | req->memlimit, | |
| 2276 | req->alg | |
| 2277 | ); | |
| 2278 | } | |
| 2279 | ||
| 2280 | static void | |
| 2281 | async_pwhash_complete(uv_work_t *uv_req, int status) { | |
| 2282 | int err; | |
| 2283 | ||
| 2284 | auto req = reinterpret_cast<sn_async_pwhash_request *>(uv_req); | |
| 2285 | ||
| 2286 | if (req->exiting) return delete req; | |
| 2287 | ||
| 2288 | js_handle_scope_t *scope; | |
| 2289 | err = js_open_handle_scope(req->env, &scope); | |
| 2290 | assert(err == 0); | |
| 2291 | ||
| 2292 | js_function_t<void, int> callback; | |
| 2293 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2294 | assert(err == 0); | |
| 2295 | ||
| 2296 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2297 | assert(err != js_pending_exception); | |
| 2298 | ||
| 2299 | err = js_close_handle_scope(req->env, scope); | |
| 2300 | assert(err == 0); | |
| 2301 | ||
| 2302 | delete req; | |
| 2303 | } | |
| 2304 | ||
| 2305 | static inline void | |
| 2306 | sn_crypto_pwhash_async( | |
| 2307 | js_env_t *env, | |
| 2308 | js_receiver_t, | |
| 2309 | ||
| 2310 | js_arraybuffer_t out, | |
| 2311 | uint32_t out_offset, | |
| 2312 | uint32_t out_len, | |
| 2313 | ||
| 2314 | js_arraybuffer_t pwd, | |
| 2315 | uint32_t pwd_offset, | |
| 2316 | uint32_t pwd_len, | |
| 2317 | ||
| 2318 | js_arraybuffer_t salt, | |
| 2319 | uint32_t salt_offset, | |
| 2320 | uint32_t salt_len, | |
| 2321 | ||
| 2322 | uint64_t opslimit, | |
| 2323 | uint64_t memlimit, | |
| 2324 | uint32_t alg, | |
| 2325 | ||
| 2326 | js_function_t<void, int> callback | |
| 2327 | ) { | |
| 2328 | int err; | |
| 2329 | ||
| 2330 | auto *req = new sn_async_pwhash_request(env); | |
| 2331 | ||
| 2332 | std::span<uint8_t> out_view; | |
| 2333 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2334 | assert(err == 0); | |
| 2335 | assert(out_offset + out_len <= out_view.size()); | |
| 2336 | ||
| 2337 | req->out = {&out_view[out_offset], out_len}; | |
| 2338 | ||
| 2339 | std::span<char> pwd_view; | |
| 2340 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2341 | assert(err == 0); | |
| 2342 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2343 | ||
| 2344 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2345 | ||
| 2346 | std::span<uint8_t> salt_view; | |
| 2347 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 2348 | assert(err == 0); | |
| 2349 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 2350 | ||
| 2351 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 2352 | ||
| 2353 | req->opslimit = opslimit; | |
| 2354 | req->memlimit = memlimit; | |
| 2355 | req->alg = alg; | |
| 2356 | ||
| 2357 | err = js_create_reference(env, out, req->out_ref); | |
| 2358 | assert(err == 0); | |
| 2359 | ||
| 2360 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2361 | assert(err == 0); | |
| 2362 | ||
| 2363 | err = js_create_reference(env, salt, req->salt_ref); | |
| 2364 | assert(err == 0); | |
| 2365 | ||
| 2366 | err = js_create_reference(env, callback, req->cb); | |
| 2367 | assert(err == 0); | |
| 2368 | ||
| 2369 | uv_loop_t *loop; | |
| 2370 | err = js_get_env_loop(env, &loop); | |
| 2371 | assert(err == 0); | |
| 2372 | ||
| 2373 | err = uv_queue_work(loop, &req->task, async_pwhash_execute, async_pwhash_complete); | |
| 2374 | assert(err == 0); | |
| 2375 | } | |
| 2376 | ||
| 2377 | struct sn_async_pwhash_str_request : sn_async_task { | |
| 2378 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2379 | std::span<char> out; | |
| 2380 | ||
| 2381 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2382 | std::span<char> pwd; | |
| 2383 | ||
| 2384 | uint64_t opslimit; | |
| 2385 | size_t memlimit; | |
| 2386 | ||
| 2387 | sn_async_pwhash_str_request(js_env_t *env) : sn_async_task(env) {} | |
| 2388 | }; | |
| 2389 | ||
| 2390 | static void | |
| 2391 | async_pwhash_str_execute(uv_work_t *uv_req) { | |
| 2392 | auto *req = reinterpret_cast<sn_async_pwhash_str_request *>(uv_req); | |
| 2393 | ||
| 2394 | req->code = crypto_pwhash_str( | |
| 2395 | req->out.data(), | |
| 2396 | req->pwd.data(), | |
| 2397 | req->pwd.size(), | |
| 2398 | req->opslimit, | |
| 2399 | req->memlimit | |
| 2400 | ); | |
| 2401 | } | |
| 2402 | ||
| 2403 | static void | |
| 2404 | async_pwhash_str_complete(uv_work_t *uv_req, int status) { | |
| 2405 | int err; | |
| 2406 | ||
| 2407 | auto *req = reinterpret_cast<sn_async_pwhash_str_request *>(uv_req); | |
| 2408 | ||
| 2409 | if (req->exiting) return delete req; | |
| 2410 | ||
| 2411 | js_handle_scope_t *scope; | |
| 2412 | err = js_open_handle_scope(req->env, &scope); | |
| 2413 | assert(err == 0); | |
| 2414 | ||
| 2415 | js_function_t<void, int> callback; | |
| 2416 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2417 | assert(err == 0); | |
| 2418 | ||
| 2419 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2420 | assert(err != js_pending_exception); | |
| 2421 | ||
| 2422 | err = js_close_handle_scope(req->env, scope); | |
| 2423 | assert(err == 0); | |
| 2424 | ||
| 2425 | delete req; | |
| 2426 | } | |
| 2427 | ||
| 2428 | static inline void | |
| 2429 | sn_crypto_pwhash_str_async( | |
| 2430 | js_env_t *env, | |
| 2431 | js_receiver_t, | |
| 2432 | ||
| 2433 | js_arraybuffer_t out, | |
| 2434 | uint32_t out_offset, | |
| 2435 | uint32_t out_len, | |
| 2436 | ||
| 2437 | js_arraybuffer_t pwd, | |
| 2438 | uint32_t pwd_offset, | |
| 2439 | uint32_t pwd_len, | |
| 2440 | ||
| 2441 | uint64_t opslimit, | |
| 2442 | uint64_t memlimit, | |
| 2443 | js_function_t<void, int> callback | |
| 2444 | ) { | |
| 2445 | int err; | |
| 2446 | ||
| 2447 | auto *req = new sn_async_pwhash_str_request(env); | |
| 2448 | ||
| 2449 | std::span<char> out_view; | |
| 2450 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2451 | assert(err == 0); | |
| 2452 | assert(out_offset + out_len <= out_view.size()); | |
| 2453 | ||
| 2454 | req->out = {&out_view[out_offset], out_len}; | |
| 2455 | ||
| 2456 | std::span<char> pwd_view; | |
| 2457 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2458 | assert(err == 0); | |
| 2459 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2460 | ||
| 2461 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2462 | ||
| 2463 | req->opslimit = opslimit; | |
| 2464 | req->memlimit = memlimit; | |
| 2465 | ||
| 2466 | err = js_create_reference(env, out, req->out_ref); | |
| 2467 | assert(err == 0); | |
| 2468 | ||
| 2469 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2470 | assert(err == 0); | |
| 2471 | ||
| 2472 | err = js_create_reference(env, callback, req->cb); | |
| 2473 | assert(err == 0); | |
| 2474 | ||
| 2475 | uv_loop_t *loop; | |
| 2476 | err = js_get_env_loop(env, &loop); | |
| 2477 | assert(err == 0); | |
| 2478 | ||
| 2479 | err = uv_queue_work(loop, &req->task, async_pwhash_str_execute, async_pwhash_str_complete); | |
| 2480 | assert(err == 0); | |
| 2481 | } | |
| 2482 | ||
| 2483 | struct sn_async_pwhash_str_verify_request : sn_async_task { | |
| 2484 | js_persistent_t<js_arraybuffer_t> str_ref; | |
| 2485 | std::span<char> str; | |
| 2486 | ||
| 2487 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2488 | std::span<char> pwd; | |
| 2489 | ||
| 2490 | sn_async_pwhash_str_verify_request(js_env_t *env) : sn_async_task(env) {} | |
| 2491 | }; | |
| 2492 | ||
| 2493 | static void | |
| 2494 | async_pwhash_str_verify_execute(uv_work_t *uv_req) { | |
| 2495 | auto *req = reinterpret_cast<sn_async_pwhash_str_verify_request *>(uv_req); | |
| 2496 | ||
| 2497 | req->code = crypto_pwhash_str_verify(req->str.data(), req->pwd.data(), req->pwd.size()); | |
| 2498 | } | |
| 2499 | ||
| 2500 | static void | |
| 2501 | async_pwhash_str_verify_complete(uv_work_t *uv_req, int status) { | |
| 2502 | int err; | |
| 2503 | ||
| 2504 | auto *req = reinterpret_cast<sn_async_pwhash_str_verify_request *>(uv_req); | |
| 2505 | ||
| 2506 | if (req->exiting) return delete req; | |
| 2507 | ||
| 2508 | js_handle_scope_t *scope; | |
| 2509 | err = js_open_handle_scope(req->env, &scope); | |
| 2510 | assert(err == 0); | |
| 2511 | ||
| 2512 | js_function_t<void, int> callback; | |
| 2513 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2514 | assert(err == 0); | |
| 2515 | ||
| 2516 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2517 | assert(err != js_pending_exception); | |
| 2518 | ||
| 2519 | err = js_close_handle_scope(req->env, scope); | |
| 2520 | assert(err == 0); | |
| 2521 | ||
| 2522 | delete req; | |
| 2523 | } | |
| 2524 | ||
| 2525 | static inline void | |
| 2526 | sn_crypto_pwhash_str_verify_async( | |
| 2527 | js_env_t *env, | |
| 2528 | js_receiver_t, | |
| 2529 | ||
| 2530 | js_arraybuffer_t str, | |
| 2531 | uint32_t str_offset, | |
| 2532 | uint32_t str_len, | |
| 2533 | ||
| 2534 | js_arraybuffer_t pwd, | |
| 2535 | uint32_t pwd_offset, | |
| 2536 | uint32_t pwd_len, | |
| 2537 | ||
| 2538 | js_function_t<void, int> callback | |
| 2539 | ) { | |
| 2540 | int err; | |
| 2541 | ||
| 2542 | auto *req = new sn_async_pwhash_str_verify_request(env); | |
| 2543 | ||
| 2544 | std::span<char> str_view; | |
| 2545 | err = js_get_arraybuffer_info(env, str, str_view); | |
| 2546 | assert(err == 0); | |
| 2547 | assert(str_offset + str_len <= str_view.size()); | |
| 2548 | ||
| 2549 | req->str = {&str_view[str_offset], str_len}; | |
| 2550 | ||
| 2551 | std::span<char> pwd_view; | |
| 2552 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2553 | assert(err == 0); | |
| 2554 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2555 | ||
| 2556 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2557 | ||
| 2558 | err = js_create_reference(env, str, req->str_ref); | |
| 2559 | assert(err == 0); | |
| 2560 | ||
| 2561 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2562 | assert(err == 0); | |
| 2563 | ||
| 2564 | err = js_create_reference(env, callback, req->cb); | |
| 2565 | assert(err == 0); | |
| 2566 | ||
| 2567 | uv_loop_t *loop; | |
| 2568 | err = js_get_env_loop(env, &loop); | |
| 2569 | assert(err == 0); | |
| 2570 | ||
| 2571 | err = uv_queue_work(loop, &req->task, async_pwhash_str_verify_execute, async_pwhash_str_verify_complete); | |
| 2572 | assert(err == 0); | |
| 2573 | } | |
| 2574 | ||
| 2575 | struct sn_async_pwhash_scryptsalsa208sha256_request : sn_async_task { | |
| 2576 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2577 | std::span<uint8_t> out; | |
| 2578 | ||
| 2579 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2580 | std::span<char> pwd; | |
| 2581 | ||
| 2582 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 2583 | std::span<uint8_t> salt; | |
| 2584 | ||
| 2585 | uint64_t opslimit; | |
| 2586 | size_t memlimit; | |
| 2587 | ||
| 2588 | sn_async_pwhash_scryptsalsa208sha256_request(js_env_t *env) : sn_async_task(env) {} | |
| 2589 | }; | |
| 2590 | ||
| 2591 | static void | |
| 2592 | async_pwhash_scryptsalsa208sha256_execute(uv_work_t *uv_req) { | |
| 2593 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_request *>(uv_req); | |
| 2594 | ||
| 2595 | req->code = crypto_pwhash_scryptsalsa208sha256( | |
| 2596 | req->out.data(), | |
| 2597 | req->out.size(), | |
| 2598 | req->pwd.data(), | |
| 2599 | req->pwd.size(), | |
| 2600 | req->salt.data(), | |
| 2601 | req->opslimit, | |
| 2602 | req->memlimit | |
| 2603 | ); | |
| 2604 | } | |
| 2605 | ||
| 2606 | static void | |
| 2607 | async_pwhash_scryptsalsa208sha256_complete(uv_work_t *uv_req, int status) { | |
| 2608 | int err; | |
| 2609 | ||
| 2610 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_request *>(uv_req); | |
| 2611 | ||
| 2612 | if (req->exiting) return delete req; | |
| 2613 | ||
| 2614 | js_handle_scope_t *scope; | |
| 2615 | err = js_open_handle_scope(req->env, &scope); | |
| 2616 | assert(err == 0); | |
| 2617 | ||
| 2618 | js_function_t<void, int> callback; | |
| 2619 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2620 | assert(err == 0); | |
| 2621 | ||
| 2622 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2623 | assert(err != js_pending_exception); | |
| 2624 | ||
| 2625 | err = js_close_handle_scope(req->env, scope); | |
| 2626 | assert(err == 0); | |
| 2627 | ||
| 2628 | delete req; | |
| 2629 | } | |
| 2630 | ||
| 2631 | static inline void | |
| 2632 | sn_crypto_pwhash_scryptsalsa208sha256_async( | |
| 2633 | js_env_t *env, | |
| 2634 | js_receiver_t, | |
| 2635 | ||
| 2636 | js_arraybuffer_t out, | |
| 2637 | uint32_t out_offset, | |
| 2638 | uint32_t out_len, | |
| 2639 | ||
| 2640 | js_arraybuffer_t pwd, | |
| 2641 | uint32_t pwd_offset, | |
| 2642 | uint32_t pwd_len, | |
| 2643 | ||
| 2644 | js_arraybuffer_t salt, | |
| 2645 | uint32_t salt_offset, | |
| 2646 | uint32_t salt_len, | |
| 2647 | ||
| 2648 | uint64_t opslimit, | |
| 2649 | uint64_t memlimit, | |
| 2650 | ||
| 2651 | js_function_t<void, int> callback | |
| 2652 | ) { | |
| 2653 | int err; | |
| 2654 | ||
| 2655 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_request(env); | |
| 2656 | ||
| 2657 | std::span<uint8_t> out_view; | |
| 2658 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2659 | assert(err == 0); | |
| 2660 | assert(out_offset + out_len <= out_view.size()); | |
| 2661 | ||
| 2662 | req->out = {&out_view[out_offset], out_len}; | |
| 2663 | ||
| 2664 | std::span<char> pwd_view; | |
| 2665 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2666 | assert(err == 0); | |
| 2667 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2668 | ||
| 2669 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2670 | ||
| 2671 | std::span<uint8_t> salt_view; | |
| 2672 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 2673 | assert(err == 0); | |
| 2674 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 2675 | assert(salt_len == crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 2676 | ||
| 2677 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 2678 | ||
| 2679 | req->opslimit = opslimit; | |
| 2680 | req->memlimit = memlimit; | |
| 2681 | ||
| 2682 | err = js_create_reference(env, out, req->out_ref); | |
| 2683 | assert(err == 0); | |
| 2684 | ||
| 2685 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2686 | assert(err == 0); | |
| 2687 | ||
| 2688 | err = js_create_reference(env, salt, req->salt_ref); | |
| 2689 | assert(err == 0); | |
| 2690 | ||
| 2691 | err = js_create_reference(env, callback, req->cb); | |
| 2692 | assert(err == 0); | |
| 2693 | ||
| 2694 | uv_loop_t *loop; | |
| 2695 | err = js_get_env_loop(env, &loop); | |
| 2696 | assert(err == 0); | |
| 2697 | ||
| 2698 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_execute, async_pwhash_scryptsalsa208sha256_complete); | |
| 2699 | assert(err == 0); | |
| 2700 | } | |
| 2701 | ||
| 2702 | struct sn_async_pwhash_scryptsalsa208sha256_str_request : sn_async_task { | |
| 2703 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2704 | std::span<char> out; | |
| 2705 | ||
| 2706 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2707 | std::span<char> pwd; | |
| 2708 | ||
| 2709 | uint64_t opslimit; | |
| 2710 | size_t memlimit; | |
| 2711 | ||
| 2712 | sn_async_pwhash_scryptsalsa208sha256_str_request(js_env_t *env) : sn_async_task(env) {} | |
| 2713 | }; | |
| 2714 | ||
| 2715 | static void | |
| 2716 | async_pwhash_scryptsalsa208sha256_str_execute(uv_work_t *uv_req) { | |
| 2717 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_request *>(uv_req); | |
| 2718 | ||
| 2719 | req->code = crypto_pwhash_scryptsalsa208sha256_str( | |
| 2720 | req->out.data(), | |
| 2721 | req->pwd.data(), | |
| 2722 | req->pwd.size(), | |
| 2723 | req->opslimit, | |
| 2724 | req->memlimit | |
| 2725 | ); | |
| 2726 | } | |
| 2727 | ||
| 2728 | static void | |
| 2729 | async_pwhash_scryptsalsa208sha256_str_complete(uv_work_t *uv_req, int status) { | |
| 2730 | int err; | |
| 2731 | ||
| 2732 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_request *>(uv_req); | |
| 2733 | ||
| 2734 | if (req->exiting) return delete req; | |
| 2735 | ||
| 2736 | js_handle_scope_t *scope; | |
| 2737 | err = js_open_handle_scope(req->env, &scope); | |
| 2738 | assert(err == 0); | |
| 2739 | ||
| 2740 | js_function_t<void, int> callback; | |
| 2741 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2742 | assert(err == 0); | |
| 2743 | ||
| 2744 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2745 | assert(err != js_pending_exception); | |
| 2746 | ||
| 2747 | err = js_close_handle_scope(req->env, scope); | |
| 2748 | assert(err == 0); | |
| 2749 | ||
| 2750 | delete req; | |
| 2751 | } | |
| 2752 | ||
| 2753 | static inline void | |
| 2754 | sn_crypto_pwhash_scryptsalsa208sha256_str_async( | |
| 2755 | js_env_t *env, | |
| 2756 | js_receiver_t, | |
| 2757 | ||
| 2758 | js_arraybuffer_t out, | |
| 2759 | uint32_t out_offset, | |
| 2760 | uint32_t out_len, | |
| 2761 | ||
| 2762 | js_arraybuffer_t pwd, | |
| 2763 | uint32_t pwd_offset, | |
| 2764 | uint32_t pwd_len, | |
| 2765 | ||
| 2766 | uint64_t opslimit, | |
| 2767 | uint64_t memlimit, | |
| 2768 | ||
| 2769 | js_function_t<void, int> callback | |
| 2770 | ) { | |
| 2771 | int err; | |
| 2772 | ||
| 2773 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_str_request(env); | |
| 2774 | ||
| 2775 | std::span<char> out_view; | |
| 2776 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2777 | assert(err == 0); | |
| 2778 | assert(out_offset + out_len <= out_view.size()); | |
| 2779 | ||
| 2780 | req->out = {&out_view[out_offset], out_len}; | |
| 2781 | ||
| 2782 | std::span<char> pwd_view; | |
| 2783 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2784 | assert(err == 0); | |
| 2785 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2786 | ||
| 2787 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2788 | ||
| 2789 | req->opslimit = opslimit; | |
| 2790 | req->memlimit = memlimit; | |
| 2791 | ||
| 2792 | err = js_create_reference(env, out, req->out_ref); | |
| 2793 | assert(err == 0); | |
| 2794 | ||
| 2795 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2796 | assert(err == 0); | |
| 2797 | ||
| 2798 | err = js_create_reference(env, callback, req->cb); | |
| 2799 | assert(err == 0); | |
| 2800 | ||
| 2801 | uv_loop_t *loop; | |
| 2802 | err = js_get_env_loop(env, &loop); | |
| 2803 | assert(err == 0); | |
| 2804 | ||
| 2805 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_str_execute, async_pwhash_scryptsalsa208sha256_str_complete); | |
| 2806 | assert(err == 0); | |
| 2807 | } | |
| 2808 | ||
| 2809 | struct sn_async_pwhash_scryptsalsa208sha256_str_verify_request : sn_async_task { | |
| 2810 | js_persistent_t<js_arraybuffer_t> str_ref; | |
| 2811 | std::span<char> str; | |
| 2812 | ||
| 2813 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2814 | std::span<char> pwd; | |
| 2815 | ||
| 2816 | sn_async_pwhash_scryptsalsa208sha256_str_verify_request(js_env_t *env) : sn_async_task(env) {} | |
| 2817 | }; | |
| 2818 | ||
| 2819 | static void | |
| 2820 | async_pwhash_scryptsalsa208sha256_str_verify_execute(uv_work_t *uv_req) { | |
| 2821 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_verify_request *>(uv_req); | |
| 2822 | ||
| 2823 | req->code = crypto_pwhash_scryptsalsa208sha256_str_verify(req->str.data(), req->pwd.data(), req->pwd.size()); | |
| 2824 | } | |
| 2825 | ||
| 2826 | static void | |
| 2827 | async_pwhash_scryptsalsa208sha256_str_verify_complete(uv_work_t *uv_req, int status) { | |
| 2828 | int err; | |
| 2829 | ||
| 2830 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_verify_request *>(uv_req); | |
| 2831 | ||
| 2832 | if (req->exiting) return delete req; | |
| 2833 | ||
| 2834 | js_handle_scope_t *scope; | |
| 2835 | err = js_open_handle_scope(req->env, &scope); | |
| 2836 | assert(err == 0); | |
| 2837 | ||
| 2838 | js_function_t<void, int> callback; | |
| 2839 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2840 | assert(err == 0); | |
| 2841 | ||
| 2842 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2843 | assert(err != js_pending_exception); | |
| 2844 | ||
| 2845 | err = js_close_handle_scope(req->env, scope); | |
| 2846 | assert(err == 0); | |
| 2847 | ||
| 2848 | delete req; | |
| 2849 | } | |
| 2850 | ||
| 2851 | static inline void | |
| 2852 | sn_crypto_pwhash_scryptsalsa208sha256_str_verify_async( | |
| 2853 | js_env_t *env, | |
| 2854 | js_receiver_t, | |
| 2855 | ||
| 2856 | js_arraybuffer_t str, | |
| 2857 | uint32_t str_offset, | |
| 2858 | uint32_t str_len, | |
| 2859 | ||
| 2860 | js_arraybuffer_t pwd, | |
| 2861 | uint32_t pwd_offset, | |
| 2862 | uint32_t pwd_len, | |
| 2863 | ||
| 2864 | js_function_t<void, int> callback | |
| 2865 | ) { | |
| 2866 | int err; | |
| 2867 | ||
| 2868 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_str_verify_request(env); | |
| 2869 | ||
| 2870 | std::span<char> str_view; | |
| 2871 | err = js_get_arraybuffer_info(env, str, str_view); | |
| 2872 | assert(err == 0); | |
| 2873 | assert(str_offset + str_len <= str_view.size()); | |
| 2874 | ||
| 2875 | req->str = {&str_view[str_offset], str_len}; | |
| 2876 | ||
| 2877 | std::span<char> pwd_view; | |
| 2878 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2879 | assert(err == 0); | |
| 2880 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2881 | ||
| 2882 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2883 | ||
| 2884 | err = js_create_reference(env, str, req->str_ref); | |
| 2885 | assert(err == 0); | |
| 2886 | ||
| 2887 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2888 | assert(err == 0); | |
| 2889 | ||
| 2890 | err = js_create_reference(env, callback, req->cb); | |
| 2891 | assert(err == 0); | |
| 2892 | ||
| 2893 | uv_loop_t *loop; | |
| 2894 | err = js_get_env_loop(env, &loop); | |
| 2895 | assert(err == 0); | |
| 2896 | ||
| 2897 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_str_verify_execute, async_pwhash_scryptsalsa208sha256_str_verify_complete); | |
| 2898 | assert(err == 0); | |
| 2899 | } | |
| 2900 | ||
| 2901 | struct sn_crypto_stream_xor_state { | |
| 2902 | unsigned char n[crypto_stream_NONCEBYTES]; | |
| 2903 | unsigned char k[crypto_stream_KEYBYTES]; | |
| 2904 | unsigned char next_block[64]; | |
| 2905 | int remainder; | |
| 2906 | uint64_t block_counter; | |
| 2907 | }; | |
| 2908 | ||
| 2909 | static inline void | |
| 2910 | sn_crypto_stream_xor_wrap_init( | |
| 2911 | js_env_t *, | |
| 2912 | js_receiver_t, | |
| 2913 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state, | |
| 2914 | js_typedarray_span_t<> n, | |
| 2915 | js_typedarray_span_t<> k | |
| 2916 | ) { | |
| 2917 | assert(n.size_bytes() == crypto_stream_NONCEBYTES); | |
| 2918 | assert(k.size_bytes() == crypto_stream_KEYBYTES); | |
| 2919 | ||
| 2920 | state->remainder = 0; | |
| 2921 | state->block_counter = 0; | |
| 2922 | memcpy(state->n, n.data(), crypto_stream_NONCEBYTES); | |
| 2923 | memcpy(state->k, k.data(), crypto_stream_KEYBYTES); | |
| 2924 | } | |
| 2925 | ||
| 2926 | static inline void | |
| 2927 | sn_crypto_stream_xor_wrap_update( | |
| 2928 | js_env_t *, | |
| 2929 | js_receiver_t, | |
| 2930 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state, | |
| 2931 | js_typedarray_span_t<> c, | |
| 2932 | js_typedarray_span_t<> m | |
| 2933 | ) { | |
| 2934 | assert(c.size_bytes() == m.size_bytes()); | |
| 2935 | ||
| 2936 | auto next_block = state->next_block; | |
| 2937 | ||
| 2938 | size_t m_size = m.size_bytes(); | |
| 2939 | auto *c_ptr = c.data(); | |
| 2940 | auto *m_ptr = m.data(); | |
| 2941 | ||
| 2942 | if (state->remainder) { | |
| 2943 | uint64_t offset = 0; | |
| 2944 | int rem = state->remainder; | |
| 2945 | ||
| 2946 | while (rem < 64 && offset < m_size) { | |
| 2947 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 2948 | ++offset; | |
| 2949 | ++rem; | |
| 2950 | } | |
| 2951 | ||
| 2952 | c_ptr += offset; | |
| 2953 | m_ptr += offset; | |
| 2954 | m_size -= offset; | |
| 2955 | state->remainder = (rem == 64) ? 0 : rem; | |
| 2956 | ||
| 2957 | if (m_size == 0) return; | |
| 2958 | } | |
| 2959 | ||
| 2960 | state->remainder = m_size & 63; | |
| 2961 | size_t main_len = m_size - state->remainder; | |
| 2962 | ||
| 2963 | crypto_stream_xsalsa20_xor_ic(c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k); | |
| 2964 | state->block_counter += main_len / 64; | |
| 2965 | ||
| 2966 | if (state->remainder) { | |
| 2967 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 2968 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 2969 | ||
| 2970 | crypto_stream_xsalsa20_xor_ic( | |
| 2971 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 2972 | ); | |
| 2973 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 2974 | ||
| 2975 | state->block_counter++; | |
| 2976 | } | |
| 2977 | } | |
| 2978 | ||
| 2979 | static inline void | |
| 2980 | sn_crypto_stream_xor_wrap_final( | |
| 2981 | js_env_t *, | |
| 2982 | js_receiver_t, | |
| 2983 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state | |
| 2984 | ) { | |
| 2985 | sodium_memzero(state->n, sizeof(state->n)); | |
| 2986 | sodium_memzero(state->k, sizeof(state->k)); | |
| 2987 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 2988 | state->remainder = 0; | |
| 2989 | } | |
| 2990 | ||
| 2991 | struct sn_crypto_stream_chacha20_xor_state { | |
| 2992 | unsigned char n[crypto_stream_chacha20_NONCEBYTES]; | |
| 2993 | unsigned char k[crypto_stream_chacha20_KEYBYTES]; | |
| 2994 | unsigned char next_block[64]; | |
| 2995 | int remainder; | |
| 2996 | uint64_t block_counter; | |
| 2997 | }; | |
| 2998 | ||
| 2999 | static inline void | |
| 3000 | sn_crypto_stream_chacha20_xor_wrap_init( | |
| 3001 | js_env_t *, | |
| 3002 | js_receiver_t, | |
| 3003 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state, | |
| 3004 | js_typedarray_span_t<> n, | |
| 3005 | js_typedarray_span_t<> k | |
| 3006 | ) { | |
| 3007 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 3008 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 3009 | ||
| 3010 | state->remainder = 0; | |
| 3011 | state->block_counter = 0; | |
| 3012 | memcpy(state->n, n.data(), crypto_stream_chacha20_NONCEBYTES); | |
| 3013 | memcpy(state->k, k.data(), crypto_stream_chacha20_KEYBYTES); | |
| 3014 | } | |
| 3015 | ||
| 3016 | static inline void | |
| 3017 | sn_crypto_stream_chacha20_xor_wrap_update( | |
| 3018 | js_env_t *, | |
| 3019 | js_receiver_t, | |
| 3020 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state, | |
| 3021 | js_typedarray_span_t<> c, | |
| 3022 | js_typedarray_span_t<> m | |
| 3023 | ) { | |
| 3024 | assert(c.size_bytes() == m.size_bytes()); | |
| 3025 | ||
| 3026 | auto *next_block = state->next_block; | |
| 3027 | ||
| 3028 | size_t m_size = m.size_bytes(); | |
| 3029 | auto *c_ptr = c.data(); | |
| 3030 | auto *m_ptr = m.data(); | |
| 3031 | ||
| 3032 | if (state->remainder) { | |
| 3033 | uint64_t offset = 0; | |
| 3034 | int rem = state->remainder; | |
| 3035 | ||
| 3036 | while (rem < 64 && offset < m_size) { | |
| 3037 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3038 | offset++; | |
| 3039 | rem++; | |
| 3040 | } | |
| 3041 | ||
| 3042 | c_ptr += offset; | |
| 3043 | m_ptr += offset; | |
| 3044 | m_size -= offset; | |
| 3045 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3046 | ||
| 3047 | if (m_size == 0) return; | |
| 3048 | } | |
| 3049 | ||
| 3050 | state->remainder = m_size & 63; | |
| 3051 | size_t main_len = m_size - state->remainder; | |
| 3052 | ||
| 3053 | crypto_stream_chacha20_xor_ic( | |
| 3054 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3055 | ); | |
| 3056 | ||
| 3057 | state->block_counter += main_len / 64; | |
| 3058 | ||
| 3059 | if (state->remainder) { | |
| 3060 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3061 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3062 | ||
| 3063 | crypto_stream_chacha20_xor_ic( | |
| 3064 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3065 | ); | |
| 3066 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3067 | ||
| 3068 | state->block_counter++; | |
| 3069 | } | |
| 3070 | } | |
| 3071 | ||
| 3072 | static inline void | |
| 3073 | sn_crypto_stream_chacha20_xor_wrap_final( | |
| 3074 | js_env_t *, | |
| 3075 | js_receiver_t, | |
| 3076 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state | |
| 3077 | ) { | |
| 3078 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3079 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3080 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3081 | state->remainder = 0; | |
| 3082 | } | |
| 3083 | ||
| 3084 | struct sn_crypto_stream_chacha20_ietf_xor_state { | |
| 3085 | unsigned char n[crypto_stream_chacha20_ietf_NONCEBYTES]; | |
| 3086 | unsigned char k[crypto_stream_chacha20_ietf_KEYBYTES]; | |
| 3087 | unsigned char next_block[64]; | |
| 3088 | int remainder; | |
| 3089 | uint64_t block_counter; | |
| 3090 | }; | |
| 3091 | ||
| 3092 | static inline void | |
| 3093 | sn_crypto_stream_chacha20_ietf_xor_wrap_init( | |
| 3094 | js_env_t *, | |
| 3095 | js_receiver_t, | |
| 3096 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state, | |
| 3097 | js_typedarray_span_t<> n, | |
| 3098 | js_typedarray_span_t<> k | |
| 3099 | ) { | |
| 3100 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 3101 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 3102 | ||
| 3103 | state->remainder = 0; | |
| 3104 | state->block_counter = 0; | |
| 3105 | memcpy(state->n, n.data(), crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 3106 | memcpy(state->k, k.data(), crypto_stream_chacha20_ietf_KEYBYTES); | |
| 3107 | } | |
| 3108 | ||
| 3109 | static inline void | |
| 3110 | sn_crypto_stream_chacha20_ietf_xor_wrap_update( | |
| 3111 | js_env_t *, | |
| 3112 | js_receiver_t, | |
| 3113 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state, | |
| 3114 | js_typedarray_span_t<> c, | |
| 3115 | js_typedarray_span_t<> m | |
| 3116 | ) { | |
| 3117 | assert(c.size_bytes() == m.size_bytes()); | |
| 3118 | ||
| 3119 | auto *next_block = state->next_block; | |
| 3120 | ||
| 3121 | size_t m_size = m.size_bytes(); | |
| 3122 | auto *c_ptr = c.data(); | |
| 3123 | auto *m_ptr = m.data(); | |
| 3124 | ||
| 3125 | if (state->remainder) { | |
| 3126 | uint64_t offset = 0; | |
| 3127 | int rem = state->remainder; | |
| 3128 | ||
| 3129 | while (rem < 64 && offset < m_size) { | |
| 3130 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3131 | offset++; | |
| 3132 | rem++; | |
| 3133 | } | |
| 3134 | ||
| 3135 | c_ptr += offset; | |
| 3136 | m_ptr += offset; | |
| 3137 | m_size -= offset; | |
| 3138 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3139 | ||
| 3140 | if (m_size == 0) return; | |
| 3141 | } | |
| 3142 | ||
| 3143 | state->remainder = m_size & 63; | |
| 3144 | size_t main_len = m_size - state->remainder; | |
| 3145 | ||
| 3146 | crypto_stream_chacha20_ietf_xor_ic( | |
| 3147 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3148 | ); | |
| 3149 | ||
| 3150 | state->block_counter += main_len / 64; | |
| 3151 | ||
| 3152 | if (state->remainder) { | |
| 3153 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3154 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3155 | ||
| 3156 | crypto_stream_chacha20_ietf_xor_ic( | |
| 3157 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3158 | ); | |
| 3159 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3160 | ||
| 3161 | state->block_counter++; | |
| 3162 | } | |
| 3163 | } | |
| 3164 | ||
| 3165 | static inline void | |
| 3166 | sn_crypto_stream_chacha20_ietf_xor_wrap_final( | |
| 3167 | js_env_t *, | |
| 3168 | js_receiver_t, | |
| 3169 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state | |
| 3170 | ) { | |
| 3171 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3172 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3173 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3174 | state->remainder = 0; | |
| 3175 | } | |
| 3176 | ||
| 3177 | struct sn_crypto_stream_xchacha20_xor_state { | |
| 3178 | unsigned char n[crypto_stream_xchacha20_NONCEBYTES]; | |
| 3179 | unsigned char k[crypto_stream_xchacha20_KEYBYTES]; | |
| 3180 | unsigned char next_block[64]; | |
| 3181 | int remainder; | |
| 3182 | uint64_t block_counter; | |
| 3183 | }; | |
| 3184 | ||
| 3185 | static inline void | |
| 3186 | sn_crypto_stream_xchacha20_xor_wrap_init( | |
| 3187 | js_env_t *, | |
| 3188 | js_receiver_t, | |
| 3189 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state, | |
| 3190 | js_typedarray_span_t<> n, | |
| 3191 | js_typedarray_span_t<> k | |
| 3192 | ) { | |
| 3193 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 3194 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 3195 | ||
| 3196 | state->remainder = 0; | |
| 3197 | state->block_counter = 0; | |
| 3198 | memcpy(state->n, n.data(), crypto_stream_xchacha20_NONCEBYTES); | |
| 3199 | memcpy(state->k, k.data(), crypto_stream_xchacha20_KEYBYTES); | |
| 3200 | } | |
| 3201 | ||
| 3202 | static inline void | |
| 3203 | sn_crypto_stream_xchacha20_xor_wrap_update( | |
| 3204 | js_env_t *, | |
| 3205 | js_receiver_t, | |
| 3206 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state, | |
| 3207 | js_typedarray_span_t<> c, | |
| 3208 | js_typedarray_span_t<> m | |
| 3209 | ) { | |
| 3210 | assert(c.size_bytes() == m.size_bytes()); | |
| 3211 | ||
| 3212 | auto *next_block = state->next_block; | |
| 3213 | ||
| 3214 | size_t m_size = m.size_bytes(); | |
| 3215 | auto *c_ptr = c.data(); | |
| 3216 | auto *m_ptr = m.data(); | |
| 3217 | ||
| 3218 | if (state->remainder) { | |
| 3219 | uint64_t offset = 0; | |
| 3220 | int rem = state->remainder; | |
| 3221 | ||
| 3222 | while (rem < 64 && offset < m_size) { | |
| 3223 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3224 | ++offset; | |
| 3225 | ++rem; | |
| 3226 | } | |
| 3227 | ||
| 3228 | c_ptr += offset; | |
| 3229 | m_ptr += offset; | |
| 3230 | m_size -= offset; | |
| 3231 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3232 | ||
| 3233 | if (m_size == 0) return; | |
| 3234 | } | |
| 3235 | ||
| 3236 | state->remainder = m_size & 63; | |
| 3237 | size_t main_len = m_size - state->remainder; | |
| 3238 | ||
| 3239 | crypto_stream_xchacha20_xor_ic( | |
| 3240 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3241 | ); | |
| 3242 | ||
| 3243 | state->block_counter += main_len / 64; | |
| 3244 | ||
| 3245 | if (state->remainder) { | |
| 3246 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3247 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3248 | ||
| 3249 | crypto_stream_xchacha20_xor_ic( | |
| 3250 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3251 | ); | |
| 3252 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3253 | ||
| 3254 | state->block_counter++; | |
| 3255 | } | |
| 3256 | } | |
| 3257 | ||
| 3258 | static inline void | |
| 3259 | sn_crypto_stream_xchacha20_xor_wrap_final( | |
| 3260 | js_env_t *, | |
| 3261 | js_receiver_t, | |
| 3262 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state | |
| 3263 | ) { | |
| 3264 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3265 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3266 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3267 | state->remainder = 0; | |
| 3268 | } | |
| 3269 | ||
| 3270 | struct sn_crypto_stream_salsa20_xor_state { | |
| 3271 | unsigned char n[crypto_stream_salsa20_NONCEBYTES]; | |
| 3272 | unsigned char k[crypto_stream_salsa20_KEYBYTES]; | |
| 3273 | unsigned char next_block[64]; | |
| 3274 | int remainder; | |
| 3275 | uint64_t block_counter; | |
| 3276 | }; | |
| 3277 | ||
| 3278 | static inline void | |
| 3279 | sn_crypto_stream_salsa20_xor_wrap_init( | |
| 3280 | js_env_t *, | |
| 3281 | js_receiver_t, | |
| 3282 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state, | |
| 3283 | js_typedarray_span_t<> n, | |
| 3284 | js_typedarray_span_t<> k | |
| 3285 | ) { | |
| 3286 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 3287 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 3288 | ||
| 3289 | state->remainder = 0; | |
| 3290 | state->block_counter = 0; | |
| 3291 | memcpy(state->n, n.data(), crypto_stream_salsa20_NONCEBYTES); | |
| 3292 | memcpy(state->k, k.data(), crypto_stream_salsa20_KEYBYTES); | |
| 3293 | } | |
| 3294 | ||
| 3295 | static inline void | |
| 3296 | sn_crypto_stream_salsa20_xor_wrap_update( | |
| 3297 | js_env_t *, | |
| 3298 | js_receiver_t, | |
| 3299 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state, | |
| 3300 | js_typedarray_span_t<> c, | |
| 3301 | js_typedarray_span_t<> m | |
| 3302 | ) { | |
| 3303 | assert(c.size_bytes() == m.size_bytes()); | |
| 3304 | ||
| 3305 | auto *next_block = state->next_block; | |
| 3306 | ||
| 3307 | size_t m_size = m.size_bytes(); | |
| 3308 | auto *c_ptr = c.data(); | |
| 3309 | auto *m_ptr = m.data(); | |
| 3310 | ||
| 3311 | if (state->remainder) { | |
| 3312 | uint64_t offset = 0; | |
| 3313 | int rem = state->remainder; | |
| 3314 | ||
| 3315 | while (rem < 64 && offset < m_size) { | |
| 3316 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3317 | ++offset; | |
| 3318 | ++rem; | |
| 3319 | } | |
| 3320 | ||
| 3321 | c_ptr += offset; | |
| 3322 | m_ptr += offset; | |
| 3323 | m_size -= offset; | |
| 3324 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3325 | ||
| 3326 | if (m_size == 0) return; | |
| 3327 | } | |
| 3328 | ||
| 3329 | state->remainder = m_size & 63; | |
| 3330 | size_t main_len = m_size - state->remainder; | |
| 3331 | ||
| 3332 | crypto_stream_salsa20_xor_ic( | |
| 3333 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3334 | ); | |
| 3335 | ||
| 3336 | state->block_counter += main_len / 64; | |
| 3337 | ||
| 3338 | if (state->remainder) { | |
| 3339 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3340 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3341 | ||
| 3342 | crypto_stream_salsa20_xor_ic( | |
| 3343 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3344 | ); | |
| 3345 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3346 | ||
| 3347 | state->block_counter++; | |
| 3348 | } | |
| 3349 | } | |
| 3350 | ||
| 3351 | static inline void | |
| 3352 | sn_crypto_stream_salsa20_xor_wrap_final( | |
| 3353 | js_env_t *, | |
| 3354 | js_receiver_t, | |
| 3355 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state | |
| 3356 | ) { | |
| 3357 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3358 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3359 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3360 | state->remainder = 0; | |
| 3361 | } | |
| 3362 | ||
| 3363 | // Experimental API | |
| 3364 | ||
| 3365 | static inline void | |
| 3366 | sn_extension_tweak_ed25519_base( | |
| 3367 | js_env_t *, | |
| 3368 | js_receiver_t, | |
| 3369 | js_typedarray_span_t<> n, | |
| 3370 | js_typedarray_span_t<> p, | |
| 3371 | js_typedarray_span_t<> ns | |
| 3372 | ) { | |
| 3373 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3374 | assert(p.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3375 | ||
| 3376 | sn__extension_tweak_ed25519_base(p.data(), n.data(), ns.data(), ns.size_bytes()); | |
| 3377 | } | |
| 3378 | ||
| 3379 | static inline int | |
| 3380 | sn_extension_tweak_ed25519_sign_detached( | |
| 3381 | js_env_t *, | |
| 3382 | js_receiver_t, | |
| 3383 | js_typedarray_span_t<> sig, | |
| 3384 | js_typedarray_span_t<> m, | |
| 3385 | js_typedarray_span_t<> scalar, | |
| 3386 | std::optional<js_typedarray_span_t<>> pk | |
| 3387 | ) { | |
| 3388 | assert(sig.size_bytes() == crypto_sign_BYTES); | |
| 3389 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3390 | ||
| 3391 | uint8_t *pk_data = nullptr; | |
| 3392 | if (pk) { | |
| 3393 | assert(pk->size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3394 | pk_data = pk->data(); | |
| 3395 | } | |
| 3396 | ||
| 3397 | return sn__extension_tweak_ed25519_sign_detached( | |
| 3398 | sig.data(), | |
| 3399 | nullptr, | |
| 3400 | m.data(), | |
| 3401 | m.size_bytes(), | |
| 3402 | scalar.data(), | |
| 3403 | pk_data | |
| 3404 | ); | |
| 3405 | } | |
| 3406 | ||
| 3407 | static inline void | |
| 3408 | sn_extension_tweak_ed25519_sk_to_scalar( | |
| 3409 | js_env_t *, | |
| 3410 | js_receiver_t, | |
| 3411 | js_typedarray_span_t<> n, | |
| 3412 | js_typedarray_span_t<> sk | |
| 3413 | ) { | |
| 3414 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3415 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 3416 | ||
| 3417 | sn__extension_tweak_ed25519_sk_to_scalar(n.data(), sk.data()); | |
| 3418 | } | |
| 3419 | ||
| 3420 | static inline void | |
| 3421 | sn_extension_tweak_ed25519_scalar( | |
| 3422 | js_env_t *, | |
| 3423 | js_receiver_t, | |
| 3424 | js_typedarray_span_t<> scalar_out, | |
| 3425 | js_typedarray_span_t<> scalar, | |
| 3426 | js_typedarray_span_t<> ns | |
| 3427 | ) { | |
| 3428 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3429 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3430 | ||
| 3431 | sn__extension_tweak_ed25519_scalar(scalar_out.data(), scalar.data(), ns.data(), ns.size_bytes()); | |
| 3432 | } | |
| 3433 | ||
| 3434 | static inline int | |
| 3435 | sn_extension_tweak_ed25519_pk( | |
| 3436 | js_env_t *, | |
| 3437 | js_receiver_t, | |
| 3438 | js_typedarray_span_t<> tpk, | |
| 3439 | js_typedarray_span_t<> pk, | |
| 3440 | js_typedarray_span_t<> ns | |
| 3441 | ) { | |
| 3442 | assert(tpk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3443 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3444 | ||
| 3445 | return sn__extension_tweak_ed25519_pk(tpk.data(), pk.data(), ns.data(), ns.size_bytes()); | |
| 3446 | } | |
| 3447 | ||
| 3448 | static inline void | |
| 3449 | sn_extension_tweak_ed25519_keypair( | |
| 3450 | js_env_t *, | |
| 3451 | js_receiver_t, | |
| 3452 | js_typedarray_span_t<> pk, | |
| 3453 | js_typedarray_span_t<> scalar_out, | |
| 3454 | js_typedarray_span_t<> scalar_in, | |
| 3455 | js_typedarray_span_t<> ns | |
| 3456 | ) { | |
| 3457 | assert(pk.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3458 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3459 | assert(scalar_in.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3460 | ||
| 3461 | sn__extension_tweak_ed25519_keypair( | |
| 3462 | pk.data(), | |
| 3463 | scalar_out.data(), | |
| 3464 | scalar_in.data(), | |
| 3465 | ns.data(), | |
| 3466 | ns.size_bytes() | |
| 3467 | ); | |
| 3468 | } | |
| 3469 | ||
| 3470 | static inline void | |
| 3471 | sn_extension_tweak_ed25519_scalar_add( | |
| 3472 | js_env_t *, | |
| 3473 | js_receiver_t, | |
| 3474 | js_typedarray_span_t<> scalar_out, | |
| 3475 | js_typedarray_span_t<> scalar, | |
| 3476 | js_typedarray_span_t<> n | |
| 3477 | ) { | |
| 3478 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3479 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3480 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3481 | ||
| 3482 | sn__extension_tweak_ed25519_scalar_add(scalar_out.data(), scalar.data(), n.data()); | |
| 3483 | } | |
| 3484 | ||
| 3485 | static inline int | |
| 3486 | sn_extension_tweak_ed25519_pk_add( | |
| 3487 | js_env_t *, | |
| 3488 | js_receiver_t, | |
| 3489 | js_typedarray_span_t<> tpk, | |
| 3490 | js_typedarray_span_t<> pk, | |
| 3491 | js_typedarray_span_t<> p | |
| 3492 | ) { | |
| 3493 | assert(tpk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3494 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3495 | assert(p.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3496 | ||
| 3497 | return sn__extension_tweak_ed25519_pk_add(tpk.data(), pk.data(), p.data()); | |
| 3498 | } | |
| 3499 | ||
| 3500 | static inline int | |
| 3501 | sn_extension_tweak_ed25519_keypair_add( | |
| 3502 | js_env_t *, | |
| 3503 | js_receiver_t, | |
| 3504 | js_typedarray_span_t<> pk, | |
| 3505 | js_typedarray_span_t<> scalar_out, | |
| 3506 | js_typedarray_span_t<> scalar_in, | |
| 3507 | js_typedarray_span_t<> tweak | |
| 3508 | ) { | |
| 3509 | assert(pk.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3510 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3511 | assert(scalar_in.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3512 | assert(tweak.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3513 | ||
| 3514 | return sn__extension_tweak_ed25519_keypair_add( | |
| 3515 | pk.data(), | |
| 3516 | scalar_out.data(), | |
| 3517 | scalar_in.data(), | |
| 3518 | tweak.data() | |
| 3519 | ); | |
| 3520 | } | |
| 3521 | ||
| 3522 | static inline int | |
| 3523 | sn_extension_pbkdf2_sha512( | |
| 3524 | js_env_t *, | |
| 3525 | js_receiver_t, | |
| 3526 | js_typedarray_span_t<> out, | |
| 3527 | js_typedarray_span_t<> passwd, | |
| 3528 | js_typedarray_span_t<> salt, | |
| 3529 | uint64_t iter, | |
| 3530 | uint64_t outlen | |
| 3531 | ) { | |
| 3532 | ||
| 3533 | assert(iter >= sn__extension_pbkdf2_sha512_ITERATIONS_MIN); | |
| 3534 | assert(outlen <= sn__extension_pbkdf2_sha512_BYTES_MAX); | |
| 3535 | assert(out.size_bytes() >= static_cast<size_t>(outlen)); | |
| 3536 | ||
| 3537 | return sn__extension_pbkdf2_sha512( | |
| 3538 | passwd.data(), | |
| 3539 | passwd.size_bytes(), | |
| 3540 | salt.data(), | |
| 3541 | salt.size_bytes(), | |
| 3542 | iter, | |
| 3543 | out.data(), | |
| 3544 | outlen | |
| 3545 | ); | |
| 3546 | } | |
| 3547 | ||
| 3548 | struct sn_async_pbkdf2_sha512_request : sn_async_task { | |
| 3549 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 3550 | std::span<uint8_t> out; | |
| 3551 | ||
| 3552 | size_t outlen; | |
| 3553 | ||
| 3554 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 3555 | std::span<uint8_t> pwd; | |
| 3556 | ||
| 3557 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 3558 | std::span<uint8_t> salt; | |
| 3559 | ||
| 3560 | uint64_t iter; | |
| 3561 | ||
| 3562 | sn_async_pbkdf2_sha512_request(js_env_t *env) : sn_async_task(env) {} | |
| 3563 | }; | |
| 3564 | ||
| 3565 | static void | |
| 3566 | async_pbkdf2_sha512_execute(uv_work_t *uv_req) { | |
| 3567 | auto *req = reinterpret_cast<sn_async_pbkdf2_sha512_request *>(uv_req); | |
| 3568 | ||
| 3569 | req->code = sn__extension_pbkdf2_sha512( | |
| 3570 | req->pwd.data(), | |
| 3571 | req->pwd.size(), | |
| 3572 | req->salt.data(), | |
| 3573 | req->salt.size(), | |
| 3574 | req->iter, | |
| 3575 | req->out.data(), | |
| 3576 | req->outlen | |
| 3577 | ); | |
| 3578 | } | |
| 3579 | ||
| 3580 | static void | |
| 3581 | async_pbkdf2_sha512_complete(uv_work_t *uv_req, int status) { | |
| 3582 | int err; | |
| 3583 | ||
| 3584 | auto *req = reinterpret_cast<sn_async_pbkdf2_sha512_request *>(uv_req); | |
| 3585 | ||
| 3586 | if (req->exiting) return delete req; | |
| 3587 | ||
| 3588 | js_handle_scope_t *scope; | |
| 3589 | err = js_open_handle_scope(req->env, &scope); | |
| 3590 | assert(err == 0); | |
| 3591 | ||
| 3592 | js_function_t<void, int> callback; | |
| 3593 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 3594 | assert(err == 0); | |
| 3595 | ||
| 3596 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 3597 | assert(err != js_pending_exception); | |
| 3598 | ||
| 3599 | err = js_close_handle_scope(req->env, scope); | |
| 3600 | assert(err == 0); | |
| 3601 | ||
| 3602 | delete req; | |
| 3603 | } | |
| 3604 | ||
| 3605 | static inline void | |
| 3606 | sn_extension_pbkdf2_sha512_async( | |
| 3607 | js_env_t *env, | |
| 3608 | js_receiver_t, | |
| 3609 | ||
| 3610 | js_arraybuffer_t out, | |
| 3611 | uint32_t out_offset, | |
| 3612 | uint32_t out_len, | |
| 3613 | ||
| 3614 | js_arraybuffer_t pwd, | |
| 3615 | uint32_t pwd_offset, | |
| 3616 | uint32_t pwd_len, | |
| 3617 | ||
| 3618 | js_arraybuffer_t salt, | |
| 3619 | uint32_t salt_offset, | |
| 3620 | uint32_t salt_len, | |
| 3621 | ||
| 3622 | uint64_t iter, | |
| 3623 | uint64_t outlen, | |
| 3624 | ||
| 3625 | js_function_t<void, int> callback | |
| 3626 | ) { | |
| 3627 | int err; | |
| 3628 | ||
| 3629 | auto *req = new sn_async_pbkdf2_sha512_request(env); | |
| 3630 | ||
| 3631 | std::span<uint8_t> out_view; | |
| 3632 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 3633 | assert(err == 0); | |
| 3634 | assert(out_offset + out_len <= out_view.size()); | |
| 3635 | ||
| 3636 | req->out = {&out_view[out_offset], out_len}; | |
| 3637 | ||
| 3638 | std::span<uint8_t> pwd_view; | |
| 3639 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 3640 | assert(err == 0); | |
| 3641 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 3642 | ||
| 3643 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 3644 | ||
| 3645 | std::span<uint8_t> salt_view; | |
| 3646 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 3647 | assert(err == 0); | |
| 3648 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 3649 | ||
| 3650 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 3651 | ||
| 3652 | req->iter = iter; | |
| 3653 | req->outlen = outlen; | |
| 3654 | ||
| 3655 | err = js_create_reference(env, out, req->out_ref); | |
| 3656 | assert(err == 0); | |
| 3657 | ||
| 3658 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 3659 | assert(err == 0); | |
| 3660 | ||
| 3661 | err = js_create_reference(env, salt, req->salt_ref); | |
| 3662 | assert(err == 0); | |
| 3663 | ||
| 3664 | err = js_create_reference(env, callback, req->cb); | |
| 3665 | assert(err == 0); | |
| 3666 | ||
| 3667 | uv_loop_t *loop; | |
| 3668 | err = js_get_env_loop(env, &loop); | |
| 3669 | assert(err == 0); | |
| 3670 | ||
| 3671 | err = uv_queue_work(loop, &req->task, async_pbkdf2_sha512_execute, async_pbkdf2_sha512_complete); | |
| 3672 | assert(err == 0); | |
| 3673 | } | |
| 3674 | ||
| 3675 | static js_value_t * | |
| 3676 | sodium_native_exports(js_env_t *env, js_value_t *exports) { | |
| 3677 | int err; | |
| 3678 | ||
| 3679 | err = sodium_init(); | |
| 3680 | assert(err >= 0); | |
| 3681 | ||
| 3682 | #define V_FUNCTION(name, fn) \ | |
| 3683 | err = js_set_property<fn>(env, exports, name); \ | |
| 3684 | assert(err == 0); | |
| 3685 | ||
| 3686 | #define V_FUNCTION_NOSCOPE(name, fn) \ | |
| 3687 | err = js_set_property<fn, js_function_options_t{.scoped = false}>(env, exports, name); \ | |
| 3688 | assert(err == 0); | |
| 3689 | ||
| 3690 | #define V_UINT32(name, constant) \ | |
| 3691 | err = js_set_property(env, exports, name, static_cast<uint32_t>(constant)); \ | |
| 3692 | assert(err == 0); | |
| 3693 | ||
| 3694 | #define V_UINT64(name, constant) \ | |
| 3695 | err = js_set_property(env, exports, name, static_cast<int64_t>(std::min<uint64_t>(constant, js_max_safe_integer))); \ | |
| 3696 | assert(err == 0); | |
| 3697 | ||
| 3698 | #define V_STRING(name, str) \ | |
| 3699 | err = js_set_property(env, exports, name, str); \ | |
| 3700 | assert(err == 0); | |
| 3701 | ||
| 3702 | // memory | |
| 3703 | ||
| 3704 | V_FUNCTION("sodium_memzero", sn_sodium_memzero); | |
| 3705 | V_FUNCTION("sodium_mlock", sn_sodium_mlock); | |
| 3706 | V_FUNCTION("sodium_munlock", sn_sodium_munlock); | |
| 3707 | V_FUNCTION("sodium_malloc", sn_sodium_malloc); | |
| 3708 | V_FUNCTION("sodium_free", sn_sodium_free); | |
| 3709 | V_FUNCTION("sodium_mprotect_noaccess", sn_sodium_mprotect_noaccess); | |
| 3710 | V_FUNCTION("sodium_mprotect_readonly", sn_sodium_mprotect_readonly); | |
| 3711 | V_FUNCTION("sodium_mprotect_readwrite", sn_sodium_mprotect_readwrite); | |
| 3712 | ||
| 3713 | // randombytes | |
| 3714 | ||
| 3715 | V_FUNCTION_NOSCOPE("randombytes_buf", sn_randombytes_buf); | |
| 3716 | V_FUNCTION_NOSCOPE("randombytes_buf_deterministic", sn_randombytes_buf_deterministic); | |
| 3717 | V_FUNCTION_NOSCOPE("randombytes_random", sn_randombytes_random); | |
| 3718 | V_FUNCTION_NOSCOPE("randombytes_uniform", sn_randombytes_uniform); | |
| 3719 | ||
| 3720 | V_UINT32("randombytes_SEEDBYTES", randombytes_SEEDBYTES); | |
| 3721 | ||
| 3722 | // sodium helpers | |
| 3723 | ||
| 3724 | V_FUNCTION("sodium_memcmp", sn_sodium_memcmp); | |
| 3725 | V_FUNCTION("sodium_increment", sn_sodium_increment); | |
| 3726 | V_FUNCTION("sodium_add", sn_sodium_add); | |
| 3727 | V_FUNCTION("sodium_sub", sn_sodium_sub); | |
| 3728 | V_FUNCTION("sodium_compare", sn_sodium_compare); | |
| 3729 | V_FUNCTION("sodium_is_zero", sn_sodium_is_zero); | |
| 3730 | V_FUNCTION("sodium_pad", sn_sodium_pad); | |
| 3731 | V_FUNCTION("sodium_unpad", sn_sodium_unpad); | |
| 3732 | ||
| 3733 | // crypto_aead | |
| 3734 | ||
| 3735 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_keygen", sn_crypto_aead_xchacha20poly1305_ietf_keygen); | |
| 3736 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_encrypt", sn_crypto_aead_xchacha20poly1305_ietf_encrypt); | |
| 3737 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_decrypt", sn_crypto_aead_xchacha20poly1305_ietf_decrypt); | |
| 3738 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_encrypt_detached", sn_crypto_aead_xchacha20poly1305_ietf_encrypt_detached); | |
| 3739 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_decrypt_detached", sn_crypto_aead_xchacha20poly1305_ietf_decrypt_detached); | |
| 3740 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_ABYTES", crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 3741 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_KEYBYTES", crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 3742 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_NPUBBYTES", crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 3743 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_NSECBYTES", crypto_aead_xchacha20poly1305_ietf_NSECBYTES); | |
| 3744 | V_UINT64("crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX", crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX); | |
| 3745 | ||
| 3746 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_keygen", sn_crypto_aead_chacha20poly1305_ietf_keygen); | |
| 3747 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_encrypt", sn_crypto_aead_chacha20poly1305_ietf_encrypt); | |
| 3748 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_decrypt", sn_crypto_aead_chacha20poly1305_ietf_decrypt); | |
| 3749 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_encrypt_detached", sn_crypto_aead_chacha20poly1305_ietf_encrypt_detached); | |
| 3750 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_decrypt_detached", sn_crypto_aead_chacha20poly1305_ietf_decrypt_detached); | |
| 3751 | V_UINT32("crypto_aead_chacha20poly1305_ietf_ABYTES", crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 3752 | V_UINT32("crypto_aead_chacha20poly1305_ietf_KEYBYTES", crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 3753 | V_UINT32("crypto_aead_chacha20poly1305_ietf_NPUBBYTES", crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 3754 | V_UINT32("crypto_aead_chacha20poly1305_ietf_NSECBYTES", crypto_aead_chacha20poly1305_ietf_NSECBYTES); | |
| 3755 | V_UINT64("crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX", crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX); | |
| 3756 | ||
| 3757 | // crypto_auth | |
| 3758 | ||
| 3759 | V_FUNCTION("crypto_auth", sn_crypto_auth); | |
| 3760 | V_FUNCTION("crypto_auth_verify", sn_crypto_auth_verify); | |
| 3761 | V_UINT32("crypto_auth_BYTES", crypto_auth_BYTES); | |
| 3762 | V_UINT32("crypto_auth_KEYBYTES", crypto_auth_KEYBYTES); | |
| 3763 | V_STRING("crypto_auth_PRIMITIVE", crypto_auth_PRIMITIVE); | |
| 3764 | ||
| 3765 | // crypto_box | |
| 3766 | ||
| 3767 | V_FUNCTION("crypto_box_keypair", sn_crypto_box_keypair); | |
| 3768 | V_FUNCTION("crypto_box_seed_keypair", sn_crypto_box_seed_keypair); | |
| 3769 | V_FUNCTION("crypto_box_easy", sn_crypto_box_easy); | |
| 3770 | V_FUNCTION("crypto_box_open_easy", sn_crypto_box_open_easy); | |
| 3771 | V_FUNCTION("crypto_box_detached", sn_crypto_box_detached); | |
| 3772 | V_FUNCTION("crypto_box_open_detached", sn_crypto_box_open_detached); | |
| 3773 | V_FUNCTION("crypto_box_seal", sn_crypto_box_seal); | |
| 3774 | V_FUNCTION_NOSCOPE("crypto_box_seal_open", sn_crypto_box_seal_open); | |
| 3775 | ||
| 3776 | V_UINT32("crypto_box_SEEDBYTES", crypto_box_SEEDBYTES); | |
| 3777 | V_UINT32("crypto_box_PUBLICKEYBYTES", crypto_box_PUBLICKEYBYTES); | |
| 3778 | V_UINT32("crypto_box_SECRETKEYBYTES", crypto_box_SECRETKEYBYTES); | |
| 3779 | V_UINT32("crypto_box_NONCEBYTES", crypto_box_NONCEBYTES); | |
| 3780 | V_UINT32("crypto_box_MACBYTES", crypto_box_MACBYTES); | |
| 3781 | V_UINT32("crypto_box_SEALBYTES", crypto_box_SEALBYTES); | |
| 3782 | V_STRING("crypto_box_PRIMITIVE", crypto_box_PRIMITIVE); | |
| 3783 | ||
| 3784 | // crypto_core | |
| 3785 | ||
| 3786 | V_FUNCTION("crypto_core_ed25519_is_valid_point", sn_crypto_core_ed25519_is_valid_point); | |
| 3787 | V_FUNCTION("crypto_core_ed25519_from_uniform", sn_crypto_core_ed25519_from_uniform); | |
| 3788 | V_FUNCTION("crypto_core_ed25519_add", sn_crypto_core_ed25519_add); | |
| 3789 | V_FUNCTION("crypto_core_ed25519_sub", sn_crypto_core_ed25519_sub); | |
| 3790 | V_FUNCTION("crypto_core_ed25519_scalar_random", sn_crypto_core_ed25519_scalar_random); | |
| 3791 | V_FUNCTION("crypto_core_ed25519_scalar_reduce", sn_crypto_core_ed25519_scalar_reduce); | |
| 3792 | V_FUNCTION("crypto_core_ed25519_scalar_invert", sn_crypto_core_ed25519_scalar_invert); | |
| 3793 | V_FUNCTION("crypto_core_ed25519_scalar_negate", sn_crypto_core_ed25519_scalar_negate); | |
| 3794 | V_FUNCTION("crypto_core_ed25519_scalar_complement", sn_crypto_core_ed25519_scalar_complement); | |
| 3795 | V_FUNCTION("crypto_core_ed25519_scalar_add", sn_crypto_core_ed25519_scalar_add); | |
| 3796 | V_FUNCTION("crypto_core_ed25519_scalar_sub", sn_crypto_core_ed25519_scalar_sub); | |
| 3797 | V_UINT32("crypto_core_ed25519_BYTES", crypto_core_ed25519_BYTES); | |
| 3798 | V_UINT32("crypto_core_ed25519_UNIFORMBYTES", crypto_core_ed25519_UNIFORMBYTES); | |
| 3799 | V_UINT32("crypto_core_ed25519_SCALARBYTES", crypto_core_ed25519_SCALARBYTES); | |
| 3800 | V_UINT32("crypto_core_ed25519_NONREDUCEDSCALARBYTES", crypto_core_ed25519_NONREDUCEDSCALARBYTES); | |
| 3801 | ||
| 3802 | // crypto_kdf | |
| 3803 | ||
| 3804 | V_FUNCTION("crypto_kdf_keygen", sn_crypto_kdf_keygen); | |
| 3805 | V_FUNCTION("crypto_kdf_derive_from_key", sn_crypto_kdf_derive_from_key); | |
| 3806 | V_UINT32("crypto_kdf_BYTES_MIN", crypto_kdf_BYTES_MIN); | |
| 3807 | V_UINT32("crypto_kdf_BYTES_MAX", crypto_kdf_BYTES_MAX); | |
| 3808 | V_UINT32("crypto_kdf_CONTEXTBYTES", crypto_kdf_CONTEXTBYTES); | |
| 3809 | V_UINT32("crypto_kdf_KEYBYTES", crypto_kdf_KEYBYTES); | |
| 3810 | V_STRING("crypto_kdf_PRIMITIVE", crypto_kdf_PRIMITIVE); | |
| 3811 | ||
| 3812 | // crypto_kx | |
| 3813 | ||
| 3814 | V_FUNCTION("crypto_kx_keypair", sn_crypto_kx_keypair); | |
| 3815 | V_FUNCTION("crypto_kx_seed_keypair", sn_crypto_kx_seed_keypair); | |
| 3816 | V_FUNCTION("crypto_kx_client_session_keys", sn_crypto_kx_client_session_keys); | |
| 3817 | V_FUNCTION("crypto_kx_server_session_keys", sn_crypto_kx_server_session_keys); | |
| 3818 | V_UINT32("crypto_kx_PUBLICKEYBYTES", crypto_kx_PUBLICKEYBYTES); | |
| 3819 | V_UINT32("crypto_kx_SECRETKEYBYTES", crypto_kx_SECRETKEYBYTES); | |
| 3820 | V_UINT32("crypto_kx_SEEDBYTES", crypto_kx_SEEDBYTES); | |
| 3821 | V_UINT32("crypto_kx_SESSIONKEYBYTES", crypto_kx_SESSIONKEYBYTES); | |
| 3822 | V_STRING("crypto_kx_PRIMITIVE", crypto_kx_PRIMITIVE); | |
| 3823 | ||
| 3824 | // crypto_generichash | |
| 3825 | ||
| 3826 | V_FUNCTION_NOSCOPE("crypto_generichash", sn_crypto_generichash); | |
| 3827 | V_FUNCTION("crypto_generichash_batch", sn_crypto_generichash_batch); | |
| 3828 | V_FUNCTION_NOSCOPE("crypto_generichash_keygen", sn_crypto_generichash_keygen); | |
| 3829 | V_FUNCTION_NOSCOPE("crypto_generichash_init", sn_crypto_generichash_init); | |
| 3830 | V_FUNCTION_NOSCOPE("crypto_generichash_update", sn_crypto_generichash_update); | |
| 3831 | V_FUNCTION_NOSCOPE("crypto_generichash_final", sn_crypto_generichash_final); | |
| 3832 | ||
| 3833 | V_UINT32("crypto_generichash_STATEBYTES", sizeof(crypto_generichash_state)); | |
| 3834 | V_STRING("crypto_generichash_PRIMITIVE", crypto_generichash_PRIMITIVE); | |
| 3835 | V_UINT32("crypto_generichash_BYTES_MIN", crypto_generichash_BYTES_MIN); | |
| 3836 | V_UINT32("crypto_generichash_BYTES_MAX", crypto_generichash_BYTES_MAX); | |
| 3837 | V_UINT32("crypto_generichash_BYTES", crypto_generichash_BYTES); | |
| 3838 | V_UINT32("crypto_generichash_KEYBYTES_MIN", crypto_generichash_KEYBYTES_MIN); | |
| 3839 | V_UINT32("crypto_generichash_KEYBYTES_MAX", crypto_generichash_KEYBYTES_MAX); | |
| 3840 | V_UINT32("crypto_generichash_KEYBYTES", crypto_generichash_KEYBYTES); | |
| 3841 | ||
| 3842 | // crypto_hash | |
| 3843 | ||
| 3844 | V_FUNCTION("crypto_hash", sn_crypto_hash); | |
| 3845 | V_UINT32("crypto_hash_BYTES", crypto_hash_BYTES); | |
| 3846 | V_STRING("crypto_hash_PRIMITIVE", crypto_hash_PRIMITIVE); | |
| 3847 | ||
| 3848 | V_FUNCTION("crypto_hash_sha256", sn_crypto_hash_sha256); | |
| 3849 | V_FUNCTION("crypto_hash_sha256_init", sn_crypto_hash_sha256_init); | |
| 3850 | V_FUNCTION("crypto_hash_sha256_update", sn_crypto_hash_sha256_update); | |
| 3851 | V_FUNCTION("crypto_hash_sha256_final", sn_crypto_hash_sha256_final); | |
| 3852 | V_UINT32("crypto_hash_sha256_STATEBYTES", sizeof(crypto_hash_sha256_state)); | |
| 3853 | V_UINT32("crypto_hash_sha256_BYTES", crypto_hash_sha256_BYTES); | |
| 3854 | ||
| 3855 | V_FUNCTION("crypto_hash_sha512", sn_crypto_hash_sha512); | |
| 3856 | V_FUNCTION("crypto_hash_sha512_init", sn_crypto_hash_sha512_init); | |
| 3857 | V_FUNCTION("crypto_hash_sha512_update", sn_crypto_hash_sha512_update); | |
| 3858 | V_FUNCTION("crypto_hash_sha512_final", sn_crypto_hash_sha512_final); | |
| 3859 | V_UINT32("crypto_hash_sha512_STATEBYTES", sizeof(crypto_hash_sha512_state)); | |
| 3860 | V_UINT32("crypto_hash_sha512_BYTES", crypto_hash_sha512_BYTES); | |
| 3861 | ||
| 3862 | // crypto_onetimeauth | |
| 3863 | ||
| 3864 | V_FUNCTION("crypto_onetimeauth", sn_crypto_onetimeauth); | |
| 3865 | V_FUNCTION("crypto_onetimeauth_verify", sn_crypto_onetimeauth_verify); | |
| 3866 | V_FUNCTION("crypto_onetimeauth_init", sn_crypto_onetimeauth_init); | |
| 3867 | V_FUNCTION("crypto_onetimeauth_update", sn_crypto_onetimeauth_update); | |
| 3868 | V_FUNCTION("crypto_onetimeauth_final", sn_crypto_onetimeauth_final); | |
| 3869 | V_UINT32("crypto_onetimeauth_STATEBYTES", sizeof(crypto_onetimeauth_state)); | |
| 3870 | V_UINT32("crypto_onetimeauth_BYTES", crypto_onetimeauth_BYTES); | |
| 3871 | V_UINT32("crypto_onetimeauth_KEYBYTES", crypto_onetimeauth_KEYBYTES); | |
| 3872 | V_STRING("crypto_onetimeauth_PRIMITIVE", crypto_onetimeauth_PRIMITIVE); | |
| 3873 | ||
| 3874 | // crypto_pwhash | |
| 3875 | ||
| 3876 | V_FUNCTION("crypto_pwhash", sn_crypto_pwhash); | |
| 3877 | V_FUNCTION("crypto_pwhash_str", sn_crypto_pwhash_str); | |
| 3878 | V_FUNCTION("crypto_pwhash_str_verify", sn_crypto_pwhash_str_verify); | |
| 3879 | V_FUNCTION("crypto_pwhash_str_needs_rehash", sn_crypto_pwhash_str_needs_rehash); | |
| 3880 | V_FUNCTION("crypto_pwhash_async", sn_crypto_pwhash_async); | |
| 3881 | V_FUNCTION("crypto_pwhash_str_async", sn_crypto_pwhash_str_async); | |
| 3882 | V_FUNCTION("crypto_pwhash_str_verify_async", sn_crypto_pwhash_str_verify_async); | |
| 3883 | V_UINT32("crypto_pwhash_ALG_ARGON2I13", crypto_pwhash_ALG_ARGON2I13); | |
| 3884 | V_UINT32("crypto_pwhash_ALG_ARGON2ID13", crypto_pwhash_ALG_ARGON2ID13); | |
| 3885 | V_UINT32("crypto_pwhash_ALG_DEFAULT", crypto_pwhash_ALG_DEFAULT); | |
| 3886 | V_UINT32("crypto_pwhash_BYTES_MIN", crypto_pwhash_BYTES_MIN); | |
| 3887 | V_UINT32("crypto_pwhash_BYTES_MAX", crypto_pwhash_BYTES_MAX); | |
| 3888 | V_UINT32("crypto_pwhash_PASSWD_MIN", crypto_pwhash_PASSWD_MIN); | |
| 3889 | V_UINT32("crypto_pwhash_PASSWD_MAX", crypto_pwhash_PASSWD_MAX); | |
| 3890 | V_UINT32("crypto_pwhash_SALTBYTES", crypto_pwhash_SALTBYTES); | |
| 3891 | V_UINT32("crypto_pwhash_STRBYTES", crypto_pwhash_STRBYTES); | |
| 3892 | V_STRING("crypto_pwhash_STRPREFIX", crypto_pwhash_STRPREFIX); | |
| 3893 | V_UINT32("crypto_pwhash_OPSLIMIT_MIN", crypto_pwhash_OPSLIMIT_MIN); | |
| 3894 | V_UINT32("crypto_pwhash_OPSLIMIT_MAX", crypto_pwhash_OPSLIMIT_MAX); | |
| 3895 | V_UINT64("crypto_pwhash_MEMLIMIT_MIN", crypto_pwhash_MEMLIMIT_MIN); | |
| 3896 | V_UINT64("crypto_pwhash_MEMLIMIT_MAX", crypto_pwhash_MEMLIMIT_MAX); | |
| 3897 | V_UINT32("crypto_pwhash_OPSLIMIT_INTERACTIVE", crypto_pwhash_OPSLIMIT_INTERACTIVE); | |
| 3898 | V_UINT64("crypto_pwhash_MEMLIMIT_INTERACTIVE", crypto_pwhash_MEMLIMIT_INTERACTIVE); | |
| 3899 | V_UINT32("crypto_pwhash_OPSLIMIT_MODERATE", crypto_pwhash_OPSLIMIT_MODERATE); | |
| 3900 | V_UINT64("crypto_pwhash_MEMLIMIT_MODERATE", crypto_pwhash_MEMLIMIT_MODERATE); | |
| 3901 | V_UINT32("crypto_pwhash_OPSLIMIT_SENSITIVE", crypto_pwhash_OPSLIMIT_SENSITIVE); | |
| 3902 | V_UINT64("crypto_pwhash_MEMLIMIT_SENSITIVE", crypto_pwhash_MEMLIMIT_SENSITIVE); | |
| 3903 | V_STRING("crypto_pwhash_PRIMITIVE", crypto_pwhash_PRIMITIVE); | |
| 3904 | ||
| 3905 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256", sn_crypto_pwhash_scryptsalsa208sha256); | |
| 3906 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str", sn_crypto_pwhash_scryptsalsa208sha256_str); | |
| 3907 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_verify", sn_crypto_pwhash_scryptsalsa208sha256_str_verify); | |
| 3908 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_needs_rehash", sn_crypto_pwhash_scryptsalsa208sha256_str_needs_rehash); | |
| 3909 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_async", sn_crypto_pwhash_scryptsalsa208sha256_async); | |
| 3910 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_async", sn_crypto_pwhash_scryptsalsa208sha256_str_async) | |
| 3911 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_verify_async", sn_crypto_pwhash_scryptsalsa208sha256_str_verify_async); | |
| 3912 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_BYTES_MIN", crypto_pwhash_scryptsalsa208sha256_BYTES_MIN); | |
| 3913 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_BYTES_MAX", crypto_pwhash_scryptsalsa208sha256_BYTES_MAX); | |
| 3914 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN", crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN); | |
| 3915 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX", crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX); | |
| 3916 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_SALTBYTES", crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 3917 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_STRBYTES", crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 3918 | V_STRING("crypto_pwhash_scryptsalsa208sha256_STRPREFIX", crypto_pwhash_scryptsalsa208sha256_STRPREFIX); | |
| 3919 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 3920 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 3921 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 3922 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 3923 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); | |
| 3924 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); | |
| 3925 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE); | |
| 3926 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE); | |
| 3927 | ||
| 3928 | // crypto_scalarmult | |
| 3929 | ||
| 3930 | V_FUNCTION("crypto_scalarmult_base", sn_crypto_scalarmult_base); | |
| 3931 | V_FUNCTION("crypto_scalarmult", sn_crypto_scalarmult); | |
| 3932 | V_STRING("crypto_scalarmult_PRIMITIVE", crypto_scalarmult_PRIMITIVE); | |
| 3933 | V_UINT32("crypto_scalarmult_BYTES", crypto_scalarmult_BYTES); | |
| 3934 | V_UINT32("crypto_scalarmult_SCALARBYTES", crypto_scalarmult_SCALARBYTES); | |
| 3935 | ||
| 3936 | V_FUNCTION("crypto_scalarmult_ed25519_base", sn_crypto_scalarmult_ed25519_base); | |
| 3937 | V_FUNCTION("crypto_scalarmult_ed25519", sn_crypto_scalarmult_ed25519); | |
| 3938 | V_FUNCTION("crypto_scalarmult_ed25519_base_noclamp", sn_crypto_scalarmult_ed25519_base_noclamp); | |
| 3939 | V_FUNCTION("crypto_scalarmult_ed25519_noclamp", sn_crypto_scalarmult_ed25519_noclamp); | |
| 3940 | V_UINT32("crypto_scalarmult_ed25519_BYTES", crypto_scalarmult_ed25519_BYTES); | |
| 3941 | V_UINT32("crypto_scalarmult_ed25519_SCALARBYTES", crypto_scalarmult_ed25519_SCALARBYTES); | |
| 3942 | ||
| 3943 | // crypto_secretbox | |
| 3944 | ||
| 3945 | V_FUNCTION("crypto_secretbox_easy", sn_crypto_secretbox_easy); | |
| 3946 | V_FUNCTION("crypto_secretbox_open_easy", sn_crypto_secretbox_open_easy); | |
| 3947 | V_FUNCTION("crypto_secretbox_detached", sn_crypto_secretbox_detached); | |
| 3948 | V_FUNCTION("crypto_secretbox_open_detached", sn_crypto_secretbox_open_detached); | |
| 3949 | V_UINT32("crypto_secretbox_KEYBYTES", crypto_secretbox_KEYBYTES); | |
| 3950 | V_UINT32("crypto_secretbox_NONCEBYTES", crypto_secretbox_NONCEBYTES); | |
| 3951 | V_UINT32("crypto_secretbox_MACBYTES", crypto_secretbox_MACBYTES); | |
| 3952 | V_STRING("crypto_secretbox_PRIMITIVE", crypto_secretbox_PRIMITIVE); | |
| 3953 | ||
| 3954 | // crypto_secretstream | |
| 3955 | ||
| 3956 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_keygen", sn_crypto_secretstream_xchacha20poly1305_keygen); | |
| 3957 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_init_push", sn_crypto_secretstream_xchacha20poly1305_init_push); | |
| 3958 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_init_pull", sn_crypto_secretstream_xchacha20poly1305_init_pull); | |
| 3959 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_push", sn_crypto_secretstream_xchacha20poly1305_push); | |
| 3960 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_pull", sn_crypto_secretstream_xchacha20poly1305_pull); | |
| 3961 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_rekey", sn_crypto_secretstream_xchacha20poly1305_rekey); | |
| 3962 | ||
| 3963 | V_UINT32("crypto_secretstream_xchacha20poly1305_STATEBYTES", sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 3964 | V_UINT32("crypto_secretstream_xchacha20poly1305_ABYTES", crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 3965 | V_UINT32("crypto_secretstream_xchacha20poly1305_HEADERBYTES", crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 3966 | V_UINT32("crypto_secretstream_xchacha20poly1305_KEYBYTES", crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 3967 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAGBYTES", 1); | |
| 3968 | V_UINT64("crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX", crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX); | |
| 3969 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_MESSAGE", crypto_secretstream_xchacha20poly1305_TAG_MESSAGE); | |
| 3970 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_PUSH", crypto_secretstream_xchacha20poly1305_TAG_PUSH); | |
| 3971 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_REKEY", crypto_secretstream_xchacha20poly1305_TAG_REKEY); | |
| 3972 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_FINAL", crypto_secretstream_xchacha20poly1305_TAG_FINAL); | |
| 3973 | ||
| 3974 | // crypto_shorthash | |
| 3975 | ||
| 3976 | V_FUNCTION("crypto_shorthash", sn_crypto_shorthash); | |
| 3977 | V_UINT32("crypto_shorthash_BYTES", crypto_shorthash_BYTES); | |
| 3978 | V_UINT32("crypto_shorthash_KEYBYTES", crypto_shorthash_KEYBYTES); | |
| 3979 | V_STRING("crypto_shorthash_PRIMITIVE", crypto_shorthash_PRIMITIVE); | |
| 3980 | ||
| 3981 | // crypto_sign | |
| 3982 | ||
| 3983 | V_FUNCTION("crypto_sign_keypair", sn_crypto_sign_keypair); | |
| 3984 | V_FUNCTION("crypto_sign_seed_keypair", sn_crypto_sign_seed_keypair); | |
| 3985 | V_FUNCTION("crypto_sign", sn_crypto_sign); | |
| 3986 | V_FUNCTION("crypto_sign_open", sn_crypto_sign_open); | |
| 3987 | V_FUNCTION("crypto_sign_detached", sn_crypto_sign_detached); | |
| 3988 | V_FUNCTION_NOSCOPE("crypto_sign_verify_detached", sn_crypto_sign_verify_detached); | |
| 3989 | V_FUNCTION("crypto_sign_ed25519_sk_to_pk", sn_crypto_sign_ed25519_sk_to_pk); | |
| 3990 | V_FUNCTION("crypto_sign_ed25519_pk_to_curve25519", sn_crypto_sign_ed25519_pk_to_curve25519); | |
| 3991 | V_FUNCTION("crypto_sign_ed25519_sk_to_curve25519", sn_crypto_sign_ed25519_sk_to_curve25519); | |
| 3992 | ||
| 3993 | V_UINT32("crypto_sign_SEEDBYTES", crypto_sign_SEEDBYTES); | |
| 3994 | V_UINT32("crypto_sign_PUBLICKEYBYTES", crypto_sign_PUBLICKEYBYTES); | |
| 3995 | V_UINT32("crypto_sign_SECRETKEYBYTES", crypto_sign_SECRETKEYBYTES); | |
| 3996 | V_UINT32("crypto_sign_BYTES", crypto_sign_BYTES); | |
| 3997 | ||
| 3998 | // crypto_stream | |
| 3999 | ||
| 4000 | V_FUNCTION("crypto_stream", sn_crypto_stream); | |
| 4001 | V_UINT32("crypto_stream_KEYBYTES", crypto_stream_KEYBYTES); | |
| 4002 | V_UINT32("crypto_stream_NONCEBYTES", crypto_stream_NONCEBYTES); | |
| 4003 | V_STRING("crypto_stream_PRIMITIVE", crypto_stream_PRIMITIVE); | |
| 4004 | ||
| 4005 | V_FUNCTION_NOSCOPE("crypto_stream_xor", sn_crypto_stream_xor); | |
| 4006 | V_FUNCTION("crypto_stream_xor_init", sn_crypto_stream_xor_wrap_init); | |
| 4007 | V_FUNCTION("crypto_stream_xor_update", sn_crypto_stream_xor_wrap_update); | |
| 4008 | V_FUNCTION("crypto_stream_xor_final", sn_crypto_stream_xor_wrap_final); | |
| 4009 | V_UINT32("crypto_stream_xor_STATEBYTES", sizeof(sn_crypto_stream_xor_state)); | |
| 4010 | ||
| 4011 | V_FUNCTION("crypto_stream_chacha20", sn_crypto_stream_chacha20); | |
| 4012 | V_UINT32("crypto_stream_chacha20_KEYBYTES", crypto_stream_chacha20_KEYBYTES); | |
| 4013 | V_UINT32("crypto_stream_chacha20_NONCEBYTES", crypto_stream_chacha20_NONCEBYTES); | |
| 4014 | V_UINT64("crypto_stream_chacha20_MESSAGEBYTES_MAX", crypto_stream_chacha20_MESSAGEBYTES_MAX); | |
| 4015 | ||
| 4016 | V_FUNCTION("crypto_stream_chacha20_xor", sn_crypto_stream_chacha20_xor); | |
| 4017 | V_FUNCTION("crypto_stream_chacha20_xor_ic", sn_crypto_stream_chacha20_xor_ic); | |
| 4018 | V_FUNCTION("crypto_stream_chacha20_xor_init", sn_crypto_stream_chacha20_xor_wrap_init); | |
| 4019 | V_FUNCTION("crypto_stream_chacha20_xor_update", sn_crypto_stream_chacha20_xor_wrap_update); | |
| 4020 | V_FUNCTION("crypto_stream_chacha20_xor_final", sn_crypto_stream_chacha20_xor_wrap_final); | |
| 4021 | V_UINT32("crypto_stream_chacha20_xor_STATEBYTES", sizeof(sn_crypto_stream_chacha20_xor_state)); | |
| 4022 | ||
| 4023 | V_FUNCTION("crypto_stream_chacha20_ietf", sn_crypto_stream_chacha20_ietf); | |
| 4024 | V_UINT32("crypto_stream_chacha20_ietf_KEYBYTES", crypto_stream_chacha20_ietf_KEYBYTES); | |
| 4025 | V_UINT32("crypto_stream_chacha20_ietf_NONCEBYTES", crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 4026 | V_UINT64("crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX", crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX); | |
| 4027 | V_UINT32("crypto_stream_chacha20_ietf_xor_STATEBYTES", sizeof(sn_crypto_stream_chacha20_ietf_xor_state)); | |
| 4028 | ||
| 4029 | V_FUNCTION("crypto_stream_chacha20_ietf_xor", sn_crypto_stream_chacha20_ietf_xor); | |
| 4030 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_ic", sn_crypto_stream_chacha20_ietf_xor_ic); | |
| 4031 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_init", sn_crypto_stream_chacha20_ietf_xor_wrap_init); | |
| 4032 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_update", sn_crypto_stream_chacha20_ietf_xor_wrap_update); | |
| 4033 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_final", sn_crypto_stream_chacha20_ietf_xor_wrap_final); | |
| 4034 | ||
| 4035 | V_FUNCTION("crypto_stream_xchacha20", sn_crypto_stream_xchacha20); | |
| 4036 | V_UINT32("crypto_stream_xchacha20_KEYBYTES", crypto_stream_xchacha20_KEYBYTES); | |
| 4037 | V_UINT32("crypto_stream_xchacha20_NONCEBYTES", crypto_stream_xchacha20_NONCEBYTES); | |
| 4038 | V_UINT64("crypto_stream_xchacha20_MESSAGEBYTES_MAX", crypto_stream_xchacha20_MESSAGEBYTES_MAX); | |
| 4039 | ||
| 4040 | V_FUNCTION("crypto_stream_xchacha20_xor", sn_crypto_stream_xchacha20_xor); | |
| 4041 | V_FUNCTION("crypto_stream_xchacha20_xor_ic", sn_crypto_stream_xchacha20_xor_ic); | |
| 4042 | V_FUNCTION("crypto_stream_xchacha20_xor_init", sn_crypto_stream_xchacha20_xor_wrap_init); | |
| 4043 | V_FUNCTION("crypto_stream_xchacha20_xor_update", sn_crypto_stream_xchacha20_xor_wrap_update); | |
| 4044 | V_FUNCTION("crypto_stream_xchacha20_xor_final", sn_crypto_stream_xchacha20_xor_wrap_final); | |
| 4045 | V_FUNCTION("crypto_stream_xchacha20", sn_crypto_stream_xchacha20); | |
| 4046 | V_UINT32("crypto_stream_xchacha20_xor_STATEBYTES", sizeof(sn_crypto_stream_xchacha20_xor_state)); | |
| 4047 | ||
| 4048 | V_FUNCTION("crypto_stream_salsa20", sn_crypto_stream_salsa20); | |
| 4049 | V_UINT32("crypto_stream_salsa20_KEYBYTES", crypto_stream_salsa20_KEYBYTES); | |
| 4050 | V_UINT32("crypto_stream_salsa20_NONCEBYTES", crypto_stream_salsa20_NONCEBYTES); | |
| 4051 | V_UINT64("crypto_stream_salsa20_MESSAGEBYTES_MAX", crypto_stream_salsa20_MESSAGEBYTES_MAX); | |
| 4052 | ||
| 4053 | V_FUNCTION("crypto_stream_salsa20_xor", sn_crypto_stream_salsa20_xor); | |
| 4054 | V_FUNCTION("crypto_stream_salsa20_xor_ic", sn_crypto_stream_salsa20_xor_ic); | |
| 4055 | V_FUNCTION("crypto_stream_salsa20_xor_init", sn_crypto_stream_salsa20_xor_wrap_init); | |
| 4056 | V_FUNCTION("crypto_stream_salsa20_xor_update", sn_crypto_stream_salsa20_xor_wrap_update); | |
| 4057 | V_FUNCTION("crypto_stream_salsa20_xor_final", sn_crypto_stream_salsa20_xor_wrap_final); | |
| 4058 | V_UINT32("crypto_stream_salsa20_xor_STATEBYTES", sizeof(sn_crypto_stream_salsa20_xor_state)); | |
| 4059 | ||
| 4060 | // extensions | |
| 4061 | ||
| 4062 | // tweak | |
| 4063 | ||
| 4064 | V_FUNCTION("extension_tweak_ed25519_base", sn_extension_tweak_ed25519_base); | |
| 4065 | V_FUNCTION("extension_tweak_ed25519_sign_detached", sn_extension_tweak_ed25519_sign_detached); | |
| 4066 | V_FUNCTION("extension_tweak_ed25519_sk_to_scalar", sn_extension_tweak_ed25519_sk_to_scalar); | |
| 4067 | V_FUNCTION("extension_tweak_ed25519_scalar", sn_extension_tweak_ed25519_scalar); | |
| 4068 | V_FUNCTION("extension_tweak_ed25519_pk", sn_extension_tweak_ed25519_pk); | |
| 4069 | V_FUNCTION("extension_tweak_ed25519_keypair", sn_extension_tweak_ed25519_keypair); | |
| 4070 | V_FUNCTION("extension_tweak_ed25519_scalar_add", sn_extension_tweak_ed25519_scalar_add); | |
| 4071 | V_FUNCTION("extension_tweak_ed25519_pk_add", sn_extension_tweak_ed25519_pk_add); | |
| 4072 | V_FUNCTION("extension_tweak_ed25519_keypair_add", sn_extension_tweak_ed25519_keypair_add); | |
| 4073 | V_UINT32("extension_tweak_ed25519_BYTES", sn__extension_tweak_ed25519_BYTES); | |
| 4074 | V_UINT32("extension_tweak_ed25519_SCALARBYTES", sn__extension_tweak_ed25519_SCALARBYTES); | |
| 4075 | ||
| 4076 | // pbkdf2 | |
| 4077 | ||
| 4078 | V_FUNCTION("extension_pbkdf2_sha512", sn_extension_pbkdf2_sha512); | |
| 4079 | V_FUNCTION("extension_pbkdf2_sha512_async", sn_extension_pbkdf2_sha512_async); | |
| 4080 | V_UINT32("extension_pbkdf2_sha512_SALTBYTES", sn__extension_pbkdf2_sha512_SALTBYTES); | |
| 4081 | V_UINT32("extension_pbkdf2_sha512_HASHBYTES", sn__extension_pbkdf2_sha512_HASHBYTES); | |
| 4082 | V_UINT32("extension_pbkdf2_sha512_ITERATIONS_MIN", sn__extension_pbkdf2_sha512_ITERATIONS_MIN); | |
| 4083 | V_UINT64("extension_pbkdf2_sha512_BYTES_MAX", sn__extension_pbkdf2_sha512_BYTES_MAX); | |
| 4084 | ||
| 4085 | #undef V_FUNCTION | |
| 4086 | #undef V_FUNCTION_NOSCOPE | |
| 4087 | #undef V_UINT32 | |
| 4088 | #undef V_UINT64 | |
| 4089 | #undef V_STRING | |
| 4090 | ||
| 4091 | return exports; | |
| 4092 | } | |
| 4093 | ||
| 4094 | BARE_MODULE(sodium_native, sodium_native_exports) |
| … | … | @@ -0,0 +1,2 @@ |
| 1 | require.addon = require('require-addon') | |
| 2 | module.exports = require.addon('.', __filename) |
| … | … | @@ -0,0 +1,90 @@ |
| 1 | /*- | |
| 2 | * Copyright 2005,2007,2009 Colin Percival | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 24 | * SUCH DAMAGE. | |
| 25 | */ | |
| 26 | ||
| 27 | /* | |
| 28 | * Adapted from libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf-sha256.c | |
| 29 | */ | |
| 30 | ||
| 31 | #include <string.h> | |
| 32 | #include <sodium.h> | |
| 33 | ||
| 34 | #include "pbkdf2.h" | |
| 35 | ||
| 36 | /** | |
| 37 | * pbkdf2_sha512(passwd, passwdlen, salt, saltlen, c, buf, dkLen): | |
| 38 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and | |
| 39 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). | |
| 40 | */ | |
| 41 | int | |
| 42 | sn__extension_pbkdf2_sha512(const unsigned char *passwd, size_t passwdlen, | |
| 43 | const unsigned char *salt, size_t saltlen, uint64_t c, | |
| 44 | unsigned char *buf, size_t dkLen) | |
| 45 | { | |
| 46 | crypto_auth_hmacsha512_state PShctx, hctx; | |
| 47 | size_t i; | |
| 48 | unsigned char ivec[4]; | |
| 49 | unsigned char U[64]; | |
| 50 | unsigned char T[64]; | |
| 51 | uint64_t j; | |
| 52 | unsigned int k; | |
| 53 | size_t clen; | |
| 54 | ||
| 55 | if (dkLen > sn__extension_pbkdf2_sha512_BYTES_MAX) { | |
| 56 | return -1; | |
| 57 | } | |
| 58 | ||
| 59 | crypto_auth_hmacsha512_init(&PShctx, passwd, passwdlen); | |
| 60 | crypto_auth_hmacsha512_update(&PShctx, salt, saltlen); | |
| 61 | ||
| 62 | for (i = 0; i * crypto_auth_hmacsha512_BYTES < dkLen; i++) { | |
| 63 | SN_PBKDF2_STORE32_BE(ivec, (uint32_t)(i + 1)); | |
| 64 | memcpy(&hctx, &PShctx, sizeof(crypto_auth_hmacsha512_state)); | |
| 65 | crypto_auth_hmacsha512_update(&hctx, ivec, 4); | |
| 66 | crypto_auth_hmacsha512_final(&hctx, U); | |
| 67 | ||
| 68 | memcpy(T, U, crypto_auth_hmacsha512_BYTES); | |
| 69 | /* LCOV_EXCL_START */ | |
| 70 | for (j = 2; j <= c; j++) { | |
| 71 | crypto_auth_hmacsha512_init(&hctx, passwd, passwdlen); | |
| 72 | crypto_auth_hmacsha512_update(&hctx, U, crypto_auth_hmacsha512_BYTES); | |
| 73 | crypto_auth_hmacsha512_final(&hctx, U); | |
| 74 | ||
| 75 | for (k = 0; k < crypto_auth_hmacsha512_BYTES; k++) { | |
| 76 | T[k] ^= U[k]; | |
| 77 | } | |
| 78 | } | |
| 79 | /* LCOV_EXCL_STOP */ | |
| 80 | ||
| 81 | clen = dkLen - i * crypto_auth_hmacsha512_BYTES; | |
| 82 | if (clen > crypto_auth_hmacsha512_BYTES) { | |
| 83 | clen = crypto_auth_hmacsha512_BYTES; | |
| 84 | } | |
| 85 | memcpy(&buf[i * crypto_auth_hmacsha512_BYTES], T, clen); | |
| 86 | } | |
| 87 | sodium_memzero((void *) &PShctx, sizeof PShctx); | |
| 88 | ||
| 89 | return 0; | |
| 90 | } |
| … | … | @@ -0,0 +1,60 @@ |
| 1 | /*- | |
| 2 | * Copyright 2005,2007,2009 Colin Percival | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 24 | * SUCH DAMAGE. | |
| 25 | * | |
| 26 | */ | |
| 27 | ||
| 28 | /* | |
| 29 | * Adapted from libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf-sha256.c | |
| 30 | */ | |
| 31 | #ifdef __cplusplus | |
| 32 | extern "C" { | |
| 33 | #endif | |
| 34 | ||
| 35 | #include <sodium.h> | |
| 36 | ||
| 37 | #define SN_PBKDF2_STORE32_BE(buf, n32) \ | |
| 38 | buf[0] = n32 >> 24 & 0xff; \ | |
| 39 | buf[1] = n32 >> 16 & 0xff; \ | |
| 40 | buf[2] = n32 >> 8 & 0xff; \ | |
| 41 | buf[3] = n32 >> 0 & 0xff; | |
| 42 | ||
| 43 | #define sn__extension_pbkdf2_sha512_SALTBYTES 16U | |
| 44 | ||
| 45 | #define sn__extension_pbkdf2_sha512_HASHBYTES crypto_hash_sha512_BYTES | |
| 46 | ||
| 47 | #define sn__extension_pbkdf2_sha512_ITERATIONS_MIN 1U | |
| 48 | ||
| 49 | #define sn__extension_pbkdf2_sha512_BYTES_MAX 0x3fffffffc0ULL | |
| 50 | ||
| 51 | /** | |
| 52 | * extension_pbkdf2_sha512(passwd, passwdlen, salt, saltlen, c, buf, dkLen): | |
| 53 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and | |
| 54 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). | |
| 55 | */ | |
| 56 | int sn__extension_pbkdf2_sha512(const unsigned char *, size_t, const unsigned char *, size_t, | |
| 57 | uint64_t, unsigned char *, size_t); | |
| 58 | #ifdef __cplusplus | |
| 59 | }; | |
| 60 | #endif |
| … | … | @@ -0,0 +1,206 @@ |
| 1 | #include "tweak.h" | |
| 2 | ||
| 3 | /* | |
| 4 | *EXPERIMENTAL API* | |
| 5 | ||
| 6 | This module is an experimental implementation of a key tweaking protocol | |
| 7 | over ed25519 keys. The signature algorithm has been reimplemented from | |
| 8 | libsodium, but the nonce generation algorithm is *non-standard*. | |
| 9 | ||
| 10 | Use at your own risk | |
| 11 | */ | |
| 12 | ||
| 13 | static void _extension_tweak_nonce (unsigned char *nonce, const unsigned char *n, | |
| 14 | const unsigned char *m, unsigned long long mlen) | |
| 15 | { | |
| 16 | // dom2(x, y) with x = 0 (not prehashed) and y = "crypto_tweak_ed25519" | |
| 17 | static const unsigned char TWEAK_PREFIX[32 + 2 + 20] = { | |
| 18 | 'S', 'i', 'g', 'E', 'd', '2', '5', '5', '1', '9', ' ', | |
| 19 | 'n', 'o', ' ', 'E', 'd', '2', '5', '5', '1', '9', ' ', | |
| 20 | 'c', 'o', 'l', 'l', 'i', 's', 'i', 'o', 'n', 's', 0, | |
| 21 | 20, 'c', 'r', 'y', 'p', 't', 'o', '_', 't', 'w', 'e', | |
| 22 | 'a', 'k', '_', 'e', 'd', '2', '5', '5', '1', '9' | |
| 23 | }; | |
| 24 | ||
| 25 | crypto_hash_sha512_state hs; | |
| 26 | ||
| 27 | crypto_hash_sha512_init(&hs); | |
| 28 | crypto_hash_sha512_update(&hs, TWEAK_PREFIX, sizeof TWEAK_PREFIX); | |
| 29 | crypto_hash_sha512_update(&hs, n, 32); | |
| 30 | crypto_hash_sha512_update(&hs, m, mlen); | |
| 31 | crypto_hash_sha512_final(&hs, nonce); | |
| 32 | } | |
| 33 | ||
| 34 | static inline void | |
| 35 | _crypto_sign_ed25519_clamp(unsigned char k[32]) | |
| 36 | { | |
| 37 | k[0] &= 248; | |
| 38 | k[31] &= 127; | |
| 39 | k[31] |= 64; | |
| 40 | } | |
| 41 | ||
| 42 | static void _extension_tweak_ed25519(unsigned char *q, unsigned char *n, | |
| 43 | const unsigned char *ns, unsigned long long nslen) | |
| 44 | { | |
| 45 | sodium_memzero(q, sizeof q); | |
| 46 | ||
| 47 | crypto_hash(n, ns, nslen); | |
| 48 | n[31] &= 127; // clear highest bit | |
| 49 | ||
| 50 | crypto_scalarmult_ed25519_base_noclamp(q, n); | |
| 51 | ||
| 52 | // hash tweak until we get a valid tweaked q | |
| 53 | while (crypto_core_ed25519_is_valid_point(q) != 1) { | |
| 54 | crypto_hash(n, n, 32); | |
| 55 | n[31] &= 127; // clear highest bit | |
| 56 | ||
| 57 | crypto_scalarmult_ed25519_base_noclamp(q, n); | |
| 58 | } | |
| 59 | } | |
| 60 | ||
| 61 | void sn__extension_tweak_ed25519_base(unsigned char *pk, unsigned char *scalar, | |
| 62 | const unsigned char *ns, unsigned long long nslen) | |
| 63 | { | |
| 64 | unsigned char n64[64]; | |
| 65 | ||
| 66 | _extension_tweak_ed25519(pk, n64, ns, nslen); | |
| 67 | ||
| 68 | SN_TWEAK_COPY_32(scalar, n64) | |
| 69 | } | |
| 70 | ||
| 71 | int sn__extension_tweak_ed25519_sign_detached(unsigned char *sig, unsigned long long *siglen_p, | |
| 72 | const unsigned char *m, unsigned long long mlen, | |
| 73 | const unsigned char *n, unsigned char *pk) | |
| 74 | { | |
| 75 | crypto_hash_sha512_state hs; | |
| 76 | ||
| 77 | unsigned char nonce[64]; | |
| 78 | unsigned char R[32]; | |
| 79 | unsigned char hram[64]; | |
| 80 | unsigned char _pk[32]; | |
| 81 | ||
| 82 | // check if pk was passed | |
| 83 | if (pk == NULL) { | |
| 84 | pk = _pk; | |
| 85 | ||
| 86 | // derive pk from scalar | |
| 87 | if (crypto_scalarmult_ed25519_base_noclamp(pk, n) != 0) { | |
| 88 | return -1; | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | _extension_tweak_nonce(nonce, n, m, mlen); | |
| 93 | crypto_core_ed25519_scalar_reduce(nonce, nonce); | |
| 94 | ||
| 95 | // R = G ^ nonce : curve point from nonce | |
| 96 | if (crypto_scalarmult_ed25519_base_noclamp(R, nonce) != 0) { | |
| 97 | return -1; | |
| 98 | } | |
| 99 | ||
| 100 | // generate challenge as h(ram) = hash(R, pk, message) | |
| 101 | crypto_hash_sha512_init(&hs); | |
| 102 | crypto_hash_sha512_update(&hs, R, 32); | |
| 103 | crypto_hash_sha512_update(&hs, pk, 32); | |
| 104 | crypto_hash_sha512_update(&hs, m, mlen); | |
| 105 | ||
| 106 | crypto_hash_sha512_final(&hs, hram); | |
| 107 | ||
| 108 | crypto_core_ed25519_scalar_reduce(hram, hram); | |
| 109 | ||
| 110 | // sig = nonce + n * h(ram) | |
| 111 | crypto_core_ed25519_scalar_mul(sig, hram, n); | |
| 112 | crypto_core_ed25519_scalar_add(sig + 32, nonce, sig); | |
| 113 | ||
| 114 | SN_TWEAK_COPY_32(sig, R) | |
| 115 | ||
| 116 | if (siglen_p != NULL) { | |
| 117 | *siglen_p = 64U; | |
| 118 | } | |
| 119 | ||
| 120 | return 0; | |
| 121 | } | |
| 122 | ||
| 123 | // tweak a secret key | |
| 124 | void sn__extension_tweak_ed25519_sk_to_scalar(unsigned char *n, const unsigned char *sk) | |
| 125 | { | |
| 126 | unsigned char n64[64]; | |
| 127 | ||
| 128 | // get sk scalar from seed, cf. crypto_sign_keypair_seed | |
| 129 | crypto_hash(n64, sk, 32); | |
| 130 | _crypto_sign_ed25519_clamp(n64); | |
| 131 | ||
| 132 | SN_TWEAK_COPY_32(n, n64) | |
| 133 | } | |
| 134 | ||
| 135 | // tweak a secret key | |
| 136 | void sn__extension_tweak_ed25519_scalar(unsigned char *scalar_out, | |
| 137 | const unsigned char *scalar, | |
| 138 | const unsigned char *ns, | |
| 139 | unsigned long long nslen) | |
| 140 | { | |
| 141 | unsigned char n[64]; | |
| 142 | unsigned char q[32]; | |
| 143 | ||
| 144 | _extension_tweak_ed25519(q, n, ns, nslen); | |
| 145 | crypto_core_ed25519_scalar_add(scalar_out, scalar, n); | |
| 146 | } | |
| 147 | ||
| 148 | // tweak a public key | |
| 149 | int sn__extension_tweak_ed25519_pk(unsigned char *tpk, | |
| 150 | const unsigned char *pk, | |
| 151 | const unsigned char *ns, | |
| 152 | unsigned long long nslen) | |
| 153 | { | |
| 154 | unsigned char n[64]; | |
| 155 | unsigned char q[32]; | |
| 156 | ||
| 157 | _extension_tweak_ed25519(q, n, ns, nslen); | |
| 158 | return crypto_core_ed25519_add(tpk, q, pk); | |
| 159 | } | |
| 160 | ||
| 161 | ||
| 162 | void sn__extension_tweak_ed25519_keypair(unsigned char *pk, unsigned char *scalar_out, | |
| 163 | unsigned char *scalar, const unsigned char *ns, | |
| 164 | unsigned long long nslen) | |
| 165 | { | |
| 166 | unsigned char n64[64]; | |
| 167 | ||
| 168 | crypto_hash(n64, ns, nslen); | |
| 169 | n64[31] &= 127; // clear highest bit | |
| 170 | ||
| 171 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, n64); | |
| 172 | crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 173 | ||
| 174 | // hash tweak until we get a valid tweaked point | |
| 175 | while (crypto_core_ed25519_is_valid_point(pk) != 1) { | |
| 176 | crypto_hash(n64, n64, 32); | |
| 177 | n64[31] &= 127; // clear highest bit | |
| 178 | ||
| 179 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, n64); | |
| 180 | crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 181 | } | |
| 182 | } | |
| 183 | ||
| 184 | // add tweak to scalar | |
| 185 | void sn__extension_tweak_ed25519_scalar_add(unsigned char *scalar_out, | |
| 186 | const unsigned char *scalar, | |
| 187 | const unsigned char *n) | |
| 188 | { | |
| 189 | crypto_core_ed25519_scalar_add(scalar_out, scalar, n); | |
| 190 | } | |
| 191 | ||
| 192 | // add tweak point to public key | |
| 193 | int sn__extension_tweak_ed25519_pk_add(unsigned char *tpk, | |
| 194 | const unsigned char *pk, | |
| 195 | const unsigned char *q) | |
| 196 | { | |
| 197 | return crypto_core_ed25519_add(tpk, pk, q); | |
| 198 | } | |
| 199 | ||
| 200 | ||
| 201 | int sn__extension_tweak_ed25519_keypair_add(unsigned char *pk, unsigned char *scalar_out, | |
| 202 | unsigned char *scalar, const unsigned char *tweak) | |
| 203 | { | |
| 204 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, tweak); | |
| 205 | return crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 206 | } |
| … | … | @@ -0,0 +1,62 @@ |
| 1 | #ifdef __cplusplus | |
| 2 | extern "C" { | |
| 3 | #endif | |
| 4 | ||
| 5 | #include <sodium.h> | |
| 6 | ||
| 7 | // copy 32 bytes using int64_t pointers | |
| 8 | #define SN_TWEAK_COPY_32(a, b) \ | |
| 9 | { \ | |
| 10 | long long *dst = (long long *) a; \ | |
| 11 | long long *src = (long long *) b; \ | |
| 12 | dst[0] = src[0]; \ | |
| 13 | dst[1] = src[1]; \ | |
| 14 | dst[2] = src[2]; \ | |
| 15 | dst[3] = src[3]; \ | |
| 16 | } | |
| 17 | ||
| 18 | #define sn__extension_tweak_ed25519_BYTES crypto_sign_ed25519_PUBLICKEYBYTES | |
| 19 | ||
| 20 | #define sn__extension_tweak_ed25519_SCALARBYTES crypto_scalarmult_ed25519_SCALARBYTES | |
| 21 | ||
| 22 | int sn__extension_tweak_ed25519_sign_detached(unsigned char *sig, unsigned long long *siglen_p, | |
| 23 | const unsigned char *m, unsigned long long mlen, | |
| 24 | const unsigned char *n, unsigned char *pk); | |
| 25 | ||
| 26 | void sn__extension_tweak_ed25519_base(unsigned char *pk, unsigned char *scalar, | |
| 27 | const unsigned char *ns, unsigned long long nslen); | |
| 28 | ||
| 29 | void sn__extension_tweak_ed25519_sk_to_scalar(unsigned char *scalar, const unsigned char *sk); | |
| 30 | ||
| 31 | // tweak a secret key | |
| 32 | void sn__extension_tweak_ed25519_scalar(unsigned char *scalar_out, | |
| 33 | const unsigned char *scalar, | |
| 34 | const unsigned char *ns, | |
| 35 | unsigned long long nslen); | |
| 36 | ||
| 37 | // tweak a public key | |
| 38 | int sn__extension_tweak_ed25519_pk(unsigned char *tpk, | |
| 39 | const unsigned char *pk, | |
| 40 | const unsigned char *ns, | |
| 41 | unsigned long long nslen); | |
| 42 | ||
| 43 | void sn__extension_tweak_ed25519_keypair(unsigned char *pk, unsigned char *scalar_out, | |
| 44 | unsigned char *scalar, const unsigned char *ns, | |
| 45 | unsigned long long nslen); | |
| 46 | ||
| 47 | // add tweak scalar to private key | |
| 48 | void sn__extension_tweak_ed25519_scalar_add(unsigned char *scalar_out, | |
| 49 | const unsigned char *scalar, | |
| 50 | const unsigned char *n); | |
| 51 | ||
| 52 | // add tweak point to public key | |
| 53 | int sn__extension_tweak_ed25519_pk_add(unsigned char *tpk, | |
| 54 | const unsigned char *pk, | |
| 55 | const unsigned char *q); | |
| 56 | ||
| 57 | int sn__extension_tweak_ed25519_keypair_add(unsigned char *pk, unsigned char *scalar_out, | |
| 58 | unsigned char *scalar, const unsigned char *tweak); | |
| 59 | ||
| 60 | #ifdef __cplusplus | |
| 61 | }; | |
| 62 | #endif |
| … | … | @@ -0,0 +1,3231 @@ |
| 1 | const assert = require('assert') | |
| 2 | const binding = require('./binding') | |
| 3 | const { isNode } = require('which-runtime') | |
| 4 | ||
| 5 | const OPTIONAL = Buffer.from(new ArrayBuffer(0)) | |
| 6 | ||
| 7 | module.exports = exports = { ...binding } | |
| 8 | ||
| 9 | // memory | |
| 10 | ||
| 11 | exports.sodium_memzero = function (buf) { | |
| 12 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 13 | ||
| 14 | binding.sodium_memzero(buf) | |
| 15 | } | |
| 16 | ||
| 17 | exports.sodium_mlock = function (buf) { | |
| 18 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 19 | ||
| 20 | const res = binding.sodium_mlock(buf) | |
| 21 | ||
| 22 | if (res !== 0) throw new Error('memory lock failed') | |
| 23 | } | |
| 24 | ||
| 25 | exports.sodium_munlock = function (buf) { | |
| 26 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 27 | ||
| 28 | const res = binding.sodium_munlock(buf) | |
| 29 | ||
| 30 | if (res !== 0) throw new Error('memory unlock failed') | |
| 31 | } | |
| 32 | ||
| 33 | exports.sodium_malloc = function (size) { | |
| 34 | assert(size >= 0, 'invalid size') | |
| 35 | ||
| 36 | const buf = Buffer.from(binding.sodium_malloc(size)) | |
| 37 | buf.secure = true | |
| 38 | return buf | |
| 39 | } | |
| 40 | ||
| 41 | exports.sodium_free = function (buf) { | |
| 42 | if (!buf || !buf.secure) return | |
| 43 | ||
| 44 | binding.sodium_free(buf.buffer) | |
| 45 | } | |
| 46 | ||
| 47 | exports.sodium_mprotect_noaccess = function (buf) { | |
| 48 | const res = binding.sodium_mprotect_noaccess(buf.buffer) | |
| 49 | ||
| 50 | if (res !== 0) throw new Error('failed to lock buffer') | |
| 51 | } | |
| 52 | ||
| 53 | exports.sodium_mprotect_readonly = function (buf) { | |
| 54 | const res = binding.sodium_mprotect_readonly(buf.buffer) | |
| 55 | ||
| 56 | if (res !== 0) throw new Error('failed to unlock buffer') | |
| 57 | } | |
| 58 | ||
| 59 | exports.sodium_mprotect_readwrite = function (buf) { | |
| 60 | const res = binding.sodium_mprotect_readwrite(buf.buffer) | |
| 61 | ||
| 62 | if (res !== 0) throw new Error('failed to unlock buffer') | |
| 63 | } | |
| 64 | ||
| 65 | // crypto_randombytes | |
| 66 | ||
| 67 | exports.randombytes_buf = function (buffer) { | |
| 68 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 69 | ||
| 70 | binding.randombytes_buf(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 71 | } | |
| 72 | ||
| 73 | exports.randombytes_buf_deterministic = function (buffer, seed) { | |
| 74 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 75 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 76 | assert( | |
| 77 | seed.byteLength === binding.randombytes_SEEDBYTES, | |
| 78 | "seed must be 'randombytes_SEEDBYTES' bytes" | |
| 79 | ) | |
| 80 | ||
| 81 | binding.randombytes_buf_deterministic( | |
| 82 | buffer.buffer, | |
| 83 | buffer.byteOffset, | |
| 84 | buffer.byteLength, | |
| 85 | ||
| 86 | seed.buffer, | |
| 87 | seed.byteOffset, | |
| 88 | seed.byteLength | |
| 89 | ) | |
| 90 | } | |
| 91 | ||
| 92 | // sodium_helpers | |
| 93 | ||
| 94 | exports.sodium_memcmp = function (a, b) { | |
| 95 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 96 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 97 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 98 | ||
| 99 | return binding.sodium_memcmp(a, b) | |
| 100 | } | |
| 101 | ||
| 102 | exports.sodium_add = function (a, b) { | |
| 103 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 104 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 105 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 106 | ||
| 107 | binding.sodium_add(a, b) | |
| 108 | } | |
| 109 | ||
| 110 | exports.sodium_sub = function (a, b) { | |
| 111 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 112 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 113 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 114 | ||
| 115 | binding.sodium_sub(a, b) | |
| 116 | } | |
| 117 | ||
| 118 | exports.sodium_compare = function (a, b) { | |
| 119 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 120 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 121 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 122 | ||
| 123 | return binding.sodium_compare(a, b) | |
| 124 | } | |
| 125 | ||
| 126 | exports.sodium_is_zero = function (buffer, length) { | |
| 127 | if (length === undefined) length = buffer.byteLength | |
| 128 | ||
| 129 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 130 | assert(length >= 0 && length <= buffer.byteLength, 'invalid length') | |
| 131 | ||
| 132 | return binding.sodium_is_zero(buffer, length) | |
| 133 | } | |
| 134 | ||
| 135 | exports.sodium_pad = function (buffer, unpaddedBuflen, blockSize) { | |
| 136 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 137 | assert(unpaddedBuflen <= buffer.byteLength, 'unpadded length cannot exceed buffer length') | |
| 138 | assert(blockSize <= buffer.byteLength, 'block size cannot exceed buffer length') | |
| 139 | assert(blockSize >= 1, 'block size must be at least 1 byte') | |
| 140 | assert( | |
| 141 | buffer.byteLength >= unpaddedBuflen + (blockSize - (unpaddedBuflen % blockSize)), | |
| 142 | 'buf not long enough' | |
| 143 | ) | |
| 144 | ||
| 145 | return binding.sodium_pad(buffer, unpaddedBuflen, blockSize) | |
| 146 | } | |
| 147 | ||
| 148 | exports.sodium_unpad = function (buffer, paddedBuflen, blockSize) { | |
| 149 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 150 | assert(paddedBuflen <= buffer.byteLength, 'unpadded length cannot exceed buffer length') | |
| 151 | assert(blockSize <= buffer.byteLength, 'block size cannot exceed buffer length') | |
| 152 | assert(blockSize >= 1, 'block size must be at least 1 byte') | |
| 153 | ||
| 154 | return binding.sodium_unpad(buffer, paddedBuflen, blockSize) | |
| 155 | } | |
| 156 | ||
| 157 | // crypto_sign | |
| 158 | ||
| 159 | exports.crypto_sign_keypair = function (pk, sk) { | |
| 160 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 161 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 162 | assert( | |
| 163 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 164 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 165 | ) | |
| 166 | assert( | |
| 167 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 168 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 169 | ) | |
| 170 | ||
| 171 | const res = binding.crypto_sign_keypair(pk, sk) | |
| 172 | ||
| 173 | if (res !== 0) throw new Error('status: ' + res) | |
| 174 | } | |
| 175 | ||
| 176 | exports.crypto_sign_seed_keypair = function (pk, sk, seed) { | |
| 177 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 178 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 179 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 180 | assert( | |
| 181 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 182 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 183 | ) | |
| 184 | assert( | |
| 185 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 186 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 187 | ) | |
| 188 | assert( | |
| 189 | seed.byteLength === binding.crypto_sign_SEEDBYTES, | |
| 190 | "seed must be 'crypto_sign_SEEDBYTES' bytes" | |
| 191 | ) | |
| 192 | ||
| 193 | const res = binding.crypto_sign_seed_keypair(pk, sk, seed) | |
| 194 | ||
| 195 | if (res !== 0) throw new Error('status: ' + res) | |
| 196 | } | |
| 197 | ||
| 198 | exports.crypto_sign = function (sm, m, sk) { | |
| 199 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 200 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 201 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 202 | assert( | |
| 203 | sm.byteLength === binding.crypto_sign_BYTES + m.byteLength, | |
| 204 | "sm must be 'm.byteLength + crypto_sign_BYTES' bytes" | |
| 205 | ) | |
| 206 | assert( | |
| 207 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 208 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 209 | ) | |
| 210 | ||
| 211 | const res = binding.crypto_sign(sm, m, sk) | |
| 212 | ||
| 213 | if (res !== 0) throw new Error('status: ' + res) | |
| 214 | } | |
| 215 | ||
| 216 | exports.crypto_sign_open = function (m, sm, pk) { | |
| 217 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 218 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 219 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 220 | assert( | |
| 221 | sm.byteLength >= binding.crypto_sign_BYTES, | |
| 222 | "sm must be at least 'crypto_sign_BYTES' bytes" | |
| 223 | ) | |
| 224 | assert( | |
| 225 | m.byteLength === sm.byteLength - binding.crypto_sign_BYTES, | |
| 226 | "m must be 'sm.byteLength - crypto_sign_BYTES' bytes" | |
| 227 | ) | |
| 228 | assert( | |
| 229 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 230 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 231 | ) | |
| 232 | ||
| 233 | const res = binding.crypto_sign_open(m, sm, pk) | |
| 234 | ||
| 235 | if (res !== 0) throw new Error('status: ' + res) | |
| 236 | } | |
| 237 | ||
| 238 | exports.crypto_sign_open = function (m, sm, pk) { | |
| 239 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 240 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 241 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 242 | assert( | |
| 243 | sm.byteLength >= binding.crypto_sign_BYTES, | |
| 244 | "sm must be at least 'crypto_sign_BYTES' bytes" | |
| 245 | ) | |
| 246 | assert( | |
| 247 | m.byteLength === sm.byteLength - binding.crypto_sign_BYTES, | |
| 248 | "m must be 'sm.byteLength - crypto_sign_BYTES' bytes" | |
| 249 | ) | |
| 250 | assert( | |
| 251 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 252 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 253 | ) | |
| 254 | ||
| 255 | return binding.crypto_sign_open(m, sm, pk) | |
| 256 | } | |
| 257 | ||
| 258 | exports.crypto_sign_detached = function (sig, m, sk) { | |
| 259 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 260 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 261 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 262 | assert(sig.byteLength === binding.crypto_sign_BYTES, "sig must be 'crypto_sign_BYTES' bytes") | |
| 263 | assert( | |
| 264 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 265 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 266 | ) | |
| 267 | ||
| 268 | const res = binding.crypto_sign_detached(sig, m, sk) | |
| 269 | ||
| 270 | if (res !== 0) throw new Error('status: ' + res) | |
| 271 | } | |
| 272 | ||
| 273 | exports.crypto_sign_verify_detached = function (sig, m, pk) { | |
| 274 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 275 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 276 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 277 | assert( | |
| 278 | sig.byteLength >= binding.crypto_sign_BYTES, | |
| 279 | "sig must be at least 'crypto_sign_BYTES' bytes" | |
| 280 | ) | |
| 281 | assert( | |
| 282 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 283 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 284 | ) | |
| 285 | ||
| 286 | return binding.crypto_sign_verify_detached( | |
| 287 | sig.buffer, | |
| 288 | sig.byteOffset, | |
| 289 | sig.byteLength, | |
| 290 | ||
| 291 | m.buffer, | |
| 292 | m.byteOffset, | |
| 293 | m.byteLength, | |
| 294 | ||
| 295 | pk.buffer, | |
| 296 | pk.byteOffset, | |
| 297 | pk.byteLength | |
| 298 | ) | |
| 299 | } | |
| 300 | ||
| 301 | exports.crypto_sign_ed25519_sk_to_pk = function (pk, sk) { | |
| 302 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 303 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 304 | assert( | |
| 305 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 306 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 307 | ) | |
| 308 | assert( | |
| 309 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 310 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 311 | ) | |
| 312 | ||
| 313 | const res = binding.crypto_sign_ed25519_sk_to_pk(pk, sk) | |
| 314 | ||
| 315 | if (res !== 0) throw new Error('status: ' + res) | |
| 316 | } | |
| 317 | ||
| 318 | exports.crypto_sign_ed25519_pk_to_curve25519 = function (x25519pk, ed25519pk) { | |
| 319 | assert(ArrayBuffer.isView(x25519pk), 'x25519pk must be a typed array') | |
| 320 | assert(ArrayBuffer.isView(ed25519pk), 'ed25519pk must be a typed array') | |
| 321 | assert( | |
| 322 | x25519pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 323 | "x25519pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 324 | ) | |
| 325 | assert( | |
| 326 | ed25519pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 327 | "ed25519pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 328 | ) | |
| 329 | ||
| 330 | const res = binding.crypto_sign_ed25519_pk_to_curve25519(x25519pk, ed25519pk) | |
| 331 | ||
| 332 | if (res !== 0) throw new Error('status: ' + res) | |
| 333 | } | |
| 334 | ||
| 335 | exports.crypto_sign_ed25519_sk_to_curve25519 = function (x25519sk, ed25519sk) { | |
| 336 | assert(ArrayBuffer.isView(x25519sk), 'x25519sk must be a typed array') | |
| 337 | assert(ArrayBuffer.isView(ed25519sk), 'ed25519sk must be a typed array') | |
| 338 | assert( | |
| 339 | x25519sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 340 | "x25519sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 341 | ) | |
| 342 | ||
| 343 | const edLen = ed25519sk.byteLength | |
| 344 | ||
| 345 | assert( | |
| 346 | edLen === binding.crypto_sign_SECRETKEYBYTES || edLen === binding.crypto_box_SECRETKEYBYTES, | |
| 347 | "ed25519sk must be 'crypto_sign_SECRETKEYBYTES' or 'crypto_sign_SECRETKEYBYTES - crypto_sign_PUBLICKEYBYTES' bytes" | |
| 348 | ) | |
| 349 | ||
| 350 | const res = binding.crypto_sign_ed25519_sk_to_curve25519(x25519sk, ed25519sk) | |
| 351 | ||
| 352 | if (res !== 0) throw new Error('status: ' + res) | |
| 353 | } | |
| 354 | ||
| 355 | // crypto_box | |
| 356 | ||
| 357 | exports.crypto_box_keypair = function (pk, sk) { | |
| 358 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 359 | assert( | |
| 360 | pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 361 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 362 | ) | |
| 363 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 364 | assert( | |
| 365 | sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 366 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 367 | ) | |
| 368 | ||
| 369 | const res = binding.crypto_box_keypair(pk, sk) | |
| 370 | ||
| 371 | if (res !== 0) throw new Error('status: ' + res) | |
| 372 | } | |
| 373 | ||
| 374 | exports.crypto_box_seed_keypair = function (pk, sk, seed) { | |
| 375 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 376 | assert( | |
| 377 | pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 378 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 379 | ) | |
| 380 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 381 | assert( | |
| 382 | sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 383 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 384 | ) | |
| 385 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 386 | assert( | |
| 387 | seed.byteLength === binding.crypto_box_SEEDBYTES, | |
| 388 | "seed must be 'crypto_box_SEEDBYTES' bytes" | |
| 389 | ) | |
| 390 | ||
| 391 | const res = binding.crypto_box_seed_keypair(pk, sk, seed) | |
| 392 | ||
| 393 | if (res !== 0) throw new Error('status: ' + res) | |
| 394 | } | |
| 395 | ||
| 396 | exports.crypto_box_easy = function (c, m, n, pk, sk) { | |
| 397 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 398 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 399 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 400 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 401 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 402 | assert( | |
| 403 | c.byteLength === m.byteLength + exports.crypto_box_MACBYTES, | |
| 404 | "c must be 'm.byteLength + crypto_box_MACBYTES' bytes" | |
| 405 | ) | |
| 406 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 407 | assert( | |
| 408 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 409 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 410 | ) | |
| 411 | assert( | |
| 412 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 413 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 414 | ) | |
| 415 | ||
| 416 | const res = binding.crypto_box_easy(c, m, n, pk, sk) | |
| 417 | ||
| 418 | if (res !== 0) throw new Error('status: ' + res) | |
| 419 | } | |
| 420 | ||
| 421 | exports.crypto_box_detached = function (c, mac, m, n, pk, sk) { | |
| 422 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 423 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 424 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 425 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 426 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 427 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 428 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 429 | assert(mac.byteLength === exports.crypto_box_MACBYTES, "mac must be 'crypto_box_MACBYTES' bytes") | |
| 430 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 431 | assert( | |
| 432 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 433 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 434 | ) | |
| 435 | assert( | |
| 436 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 437 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 438 | ) | |
| 439 | ||
| 440 | const res = binding.crypto_box_detached(c, mac, m, n, pk, sk) | |
| 441 | ||
| 442 | if (res !== 0) throw new Error('status: ' + res) | |
| 443 | } | |
| 444 | ||
| 445 | exports.crypto_box_open_easy = function (m, c, n, pk, sk) { | |
| 446 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 447 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 448 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 449 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 450 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 451 | assert( | |
| 452 | c.byteLength >= exports.crypto_box_MACBYTES, | |
| 453 | "c must be at least 'crypto_box_MACBYTES' bytes" | |
| 454 | ) | |
| 455 | assert( | |
| 456 | m.byteLength === c.byteLength - exports.crypto_box_MACBYTES, | |
| 457 | "m must be 'c.byteLength - crypto_box_MACBYTES' bytes" | |
| 458 | ) | |
| 459 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 460 | assert( | |
| 461 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 462 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 463 | ) | |
| 464 | assert( | |
| 465 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 466 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 467 | ) | |
| 468 | ||
| 469 | return binding.crypto_box_open_easy(m, c, n, pk, sk) | |
| 470 | } | |
| 471 | ||
| 472 | exports.crypto_box_open_detached = function (m, c, mac, n, pk, sk) { | |
| 473 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 474 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 475 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 476 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 477 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 478 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 479 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 480 | assert(mac.byteLength === exports.crypto_box_MACBYTES, "mac must be 'crypto_box_MACBYTES' bytes") | |
| 481 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 482 | assert( | |
| 483 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 484 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 485 | ) | |
| 486 | assert( | |
| 487 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 488 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 489 | ) | |
| 490 | ||
| 491 | return binding.crypto_box_open_detached(m, c, mac, n, pk, sk) | |
| 492 | } | |
| 493 | ||
| 494 | exports.crypto_box_seal = function (c, m, pk) { | |
| 495 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 496 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 497 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 498 | assert( | |
| 499 | c.byteLength === m.byteLength + exports.crypto_box_SEALBYTES, | |
| 500 | "c must be 'm.byteLength + crypto_box_SEALBYTES' bytes" | |
| 501 | ) | |
| 502 | assert( | |
| 503 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 504 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 505 | ) | |
| 506 | ||
| 507 | const res = binding.crypto_box_seal(c, m, pk) | |
| 508 | ||
| 509 | if (res !== 0) throw new Error('status: ' + res) | |
| 510 | } | |
| 511 | ||
| 512 | exports.crypto_box_seal_open = function (m, c, pk, sk) { | |
| 513 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 514 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 515 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 516 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 517 | assert( | |
| 518 | c.byteLength >= exports.crypto_box_SEALBYTES, | |
| 519 | "c must be at least 'crypto_box_SEALBYTES' bytes" | |
| 520 | ) | |
| 521 | assert( | |
| 522 | m.byteLength === c.byteLength - exports.crypto_box_SEALBYTES, | |
| 523 | "m must be 'c.byteLength - crypto_box_SEALBYTES' bytes" | |
| 524 | ) | |
| 525 | assert( | |
| 526 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 527 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 528 | ) | |
| 529 | assert( | |
| 530 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 531 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 532 | ) | |
| 533 | ||
| 534 | return binding.crypto_box_seal_open( | |
| 535 | m.buffer, | |
| 536 | m.byteOffset, | |
| 537 | m.byteLength, | |
| 538 | ||
| 539 | c.buffer, | |
| 540 | c.byteOffset, | |
| 541 | c.byteLength, | |
| 542 | ||
| 543 | pk.buffer, | |
| 544 | pk.byteOffset, | |
| 545 | pk.byteLength, | |
| 546 | ||
| 547 | sk.buffer, | |
| 548 | sk.byteOffset, | |
| 549 | sk.byteLength | |
| 550 | ) | |
| 551 | } | |
| 552 | ||
| 553 | // crypto_secretbox | |
| 554 | ||
| 555 | exports.crypto_secretbox_easy = function (c, m, n, k) { | |
| 556 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 557 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 558 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 559 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 560 | assert( | |
| 561 | c.byteLength === m.byteLength + binding.crypto_secretbox_MACBYTES, | |
| 562 | "c must be 'm.byteLength + crypto_secretbox_MACBYTES' bytes" | |
| 563 | ) | |
| 564 | assert( | |
| 565 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 566 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 567 | ) | |
| 568 | assert( | |
| 569 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 570 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 571 | ) | |
| 572 | ||
| 573 | const res = binding.crypto_secretbox_easy(c, m, n, k) | |
| 574 | ||
| 575 | if (res !== 0) throw new Error('status: ' + res) | |
| 576 | } | |
| 577 | ||
| 578 | exports.crypto_secretbox_open_easy = function (m, c, n, k) { | |
| 579 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 580 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 581 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 582 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 583 | assert( | |
| 584 | c.byteLength >= binding.crypto_secretbox_MACBYTES, | |
| 585 | "c must be at least 'crypto_secretbox_MACBYTES' bytes" | |
| 586 | ) | |
| 587 | assert( | |
| 588 | m.byteLength === c.byteLength - binding.crypto_secretbox_MACBYTES, | |
| 589 | "m must be 'c.byteLength - crypto_secretbox_MACBYTES' bytes" | |
| 590 | ) | |
| 591 | assert( | |
| 592 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 593 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 594 | ) | |
| 595 | assert( | |
| 596 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 597 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 598 | ) | |
| 599 | ||
| 600 | return binding.crypto_secretbox_open_easy(m, c, n, k) | |
| 601 | } | |
| 602 | ||
| 603 | exports.crypto_secretbox_detached = function (c, mac, m, n, k) { | |
| 604 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 605 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 606 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 607 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 608 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 609 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 610 | assert( | |
| 611 | mac.byteLength === binding.crypto_secretbox_MACBYTES, | |
| 612 | "mac must be 'crypto_secretbox_MACBYTES' bytes" | |
| 613 | ) | |
| 614 | assert( | |
| 615 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 616 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 617 | ) | |
| 618 | assert( | |
| 619 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 620 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 621 | ) | |
| 622 | ||
| 623 | const res = binding.crypto_secretbox_detached(c, mac, m, n, k) | |
| 624 | ||
| 625 | if (res !== 0) throw new Error('status: ' + res) | |
| 626 | } | |
| 627 | ||
| 628 | exports.crypto_secretbox_open_detached = function (m, c, mac, n, k) { | |
| 629 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 630 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 631 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 632 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 633 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 634 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 635 | assert( | |
| 636 | mac.byteLength === binding.crypto_secretbox_MACBYTES, | |
| 637 | "mac must be 'crypto_secretbox_MACBYTES' bytes" | |
| 638 | ) | |
| 639 | assert( | |
| 640 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 641 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 642 | ) | |
| 643 | assert( | |
| 644 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 645 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 646 | ) | |
| 647 | ||
| 648 | return binding.crypto_secretbox_open_detached(m, c, mac, n, k) | |
| 649 | } | |
| 650 | ||
| 651 | // crypto_generichash | |
| 652 | ||
| 653 | exports.crypto_generichash = function (output, input, key) { | |
| 654 | if (!key) key = OPTIONAL | |
| 655 | ||
| 656 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 657 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 658 | assert( | |
| 659 | output.byteLength >= binding.crypto_generichash_BYTES_MIN && | |
| 660 | output.byteLength <= binding.crypto_generichash_BYTES_MAX, | |
| 661 | 'output must be between crypto_generichash_BYTES_MIN and crypto_generichash_BYTES_MAX bytes' | |
| 662 | ) | |
| 663 | ||
| 664 | if (key !== OPTIONAL) { | |
| 665 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 666 | assert( | |
| 667 | key.byteLength >= binding.crypto_generichash_KEYBYTES_MIN && | |
| 668 | key.byteLength <= binding.crypto_generichash_KEYBYTES_MAX, | |
| 669 | 'key must be between crypto_generichash_KEYBYTES_MIN and crypto_generichash_KEYBYTES_MAX bytes' | |
| 670 | ) | |
| 671 | } | |
| 672 | ||
| 673 | const res = binding.crypto_generichash( | |
| 674 | output.buffer, | |
| 675 | output.byteOffset, | |
| 676 | output.byteLength, | |
| 677 | ||
| 678 | input.buffer, | |
| 679 | input.byteOffset, | |
| 680 | input.byteLength, | |
| 681 | ||
| 682 | key.buffer, | |
| 683 | key.byteOffset, | |
| 684 | key.byteLength | |
| 685 | ) | |
| 686 | ||
| 687 | if (res !== 0) throw new Error('status: ' + res) | |
| 688 | } | |
| 689 | ||
| 690 | exports.crypto_generichash_batch = function (output, batch, key) { | |
| 691 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 692 | ||
| 693 | if (isNode || batch.length < 4) { | |
| 694 | const res = binding.crypto_generichash_batch(output, batch, !!key, key || OPTIONAL) | |
| 695 | if (res !== 0) throw new Error('status: ' + res) | |
| 696 | } else { | |
| 697 | const state = Buffer.alloc(binding.crypto_generichash_STATEBYTES) | |
| 698 | ||
| 699 | exports.crypto_generichash_init(state, key, output.byteLength) | |
| 700 | ||
| 701 | for (const buf of batch) { | |
| 702 | exports.crypto_generichash_update(state, buf) | |
| 703 | } | |
| 704 | ||
| 705 | exports.crypto_generichash_final(state, output) | |
| 706 | } | |
| 707 | } | |
| 708 | ||
| 709 | exports.crypto_generichash_keygen = function (key) { | |
| 710 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 711 | assert( | |
| 712 | key.byteLength === binding.crypto_generichash_KEYBYTES, | |
| 713 | "key must be 'crypto_generichash_KEYBYTES' bytes" | |
| 714 | ) | |
| 715 | ||
| 716 | const res = binding.crypto_generichash_keygen(key.buffer, key.byteOffset, key.byteLength) | |
| 717 | ||
| 718 | if (res !== 0) throw new Error('status: ' + res) | |
| 719 | } | |
| 720 | ||
| 721 | exports.crypto_generichash_init = function (state, key, outputLength) { | |
| 722 | if (!key) key = OPTIONAL | |
| 723 | ||
| 724 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 725 | assert( | |
| 726 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 727 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 728 | ) | |
| 729 | ||
| 730 | const res = binding.crypto_generichash_init( | |
| 731 | state.buffer, | |
| 732 | state.byteOffset, | |
| 733 | state.byteLength, | |
| 734 | ||
| 735 | key.buffer, | |
| 736 | key.byteOffset, | |
| 737 | key.byteLength, | |
| 738 | ||
| 739 | outputLength | |
| 740 | ) | |
| 741 | ||
| 742 | if (res !== 0) throw new Error('status: ' + res) | |
| 743 | } | |
| 744 | ||
| 745 | exports.crypto_generichash_update = function (state, input) { | |
| 746 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 747 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 748 | assert( | |
| 749 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 750 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 751 | ) | |
| 752 | ||
| 753 | const res = binding.crypto_generichash_update( | |
| 754 | state.buffer, | |
| 755 | state.byteOffset, | |
| 756 | state.byteLength, | |
| 757 | ||
| 758 | input.buffer, | |
| 759 | input.byteOffset, | |
| 760 | input.byteLength | |
| 761 | ) | |
| 762 | ||
| 763 | if (res !== 0) throw new Error('status: ' + res) | |
| 764 | } | |
| 765 | ||
| 766 | exports.crypto_generichash_final = function (state, output) { | |
| 767 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 768 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 769 | assert( | |
| 770 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 771 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 772 | ) | |
| 773 | ||
| 774 | const res = binding.crypto_generichash_final( | |
| 775 | state.buffer, | |
| 776 | state.byteOffset, | |
| 777 | state.byteLength, | |
| 778 | ||
| 779 | output.buffer, | |
| 780 | output.byteOffset, | |
| 781 | output.byteLength | |
| 782 | ) | |
| 783 | ||
| 784 | if (res !== 0) throw new Error('status: ' + res) | |
| 785 | } | |
| 786 | ||
| 787 | // secretstream | |
| 788 | ||
| 789 | exports.crypto_secretstream_xchacha20poly1305_keygen = function (k) { | |
| 790 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 791 | assert( | |
| 792 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 793 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 794 | ) | |
| 795 | ||
| 796 | binding.crypto_secretstream_xchacha20poly1305_keygen(k.buffer, k.byteOffset, k.byteLength) | |
| 797 | } | |
| 798 | ||
| 799 | exports.crypto_secretstream_xchacha20poly1305_init_push = function (state, header, k) { | |
| 800 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 801 | assert(ArrayBuffer.isView(header), 'header must be a typed array') | |
| 802 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 803 | assert( | |
| 804 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 805 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 806 | ) | |
| 807 | assert( | |
| 808 | header.byteLength === binding.crypto_secretstream_xchacha20poly1305_HEADERBYTES, | |
| 809 | "header must be 'crypto_secretstream_xchacha20poly1305_HEADERBYTES' bytes" | |
| 810 | ) | |
| 811 | assert( | |
| 812 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 813 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 814 | ) | |
| 815 | ||
| 816 | const res = binding.crypto_secretstream_xchacha20poly1305_init_push( | |
| 817 | state.buffer, | |
| 818 | state.byteOffset, | |
| 819 | state.byteLength, | |
| 820 | ||
| 821 | header.buffer, | |
| 822 | header.byteOffset, | |
| 823 | header.byteLength, | |
| 824 | ||
| 825 | k.buffer, | |
| 826 | k.byteOffset, | |
| 827 | k.byteLength | |
| 828 | ) | |
| 829 | ||
| 830 | if (res !== 0) throw new Error('status: ' + res) | |
| 831 | } | |
| 832 | ||
| 833 | exports.crypto_secretstream_xchacha20poly1305_init_pull = function (state, header, k) { | |
| 834 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 835 | assert(ArrayBuffer.isView(header), 'header must be a typed array') | |
| 836 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 837 | assert( | |
| 838 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 839 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 840 | ) | |
| 841 | assert( | |
| 842 | header.byteLength === binding.crypto_secretstream_xchacha20poly1305_HEADERBYTES, | |
| 843 | "header must be 'crypto_secretstream_xchacha20poly1305_HEADERBYTES' bytes" | |
| 844 | ) | |
| 845 | assert( | |
| 846 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 847 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 848 | ) | |
| 849 | ||
| 850 | const res = binding.crypto_secretstream_xchacha20poly1305_init_pull( | |
| 851 | state.buffer, | |
| 852 | state.byteOffset, | |
| 853 | state.byteLength, | |
| 854 | ||
| 855 | header.buffer, | |
| 856 | header.byteOffset, | |
| 857 | header.byteLength, | |
| 858 | ||
| 859 | k.buffer, | |
| 860 | k.byteOffset, | |
| 861 | k.byteLength | |
| 862 | ) | |
| 863 | ||
| 864 | if (res !== 0) throw new Error('status: ' + res) | |
| 865 | } | |
| 866 | ||
| 867 | exports.crypto_secretstream_xchacha20poly1305_push = function (state, c, m, ad, tag) { | |
| 868 | if (!ad) ad = OPTIONAL | |
| 869 | ||
| 870 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 871 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 872 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 873 | assert( | |
| 874 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 875 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 876 | ) | |
| 877 | assert( | |
| 878 | c.byteLength === m.byteLength + binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 879 | "c must be 'm.byteLength + crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 880 | ) | |
| 881 | ||
| 882 | const res = binding.crypto_secretstream_xchacha20poly1305_push( | |
| 883 | state.buffer, | |
| 884 | state.byteOffset, | |
| 885 | state.byteLength, | |
| 886 | ||
| 887 | c.buffer, | |
| 888 | c.byteOffset, | |
| 889 | c.byteLength, | |
| 890 | ||
| 891 | m.buffer, | |
| 892 | m.byteOffset, | |
| 893 | m.byteLength, | |
| 894 | ||
| 895 | ad.buffer, | |
| 896 | ad.byteOffset, | |
| 897 | ad.byteLength, | |
| 898 | ||
| 899 | tag | |
| 900 | ) | |
| 901 | ||
| 902 | if (res < 0) throw new Error('push failed') | |
| 903 | ||
| 904 | return res | |
| 905 | } | |
| 906 | ||
| 907 | exports.crypto_secretstream_xchacha20poly1305_pull = function (state, m, tag, c, ad) { | |
| 908 | if (!ad) ad = OPTIONAL | |
| 909 | ||
| 910 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 911 | assert( | |
| 912 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 913 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 914 | ) | |
| 915 | assert(ArrayBuffer.isView(tag), 'tag must be a typed array') | |
| 916 | assert(tag.byteLength === 1, 'tag must be 1 byte') | |
| 917 | ||
| 918 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 919 | assert( | |
| 920 | c.byteLength >= binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 921 | "c must be at least 'crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 922 | ) | |
| 923 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 924 | assert( | |
| 925 | m.byteLength === c.byteLength - binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 926 | "m must be 'c.byteLength - crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 927 | ) | |
| 928 | ||
| 929 | const res = binding.crypto_secretstream_xchacha20poly1305_pull( | |
| 930 | state.buffer, | |
| 931 | state.byteOffset, | |
| 932 | state.byteLength, | |
| 933 | ||
| 934 | m.buffer, | |
| 935 | m.byteOffset, | |
| 936 | m.byteLength, | |
| 937 | ||
| 938 | tag.buffer, | |
| 939 | tag.byteOffset, | |
| 940 | tag.byteLength, | |
| 941 | ||
| 942 | c.buffer, | |
| 943 | c.byteOffset, | |
| 944 | c.byteLength, | |
| 945 | ||
| 946 | ad.buffer, | |
| 947 | ad.byteOffset, | |
| 948 | ad.byteLength | |
| 949 | ) | |
| 950 | ||
| 951 | if (res < 0) throw new Error('pull failed') | |
| 952 | ||
| 953 | return res | |
| 954 | } | |
| 955 | ||
| 956 | exports.crypto_secretstream_xchacha20poly1305_rekey = function (state) { | |
| 957 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 958 | assert( | |
| 959 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 960 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 961 | ) | |
| 962 | ||
| 963 | binding.crypto_secretstream_xchacha20poly1305_rekey( | |
| 964 | state.buffer, | |
| 965 | state.byteOffset, | |
| 966 | state.byteLength | |
| 967 | ) | |
| 968 | } | |
| 969 | ||
| 970 | // crypto_stream | |
| 971 | ||
| 972 | exports.crypto_stream = function (c, n, k) { | |
| 973 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 974 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 975 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 976 | assert( | |
| 977 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 978 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 979 | ) | |
| 980 | assert( | |
| 981 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 982 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 983 | ) | |
| 984 | ||
| 985 | const res = binding.crypto_stream(c, n, k) | |
| 986 | ||
| 987 | if (res !== 0) throw new Error('status: ' + res) | |
| 988 | } | |
| 989 | ||
| 990 | exports.crypto_stream_xor = function (c, m, n, k) { | |
| 991 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 992 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 993 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 994 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 995 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 996 | assert( | |
| 997 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 998 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 999 | ) | |
| 1000 | assert( | |
| 1001 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 1002 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 1003 | ) | |
| 1004 | ||
| 1005 | const res = binding.crypto_stream_xor( | |
| 1006 | c.buffer, | |
| 1007 | c.byteOffset, | |
| 1008 | c.byteLength, | |
| 1009 | ||
| 1010 | m.buffer, | |
| 1011 | m.byteOffset, | |
| 1012 | m.byteLength, | |
| 1013 | ||
| 1014 | n.buffer, | |
| 1015 | n.byteOffset, | |
| 1016 | n.byteLength, | |
| 1017 | ||
| 1018 | k.buffer, | |
| 1019 | k.byteOffset, | |
| 1020 | k.byteLength | |
| 1021 | ) | |
| 1022 | ||
| 1023 | if (res !== 0) throw new Error('status: ' + res) | |
| 1024 | } | |
| 1025 | ||
| 1026 | exports.crypto_stream_chacha20 = function (c, n, k) { | |
| 1027 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1028 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1029 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1030 | assert( | |
| 1031 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1032 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1033 | ) | |
| 1034 | assert( | |
| 1035 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1036 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1037 | ) | |
| 1038 | ||
| 1039 | const res = binding.crypto_stream_chacha20(c, n, k) | |
| 1040 | ||
| 1041 | if (res !== 0) throw new Error('status: ' + res) | |
| 1042 | } | |
| 1043 | ||
| 1044 | exports.crypto_stream_chacha20_xor = function (c, m, n, k) { | |
| 1045 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1046 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1047 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1048 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1049 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1050 | assert( | |
| 1051 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1052 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1053 | ) | |
| 1054 | assert( | |
| 1055 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1056 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1057 | ) | |
| 1058 | ||
| 1059 | const res = binding.crypto_stream_chacha20_xor(c, m, n, k) | |
| 1060 | ||
| 1061 | if (res !== 0) throw new Error('status: ' + res) | |
| 1062 | } | |
| 1063 | ||
| 1064 | exports.crypto_stream_chacha20_xor_ic = function (c, m, n, ic, k) { | |
| 1065 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1066 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1067 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1068 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1069 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1070 | assert( | |
| 1071 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1072 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1073 | ) | |
| 1074 | assert( | |
| 1075 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1076 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1077 | ) | |
| 1078 | ||
| 1079 | const res = binding.crypto_stream_chacha20_xor_ic(c, m, n, ic, k) | |
| 1080 | ||
| 1081 | if (res !== 0) throw new Error('status: ' + res) | |
| 1082 | } | |
| 1083 | ||
| 1084 | exports.crypto_stream_chacha20_ietf = function (c, n, k) { | |
| 1085 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1086 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1087 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1088 | assert( | |
| 1089 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1090 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1091 | ) | |
| 1092 | assert( | |
| 1093 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1094 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1095 | ) | |
| 1096 | ||
| 1097 | const res = binding.crypto_stream_chacha20_ietf(c, n, k) | |
| 1098 | ||
| 1099 | if (res !== 0) throw new Error('status: ' + res) | |
| 1100 | } | |
| 1101 | ||
| 1102 | exports.crypto_stream_chacha20_ietf_xor = function (c, m, n, k) { | |
| 1103 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1104 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1105 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1106 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1107 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1108 | assert( | |
| 1109 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1110 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1111 | ) | |
| 1112 | assert( | |
| 1113 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1114 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1115 | ) | |
| 1116 | ||
| 1117 | const res = binding.crypto_stream_chacha20_ietf_xor(c, m, n, k) | |
| 1118 | ||
| 1119 | if (res !== 0) throw new Error('status: ' + res) | |
| 1120 | } | |
| 1121 | ||
| 1122 | exports.crypto_stream_chacha20_ietf_xor_ic = function (c, m, n, ic, k) { | |
| 1123 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1124 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1125 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1126 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1127 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1128 | assert( | |
| 1129 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1130 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1131 | ) | |
| 1132 | assert( | |
| 1133 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1134 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1135 | ) | |
| 1136 | ||
| 1137 | const res = binding.crypto_stream_chacha20_ietf_xor_ic(c, m, n, ic, k) | |
| 1138 | ||
| 1139 | if (res !== 0) throw new Error('status: ' + res) | |
| 1140 | } | |
| 1141 | ||
| 1142 | exports.crypto_stream_xchacha20 = function (c, n, k) { | |
| 1143 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1144 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1145 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1146 | assert( | |
| 1147 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1148 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1149 | ) | |
| 1150 | assert( | |
| 1151 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1152 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1153 | ) | |
| 1154 | ||
| 1155 | const res = binding.crypto_stream_xchacha20(c, n, k) | |
| 1156 | ||
| 1157 | if (res !== 0) throw new Error('status: ' + res) | |
| 1158 | } | |
| 1159 | ||
| 1160 | exports.crypto_stream_xchacha20_xor = function (c, m, n, k) { | |
| 1161 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1162 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1163 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1164 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1165 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1166 | assert( | |
| 1167 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1168 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1169 | ) | |
| 1170 | assert( | |
| 1171 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1172 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1173 | ) | |
| 1174 | ||
| 1175 | const res = binding.crypto_stream_xchacha20_xor(c, m, n, k) | |
| 1176 | ||
| 1177 | if (res !== 0) throw new Error('status: ' + res) | |
| 1178 | } | |
| 1179 | ||
| 1180 | exports.crypto_stream_xchacha20_xor_ic = function (c, m, n, ic, k) { | |
| 1181 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1182 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1183 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1184 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1185 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1186 | assert( | |
| 1187 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1188 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1189 | ) | |
| 1190 | assert( | |
| 1191 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1192 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1193 | ) | |
| 1194 | ||
| 1195 | const res = binding.crypto_stream_xchacha20_xor_ic(c, m, n, ic, k) | |
| 1196 | ||
| 1197 | if (res !== 0) throw new Error('status: ' + res) | |
| 1198 | } | |
| 1199 | ||
| 1200 | exports.crypto_stream_salsa20 = function (c, n, k) { | |
| 1201 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1202 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1203 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1204 | assert( | |
| 1205 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1206 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1207 | ) | |
| 1208 | assert( | |
| 1209 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1210 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1211 | ) | |
| 1212 | ||
| 1213 | const res = binding.crypto_stream_salsa20(c, n, k) | |
| 1214 | ||
| 1215 | if (res !== 0) throw new Error('status: ' + res) | |
| 1216 | } | |
| 1217 | ||
| 1218 | exports.crypto_stream_salsa20_xor = function (c, m, n, k) { | |
| 1219 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1220 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1221 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1222 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1223 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1224 | assert( | |
| 1225 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1226 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1227 | ) | |
| 1228 | assert( | |
| 1229 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1230 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1231 | ) | |
| 1232 | ||
| 1233 | const res = binding.crypto_stream_salsa20_xor(c, m, n, k) | |
| 1234 | ||
| 1235 | if (res !== 0) throw new Error('status: ' + res) | |
| 1236 | } | |
| 1237 | ||
| 1238 | exports.crypto_stream_salsa20_xor_ic = function (c, m, n, ic, k) { | |
| 1239 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1240 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1241 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1242 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1243 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1244 | assert( | |
| 1245 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1246 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1247 | ) | |
| 1248 | assert( | |
| 1249 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1250 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1251 | ) | |
| 1252 | ||
| 1253 | const res = binding.crypto_stream_salsa20_xor_ic(c, m, n, ic, k) | |
| 1254 | ||
| 1255 | if (res !== 0) throw new Error('status: ' + res) | |
| 1256 | } | |
| 1257 | ||
| 1258 | // crypto_auth | |
| 1259 | ||
| 1260 | exports.crypto_auth = function (out, input, k) { | |
| 1261 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1262 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1263 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1264 | assert(out.byteLength === binding.crypto_auth_BYTES, "out must be 'crypto_auth_BYTES' bytes") | |
| 1265 | assert(k.byteLength === binding.crypto_auth_KEYBYTES, "k must be 'crypto_auth_KEYBYTES' bytes") | |
| 1266 | ||
| 1267 | const res = binding.crypto_auth(out, input, k) | |
| 1268 | ||
| 1269 | if (res !== 0) throw new Error('status: ' + res) | |
| 1270 | } | |
| 1271 | ||
| 1272 | exports.crypto_auth_verify = function (h, input, k) { | |
| 1273 | assert(ArrayBuffer.isView(h), 'h must be a typed array') | |
| 1274 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1275 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1276 | assert(h.byteLength === binding.crypto_auth_BYTES, "h must be 'crypto_auth_BYTES' bytes") | |
| 1277 | assert(k.byteLength === binding.crypto_auth_KEYBYTES, "k must be 'crypto_auth_KEYBYTES' bytes") | |
| 1278 | ||
| 1279 | return binding.crypto_auth_verify(h, input, k) | |
| 1280 | } | |
| 1281 | ||
| 1282 | // crypto_onetimeauth | |
| 1283 | ||
| 1284 | exports.crypto_onetimeauth = function (out, input, k) { | |
| 1285 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1286 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1287 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1288 | assert( | |
| 1289 | out.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1290 | "out must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1291 | ) | |
| 1292 | assert( | |
| 1293 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1294 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1295 | ) | |
| 1296 | ||
| 1297 | const res = binding.crypto_onetimeauth(out, input, k) | |
| 1298 | ||
| 1299 | if (res !== 0) throw new Error('status: ' + res) | |
| 1300 | } | |
| 1301 | ||
| 1302 | exports.crypto_onetimeauth_init = function (state, k) { | |
| 1303 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1304 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1305 | assert( | |
| 1306 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1307 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1308 | ) | |
| 1309 | assert( | |
| 1310 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1311 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1312 | ) | |
| 1313 | ||
| 1314 | const res = binding.crypto_onetimeauth_init(state, k) | |
| 1315 | ||
| 1316 | if (res !== 0) throw new Error('status: ' + res) | |
| 1317 | } | |
| 1318 | ||
| 1319 | exports.crypto_onetimeauth_update = function (state, input) { | |
| 1320 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1321 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1322 | assert( | |
| 1323 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1324 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1325 | ) | |
| 1326 | ||
| 1327 | const res = binding.crypto_onetimeauth_update(state, input) | |
| 1328 | ||
| 1329 | if (res !== 0) throw new Error('status: ' + res) | |
| 1330 | } | |
| 1331 | ||
| 1332 | exports.crypto_onetimeauth_final = function (state, out) { | |
| 1333 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1334 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1335 | assert( | |
| 1336 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1337 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1338 | ) | |
| 1339 | assert( | |
| 1340 | out.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1341 | "out must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1342 | ) | |
| 1343 | ||
| 1344 | const res = binding.crypto_onetimeauth_final(state, out) | |
| 1345 | ||
| 1346 | if (res !== 0) throw new Error('status: ' + res) | |
| 1347 | } | |
| 1348 | ||
| 1349 | exports.crypto_onetimeauth_verify = function (h, input, k) { | |
| 1350 | assert(ArrayBuffer.isView(h), 'h must be a typed array') | |
| 1351 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1352 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1353 | assert( | |
| 1354 | h.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1355 | "h must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1356 | ) | |
| 1357 | assert( | |
| 1358 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1359 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1360 | ) | |
| 1361 | ||
| 1362 | return binding.crypto_onetimeauth_verify(h, input, k) | |
| 1363 | } | |
| 1364 | ||
| 1365 | // crypto_pwhash | |
| 1366 | ||
| 1367 | exports.crypto_pwhash = function (out, passwd, salt, opslimit, memlimit, alg) { | |
| 1368 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1369 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1370 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1371 | assert( | |
| 1372 | out.byteLength >= binding.crypto_pwhash_BYTES_MIN, | |
| 1373 | "out must be at least 'crypto_pwhash_BYTES_MIN' bytes" | |
| 1374 | ) | |
| 1375 | assert( | |
| 1376 | out.byteLength <= binding.crypto_pwhash_BYTES_MAX, | |
| 1377 | "out must be at most 'crypto_pwhash_BYTES_MAX' bytes" | |
| 1378 | ) | |
| 1379 | assert( | |
| 1380 | salt.byteLength === binding.crypto_pwhash_SALTBYTES, | |
| 1381 | "salt must be 'crypto_pwhash_SALTBYTES' bytes" | |
| 1382 | ) | |
| 1383 | assert( | |
| 1384 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1385 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1386 | ) | |
| 1387 | assert( | |
| 1388 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1389 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1390 | ) | |
| 1391 | assert( | |
| 1392 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1393 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1394 | ) | |
| 1395 | assert( | |
| 1396 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1397 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1398 | ) | |
| 1399 | assert(alg >= 1 && alg <= 2, 'alg must be either Argon2i 1.3 or Argon2id 1.3') | |
| 1400 | ||
| 1401 | const res = binding.crypto_pwhash(out, passwd, salt, opslimit, memlimit, alg) | |
| 1402 | ||
| 1403 | if (res !== 0) throw new Error('status: ' + res) | |
| 1404 | } | |
| 1405 | ||
| 1406 | exports.crypto_pwhash_async = function ( | |
| 1407 | out, | |
| 1408 | passwd, | |
| 1409 | salt, | |
| 1410 | opslimit, | |
| 1411 | memlimit, | |
| 1412 | alg, | |
| 1413 | callback = undefined | |
| 1414 | ) { | |
| 1415 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1416 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1417 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1418 | assert( | |
| 1419 | out.byteLength >= binding.crypto_pwhash_BYTES_MIN, | |
| 1420 | "out must be at least 'crypto_pwhash_BYTES_MIN' bytes" | |
| 1421 | ) | |
| 1422 | assert( | |
| 1423 | out.byteLength <= binding.crypto_pwhash_BYTES_MAX, | |
| 1424 | "out must be at most 'crypto_pwhash_BYTES_MAX' bytes" | |
| 1425 | ) | |
| 1426 | assert( | |
| 1427 | salt.byteLength === binding.crypto_pwhash_SALTBYTES, | |
| 1428 | "salt must be 'crypto_pwhash_SALTBYTES' bytes" | |
| 1429 | ) | |
| 1430 | assert( | |
| 1431 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1432 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1433 | ) | |
| 1434 | assert( | |
| 1435 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1436 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1437 | ) | |
| 1438 | assert( | |
| 1439 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1440 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1441 | ) | |
| 1442 | assert( | |
| 1443 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1444 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1445 | ) | |
| 1446 | assert(alg >= 1 && alg <= 2, 'alg must be either Argon2i 1.3 or Argon2id 1.3') | |
| 1447 | ||
| 1448 | const [done, promise] = checkStatus(callback) | |
| 1449 | ||
| 1450 | binding.crypto_pwhash_async( | |
| 1451 | out.buffer, | |
| 1452 | out.byteOffset, | |
| 1453 | out.byteLength, | |
| 1454 | ||
| 1455 | passwd.buffer, | |
| 1456 | passwd.byteOffset, | |
| 1457 | passwd.byteLength, | |
| 1458 | ||
| 1459 | salt.buffer, | |
| 1460 | salt.byteOffset, | |
| 1461 | salt.byteLength, | |
| 1462 | ||
| 1463 | opslimit, | |
| 1464 | memlimit, | |
| 1465 | alg, | |
| 1466 | ||
| 1467 | done | |
| 1468 | ) | |
| 1469 | ||
| 1470 | return promise | |
| 1471 | } | |
| 1472 | ||
| 1473 | exports.crypto_pwhash_str = function (out, passwd, opslimit, memlimit) { | |
| 1474 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1475 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1476 | assert( | |
| 1477 | out.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1478 | "out must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1479 | ) | |
| 1480 | assert(typeof opslimit === 'number', 'opslimit must be a number') | |
| 1481 | assert( | |
| 1482 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1483 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1484 | ) | |
| 1485 | assert( | |
| 1486 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1487 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1488 | ) | |
| 1489 | assert(typeof memlimit === 'number', 'memlimit must be a number') | |
| 1490 | assert( | |
| 1491 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1492 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1493 | ) | |
| 1494 | assert( | |
| 1495 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1496 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1497 | ) | |
| 1498 | ||
| 1499 | const res = binding.crypto_pwhash_str(out, passwd, opslimit, memlimit) | |
| 1500 | ||
| 1501 | if (res !== 0) throw new Error('status: ' + res) | |
| 1502 | } | |
| 1503 | ||
| 1504 | exports.crypto_pwhash_str_async = function (out, passwd, opslimit, memlimit, callback = undefined) { | |
| 1505 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1506 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1507 | assert( | |
| 1508 | out.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1509 | "out must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1510 | ) | |
| 1511 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1512 | assert(typeof opslimit === 'number', 'opslimit must be a number') | |
| 1513 | assert( | |
| 1514 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1515 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1516 | ) | |
| 1517 | assert( | |
| 1518 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1519 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1520 | ) | |
| 1521 | assert(typeof memlimit === 'number', 'memlimit must be a number') | |
| 1522 | assert( | |
| 1523 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1524 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1525 | ) | |
| 1526 | assert( | |
| 1527 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1528 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1529 | ) | |
| 1530 | ||
| 1531 | const [done, promise] = checkStatus(callback) | |
| 1532 | ||
| 1533 | binding.crypto_pwhash_str_async( | |
| 1534 | out.buffer, | |
| 1535 | out.byteOffset, | |
| 1536 | out.byteLength, | |
| 1537 | ||
| 1538 | passwd.buffer, | |
| 1539 | passwd.byteOffset, | |
| 1540 | passwd.byteLength, | |
| 1541 | ||
| 1542 | opslimit, | |
| 1543 | memlimit, | |
| 1544 | ||
| 1545 | done | |
| 1546 | ) | |
| 1547 | ||
| 1548 | return promise | |
| 1549 | } | |
| 1550 | ||
| 1551 | exports.crypto_pwhash_str_verify = function (str, passwd) { | |
| 1552 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1553 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1554 | assert( | |
| 1555 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1556 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1557 | ) | |
| 1558 | ||
| 1559 | return binding.crypto_pwhash_str_verify(str, passwd) | |
| 1560 | } | |
| 1561 | ||
| 1562 | exports.crypto_pwhash_str_verify_async = function (str, passwd, callback = undefined) { | |
| 1563 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1564 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1565 | assert( | |
| 1566 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1567 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1568 | ) | |
| 1569 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1570 | ||
| 1571 | const [done, promise] = checkStatus(callback, true) | |
| 1572 | ||
| 1573 | binding.crypto_pwhash_str_verify_async( | |
| 1574 | str.buffer, | |
| 1575 | str.byteOffset, | |
| 1576 | str.byteLength, | |
| 1577 | ||
| 1578 | passwd.buffer, | |
| 1579 | passwd.byteOffset, | |
| 1580 | passwd.byteLength, | |
| 1581 | ||
| 1582 | done | |
| 1583 | ) | |
| 1584 | ||
| 1585 | return promise | |
| 1586 | } | |
| 1587 | ||
| 1588 | exports.crypto_pwhash_str_needs_rehash = function (str, opslimit, memlimit) { | |
| 1589 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1590 | assert( | |
| 1591 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1592 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1593 | ) | |
| 1594 | assert( | |
| 1595 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1596 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1597 | ) | |
| 1598 | assert( | |
| 1599 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1600 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1601 | ) | |
| 1602 | assert( | |
| 1603 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1604 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1605 | ) | |
| 1606 | assert( | |
| 1607 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1608 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1609 | ) | |
| 1610 | ||
| 1611 | return binding.crypto_pwhash_str_needs_rehash(str, opslimit, memlimit) | |
| 1612 | } | |
| 1613 | ||
| 1614 | exports.crypto_pwhash_scryptsalsa208sha256 = function (out, passwd, salt, opslimit, memlimit) { | |
| 1615 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1616 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1617 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1618 | assert( | |
| 1619 | out.byteLength >= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN, | |
| 1620 | "out must be at least 'crypto_pwhash_scryptsalsa208sha256_BYTES_MIN' bytes" | |
| 1621 | ) | |
| 1622 | assert( | |
| 1623 | out.byteLength <= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX, | |
| 1624 | "out must be at most 'crypto_pwhash_scryptsalsa208sha256_BYTES_MAX' bytes" | |
| 1625 | ) | |
| 1626 | assert( | |
| 1627 | salt.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_SALTBYTES, | |
| 1628 | "salt must be 'crypto_pwhash_scryptsalsa208sha256_SALTBYTES' bytes" | |
| 1629 | ) | |
| 1630 | assert( | |
| 1631 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1632 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1633 | ) | |
| 1634 | assert( | |
| 1635 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1636 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1637 | ) | |
| 1638 | assert( | |
| 1639 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1640 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1641 | ) | |
| 1642 | assert( | |
| 1643 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1644 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1645 | ) | |
| 1646 | ||
| 1647 | const res = binding.crypto_pwhash_scryptsalsa208sha256(out, passwd, salt, opslimit, memlimit) | |
| 1648 | ||
| 1649 | if (res !== 0) throw new Error('status: ' + res) | |
| 1650 | } | |
| 1651 | ||
| 1652 | exports.crypto_pwhash_scryptsalsa208sha256_async = function ( | |
| 1653 | out, | |
| 1654 | passwd, | |
| 1655 | salt, | |
| 1656 | opslimit, | |
| 1657 | memlimit, | |
| 1658 | callback = undefined | |
| 1659 | ) { | |
| 1660 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1661 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1662 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1663 | assert( | |
| 1664 | out.byteLength >= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN, | |
| 1665 | "out must be at least 'crypto_pwhash_scryptsalsa208sha256_BYTES_MIN' bytes" | |
| 1666 | ) | |
| 1667 | assert( | |
| 1668 | out.byteLength <= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX, | |
| 1669 | "out must be at most 'crypto_pwhash_scryptsalsa208sha256_BYTES_MAX' bytes" | |
| 1670 | ) | |
| 1671 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1672 | assert( | |
| 1673 | salt.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_SALTBYTES, | |
| 1674 | "salt must be 'crypto_pwhash_scryptsalsa208sha256_SALTBYTES' bytes" | |
| 1675 | ) | |
| 1676 | assert( | |
| 1677 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1678 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1679 | ) | |
| 1680 | assert( | |
| 1681 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1682 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1683 | ) | |
| 1684 | assert( | |
| 1685 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1686 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1687 | ) | |
| 1688 | assert( | |
| 1689 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1690 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1691 | ) | |
| 1692 | ||
| 1693 | const [done, promise] = checkStatus(callback) | |
| 1694 | ||
| 1695 | binding.crypto_pwhash_scryptsalsa208sha256_async( | |
| 1696 | out.buffer, | |
| 1697 | out.byteOffset, | |
| 1698 | out.byteLength, | |
| 1699 | ||
| 1700 | passwd.buffer, | |
| 1701 | passwd.byteOffset, | |
| 1702 | passwd.byteLength, | |
| 1703 | ||
| 1704 | salt.buffer, | |
| 1705 | salt.byteOffset, | |
| 1706 | salt.byteLength, | |
| 1707 | ||
| 1708 | opslimit, | |
| 1709 | memlimit, | |
| 1710 | ||
| 1711 | done | |
| 1712 | ) | |
| 1713 | ||
| 1714 | return promise | |
| 1715 | } | |
| 1716 | ||
| 1717 | exports.crypto_pwhash_scryptsalsa208sha256_str_async = function ( | |
| 1718 | out, | |
| 1719 | passwd, | |
| 1720 | opslimit, | |
| 1721 | memlimit, | |
| 1722 | callback = undefined | |
| 1723 | ) { | |
| 1724 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1725 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1726 | assert( | |
| 1727 | out.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1728 | "out must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1729 | ) | |
| 1730 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1731 | assert( | |
| 1732 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1733 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1734 | ) | |
| 1735 | assert( | |
| 1736 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1737 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1738 | ) | |
| 1739 | assert( | |
| 1740 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1741 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1742 | ) | |
| 1743 | assert( | |
| 1744 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1745 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1746 | ) | |
| 1747 | ||
| 1748 | const [done, promise] = checkStatus(callback) | |
| 1749 | ||
| 1750 | binding.crypto_pwhash_scryptsalsa208sha256_str_async( | |
| 1751 | out.buffer, | |
| 1752 | out.byteOffset, | |
| 1753 | out.byteLength, | |
| 1754 | ||
| 1755 | passwd.buffer, | |
| 1756 | passwd.byteOffset, | |
| 1757 | passwd.byteLength, | |
| 1758 | ||
| 1759 | opslimit, | |
| 1760 | memlimit, | |
| 1761 | ||
| 1762 | done | |
| 1763 | ) | |
| 1764 | ||
| 1765 | return promise | |
| 1766 | } | |
| 1767 | ||
| 1768 | exports.crypto_pwhash_scryptsalsa208sha256_str = function (out, passwd, opslimit, memlimit) { | |
| 1769 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1770 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1771 | assert( | |
| 1772 | out.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1773 | "out must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1774 | ) | |
| 1775 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1776 | assert( | |
| 1777 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1778 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1779 | ) | |
| 1780 | assert( | |
| 1781 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1782 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1783 | ) | |
| 1784 | assert( | |
| 1785 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1786 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1787 | ) | |
| 1788 | assert( | |
| 1789 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1790 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1791 | ) | |
| 1792 | ||
| 1793 | const res = binding.crypto_pwhash_scryptsalsa208sha256_str(out, passwd, opslimit, memlimit) | |
| 1794 | ||
| 1795 | if (res !== 0) throw new Error('status: ' + res) | |
| 1796 | } | |
| 1797 | ||
| 1798 | exports.crypto_pwhash_scryptsalsa208sha256_str_verify_async = function ( | |
| 1799 | str, | |
| 1800 | passwd, | |
| 1801 | callback = undefined | |
| 1802 | ) { | |
| 1803 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1804 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1805 | assert( | |
| 1806 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1807 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1808 | ) | |
| 1809 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1810 | ||
| 1811 | const [done, promise] = checkStatus(callback, true) | |
| 1812 | ||
| 1813 | binding.crypto_pwhash_scryptsalsa208sha256_str_verify_async( | |
| 1814 | str.buffer, | |
| 1815 | str.byteOffset, | |
| 1816 | str.byteLength, | |
| 1817 | ||
| 1818 | passwd.buffer, | |
| 1819 | passwd.byteOffset, | |
| 1820 | passwd.byteLength, | |
| 1821 | ||
| 1822 | done | |
| 1823 | ) | |
| 1824 | ||
| 1825 | return promise | |
| 1826 | } | |
| 1827 | ||
| 1828 | exports.crypto_pwhash_scryptsalsa208sha256_str_verify = function (str, passwd) { | |
| 1829 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1830 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1831 | assert( | |
| 1832 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1833 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1834 | ) | |
| 1835 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1836 | ||
| 1837 | return binding.crypto_pwhash_scryptsalsa208sha256_str_verify(str, passwd) | |
| 1838 | } | |
| 1839 | ||
| 1840 | exports.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash = function (str, opslimit, memlimit) { | |
| 1841 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1842 | assert( | |
| 1843 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1844 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1845 | ) | |
| 1846 | assert( | |
| 1847 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1848 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1849 | ) | |
| 1850 | assert( | |
| 1851 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1852 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1853 | ) | |
| 1854 | assert( | |
| 1855 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1856 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1857 | ) | |
| 1858 | assert( | |
| 1859 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1860 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1861 | ) | |
| 1862 | ||
| 1863 | return binding.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(str, opslimit, memlimit) | |
| 1864 | } | |
| 1865 | ||
| 1866 | // crypto_kx | |
| 1867 | ||
| 1868 | exports.crypto_kx_keypair = function (pk, sk) { | |
| 1869 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 1870 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 1871 | assert( | |
| 1872 | pk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1873 | "pk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1874 | ) | |
| 1875 | assert( | |
| 1876 | sk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1877 | "sk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1878 | ) | |
| 1879 | ||
| 1880 | const res = binding.crypto_kx_keypair(pk, sk) | |
| 1881 | ||
| 1882 | if (res !== 0) throw new Error('status: ' + res) | |
| 1883 | } | |
| 1884 | ||
| 1885 | exports.crypto_kx_seed_keypair = function (pk, sk, seed) { | |
| 1886 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 1887 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 1888 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 1889 | assert( | |
| 1890 | pk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1891 | "pk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1892 | ) | |
| 1893 | assert( | |
| 1894 | sk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1895 | "sk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1896 | ) | |
| 1897 | assert( | |
| 1898 | seed.byteLength === binding.crypto_kx_SEEDBYTES, | |
| 1899 | "seed must be 'crypto_kx_SEEDBYTES' bytes" | |
| 1900 | ) | |
| 1901 | ||
| 1902 | const res = binding.crypto_kx_seed_keypair(pk, sk, seed) | |
| 1903 | ||
| 1904 | if (res !== 0) throw new Error('status: ' + res) | |
| 1905 | } | |
| 1906 | ||
| 1907 | exports.crypto_kx_client_session_keys = function (rx, tx, clientPk, clientSk, serverPk) { | |
| 1908 | if (!rx) rx = undefined | |
| 1909 | if (!tx) tx = undefined | |
| 1910 | ||
| 1911 | assert(rx || tx, 'at least one session key must be specified') | |
| 1912 | ||
| 1913 | if (rx) { | |
| 1914 | assert(ArrayBuffer.isView(rx), 'rx must be a typed array') | |
| 1915 | assert( | |
| 1916 | rx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1917 | "rx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1918 | ) | |
| 1919 | } | |
| 1920 | ||
| 1921 | if (tx) { | |
| 1922 | assert(ArrayBuffer.isView(tx), 'tx must be a typed array') | |
| 1923 | assert( | |
| 1924 | tx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1925 | "tx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1926 | ) | |
| 1927 | } | |
| 1928 | ||
| 1929 | assert(ArrayBuffer.isView(clientPk), 'clientPk must be a typed array') | |
| 1930 | assert(ArrayBuffer.isView(clientSk), 'clientSk must be a typed array') | |
| 1931 | assert(ArrayBuffer.isView(serverPk), 'serverPk must be a typed array') | |
| 1932 | assert( | |
| 1933 | clientPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1934 | "clientPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1935 | ) | |
| 1936 | assert( | |
| 1937 | clientSk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1938 | "clientSk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1939 | ) | |
| 1940 | assert( | |
| 1941 | serverPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1942 | "serverPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1943 | ) | |
| 1944 | ||
| 1945 | const res = binding.crypto_kx_client_session_keys(rx, tx, clientPk, clientSk, serverPk) | |
| 1946 | ||
| 1947 | if (res !== 0) throw new Error('status: ' + res) | |
| 1948 | } | |
| 1949 | ||
| 1950 | exports.crypto_kx_server_session_keys = function (rx, tx, serverPk, serverSk, clientPk) { | |
| 1951 | if (!rx) rx = undefined | |
| 1952 | if (!tx) tx = undefined | |
| 1953 | ||
| 1954 | assert(rx || tx, 'at least one session key must be specified') | |
| 1955 | ||
| 1956 | if (rx) { | |
| 1957 | assert(ArrayBuffer.isView(rx), 'rx must be a typed array') | |
| 1958 | assert( | |
| 1959 | rx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1960 | "rx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1961 | ) | |
| 1962 | } | |
| 1963 | ||
| 1964 | if (tx) { | |
| 1965 | assert(ArrayBuffer.isView(tx), 'tx must be a typed array') | |
| 1966 | assert( | |
| 1967 | tx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1968 | "tx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1969 | ) | |
| 1970 | } | |
| 1971 | ||
| 1972 | assert(ArrayBuffer.isView(serverPk), 'serverPk must be a typed array') | |
| 1973 | assert(ArrayBuffer.isView(serverSk), 'serverSk must be a typed array') | |
| 1974 | assert(ArrayBuffer.isView(clientPk), 'clientPk must be a typed array') | |
| 1975 | assert( | |
| 1976 | serverPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1977 | "serverPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1978 | ) | |
| 1979 | assert( | |
| 1980 | serverSk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1981 | "serverSk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1982 | ) | |
| 1983 | assert( | |
| 1984 | clientPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1985 | "clientPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1986 | ) | |
| 1987 | ||
| 1988 | const res = binding.crypto_kx_server_session_keys(rx, tx, serverPk, serverSk, clientPk) | |
| 1989 | ||
| 1990 | if (res !== 0) throw new Error('status: ' + res) | |
| 1991 | } | |
| 1992 | ||
| 1993 | // crypto_scalarmult | |
| 1994 | ||
| 1995 | exports.crypto_scalarmult_base = function (q, n) { | |
| 1996 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 1997 | assert( | |
| 1998 | q.byteLength === binding.crypto_scalarmult_BYTES, | |
| 1999 | "q must be 'crypto_scalarmult_BYTES' bytes" | |
| 2000 | ) | |
| 2001 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2002 | assert( | |
| 2003 | n.byteLength === binding.crypto_scalarmult_SCALARBYTES, | |
| 2004 | "n must be 'crypto_scalarmult_SCALARBYTES' bytes" | |
| 2005 | ) | |
| 2006 | ||
| 2007 | const res = binding.crypto_scalarmult_base(q, n) | |
| 2008 | ||
| 2009 | if (res !== 0) throw new Error('status: ' + res) | |
| 2010 | } | |
| 2011 | ||
| 2012 | exports.crypto_scalarmult = function (q, n, p) { | |
| 2013 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2014 | assert( | |
| 2015 | q.byteLength === binding.crypto_scalarmult_BYTES, | |
| 2016 | "q must be 'crypto_scalarmult_BYTES' bytes" | |
| 2017 | ) | |
| 2018 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2019 | assert( | |
| 2020 | n.byteLength === binding.crypto_scalarmult_SCALARBYTES, | |
| 2021 | "n must be 'crypto_scalarmult_SCALARBYTES' bytes" | |
| 2022 | ) | |
| 2023 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2024 | assert( | |
| 2025 | p.byteLength === binding.crypto_scalarmult_BYTES, | |
| 2026 | "p must be 'crypto_scalarmult_BYTES' bytes" | |
| 2027 | ) | |
| 2028 | ||
| 2029 | const res = binding.crypto_scalarmult(q, n, p) | |
| 2030 | ||
| 2031 | if (res !== 0) throw new Error('status: ' + res) | |
| 2032 | } | |
| 2033 | ||
| 2034 | exports.crypto_scalarmult_ed25519_base = function (q, n) { | |
| 2035 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2036 | assert( | |
| 2037 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2038 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2039 | ) | |
| 2040 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2041 | assert( | |
| 2042 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2043 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2044 | ) | |
| 2045 | ||
| 2046 | const res = binding.crypto_scalarmult_ed25519_base(q, n) | |
| 2047 | ||
| 2048 | if (res !== 0) throw new Error('status: ' + res) | |
| 2049 | } | |
| 2050 | ||
| 2051 | exports.crypto_scalarmult_ed25519 = function (q, n, p) { | |
| 2052 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2053 | assert( | |
| 2054 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2055 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2056 | ) | |
| 2057 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2058 | assert( | |
| 2059 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2060 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2061 | ) | |
| 2062 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2063 | assert( | |
| 2064 | p.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2065 | "p must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2066 | ) | |
| 2067 | ||
| 2068 | const res = binding.crypto_scalarmult_ed25519(q, n, p) | |
| 2069 | ||
| 2070 | if (res !== 0) throw new Error('status: ' + res) | |
| 2071 | } | |
| 2072 | ||
| 2073 | exports.crypto_core_ed25519_is_valid_point = function (p) { | |
| 2074 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2075 | assert( | |
| 2076 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2077 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2078 | ) | |
| 2079 | ||
| 2080 | return binding.crypto_core_ed25519_is_valid_point(p) | |
| 2081 | } | |
| 2082 | ||
| 2083 | exports.crypto_core_ed25519_from_uniform = function (p, r) { | |
| 2084 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2085 | assert( | |
| 2086 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2087 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2088 | ) | |
| 2089 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2090 | assert( | |
| 2091 | r.byteLength === binding.crypto_core_ed25519_UNIFORMBYTES, | |
| 2092 | "r must be 'crypto_core_ed25519_UNIFORMBYTES' bytes" | |
| 2093 | ) | |
| 2094 | ||
| 2095 | const res = binding.crypto_core_ed25519_from_uniform(p, r) | |
| 2096 | ||
| 2097 | if (res !== 0) throw new Error('status: ' + res) | |
| 2098 | } | |
| 2099 | ||
| 2100 | exports.crypto_scalarmult_ed25519_base_noclamp = function (q, n) { | |
| 2101 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2102 | assert( | |
| 2103 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2104 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2105 | ) | |
| 2106 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2107 | assert( | |
| 2108 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2109 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2110 | ) | |
| 2111 | ||
| 2112 | const res = binding.crypto_scalarmult_ed25519_base_noclamp(q, n) | |
| 2113 | ||
| 2114 | if (res !== 0) throw new Error('status: ' + res) | |
| 2115 | } | |
| 2116 | ||
| 2117 | exports.crypto_scalarmult_ed25519_noclamp = function (q, n, p) { | |
| 2118 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2119 | assert( | |
| 2120 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2121 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2122 | ) | |
| 2123 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2124 | assert( | |
| 2125 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2126 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2127 | ) | |
| 2128 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2129 | assert( | |
| 2130 | p.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2131 | "p must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2132 | ) | |
| 2133 | ||
| 2134 | const res = binding.crypto_scalarmult_ed25519_noclamp(q, n, p) | |
| 2135 | ||
| 2136 | if (res !== 0) throw new Error('status: ' + res) | |
| 2137 | } | |
| 2138 | ||
| 2139 | // crypto_core | |
| 2140 | ||
| 2141 | exports.crypto_core_ed25519_add = function (r, p, q) { | |
| 2142 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2143 | assert( | |
| 2144 | r.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2145 | "r must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2146 | ) | |
| 2147 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2148 | assert( | |
| 2149 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2150 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2151 | ) | |
| 2152 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2153 | assert( | |
| 2154 | q.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2155 | "q must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2156 | ) | |
| 2157 | ||
| 2158 | const res = binding.crypto_core_ed25519_add(r, p, q) | |
| 2159 | ||
| 2160 | if (res !== 0) throw new Error('status: ' + res) | |
| 2161 | } | |
| 2162 | ||
| 2163 | exports.crypto_core_ed25519_sub = function (r, p, q) { | |
| 2164 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2165 | assert( | |
| 2166 | r.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2167 | "r must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2168 | ) | |
| 2169 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2170 | assert( | |
| 2171 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2172 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2173 | ) | |
| 2174 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2175 | assert( | |
| 2176 | q.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2177 | "q must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2178 | ) | |
| 2179 | ||
| 2180 | const res = binding.crypto_core_ed25519_sub(r, p, q) | |
| 2181 | ||
| 2182 | if (res !== 0) throw new Error('status: ' + res) | |
| 2183 | } | |
| 2184 | ||
| 2185 | exports.crypto_core_ed25519_scalar_random = function (r) { | |
| 2186 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2187 | assert( | |
| 2188 | r.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2189 | "r must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2190 | ) | |
| 2191 | ||
| 2192 | binding.crypto_core_ed25519_scalar_random(r) | |
| 2193 | } | |
| 2194 | ||
| 2195 | exports.crypto_core_ed25519_scalar_reduce = function (r, s) { | |
| 2196 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2197 | assert( | |
| 2198 | r.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2199 | "r must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2200 | ) | |
| 2201 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2202 | assert( | |
| 2203 | s.byteLength === binding.crypto_core_ed25519_NONREDUCEDSCALARBYTES, | |
| 2204 | "s must be 'crypto_core_ed25519_NONREDUCEDSCALARBYTES' bytes" | |
| 2205 | ) | |
| 2206 | ||
| 2207 | binding.crypto_core_ed25519_scalar_reduce(r, s) | |
| 2208 | } | |
| 2209 | ||
| 2210 | exports.crypto_core_ed25519_scalar_invert = function (recip, s) { | |
| 2211 | assert(ArrayBuffer.isView(recip), 'recip must be a typed array') | |
| 2212 | assert( | |
| 2213 | recip.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2214 | "recip must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2215 | ) | |
| 2216 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2217 | assert( | |
| 2218 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2219 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2220 | ) | |
| 2221 | ||
| 2222 | binding.crypto_core_ed25519_scalar_invert(recip, s) | |
| 2223 | } | |
| 2224 | ||
| 2225 | exports.crypto_core_ed25519_scalar_negate = function (neg, s) { | |
| 2226 | assert(ArrayBuffer.isView(neg), 'neg must be a typed array') | |
| 2227 | assert( | |
| 2228 | neg.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2229 | "neg must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2230 | ) | |
| 2231 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2232 | assert( | |
| 2233 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2234 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2235 | ) | |
| 2236 | ||
| 2237 | binding.crypto_core_ed25519_scalar_negate(neg, s) | |
| 2238 | } | |
| 2239 | ||
| 2240 | exports.crypto_core_ed25519_scalar_complement = function (comp, s) { | |
| 2241 | assert(ArrayBuffer.isView(comp), 'comp must be a typed array') | |
| 2242 | assert( | |
| 2243 | comp.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2244 | "comp must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2245 | ) | |
| 2246 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2247 | assert( | |
| 2248 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2249 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2250 | ) | |
| 2251 | ||
| 2252 | binding.crypto_core_ed25519_scalar_complement(comp, s) | |
| 2253 | } | |
| 2254 | ||
| 2255 | exports.crypto_core_ed25519_scalar_add = function (z, x, y) { | |
| 2256 | assert(ArrayBuffer.isView(z), 'z must be a typed array') | |
| 2257 | assert( | |
| 2258 | z.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2259 | "z must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2260 | ) | |
| 2261 | assert(ArrayBuffer.isView(x), 'x must be a typed array') | |
| 2262 | assert( | |
| 2263 | x.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2264 | "x must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2265 | ) | |
| 2266 | assert(ArrayBuffer.isView(y), 'y must be a typed array') | |
| 2267 | assert( | |
| 2268 | y.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2269 | "y must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2270 | ) | |
| 2271 | ||
| 2272 | binding.crypto_core_ed25519_scalar_add(z, x, y) | |
| 2273 | } | |
| 2274 | ||
| 2275 | exports.crypto_core_ed25519_scalar_sub = function (z, x, y) { | |
| 2276 | assert(ArrayBuffer.isView(z), 'z must be a typed array') | |
| 2277 | assert( | |
| 2278 | z.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2279 | "z must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2280 | ) | |
| 2281 | assert(ArrayBuffer.isView(x), 'x must be a typed array') | |
| 2282 | assert( | |
| 2283 | x.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2284 | "x must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2285 | ) | |
| 2286 | assert(ArrayBuffer.isView(y), 'y must be a typed array') | |
| 2287 | assert( | |
| 2288 | y.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2289 | "y must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2290 | ) | |
| 2291 | ||
| 2292 | binding.crypto_core_ed25519_scalar_sub(z, x, y) | |
| 2293 | } | |
| 2294 | ||
| 2295 | // crypto_shorthash | |
| 2296 | ||
| 2297 | exports.crypto_shorthash = function (out, input, k) { | |
| 2298 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2299 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2300 | assert( | |
| 2301 | out.byteLength === binding.crypto_shorthash_BYTES, | |
| 2302 | "out must be 'crypto_shorthash_BYTES' bytes" | |
| 2303 | ) | |
| 2304 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2305 | assert( | |
| 2306 | k.byteLength === binding.crypto_shorthash_KEYBYTES, | |
| 2307 | "k must be 'crypto_shorthash_KEYBYTES' bytes" | |
| 2308 | ) | |
| 2309 | ||
| 2310 | const res = binding.crypto_shorthash(out, input, k) | |
| 2311 | ||
| 2312 | if (res !== 0) throw new Error('status: ' + res) | |
| 2313 | } | |
| 2314 | ||
| 2315 | // crypto_kdf | |
| 2316 | ||
| 2317 | exports.crypto_kdf_keygen = function (key) { | |
| 2318 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 2319 | assert(key.byteLength === binding.crypto_kdf_KEYBYTES, "key must be 'crypto_kdf_KEYBYTES' bytes") | |
| 2320 | ||
| 2321 | binding.crypto_kdf_keygen(key) | |
| 2322 | } | |
| 2323 | ||
| 2324 | exports.crypto_kdf_derive_from_key = function (subkey, subkeyId, ctx, key) { | |
| 2325 | assert(ArrayBuffer.isView(subkey), 'subkey must be a typed array') | |
| 2326 | assert( | |
| 2327 | subkey.byteLength >= binding.crypto_kdf_BYTES_MIN, | |
| 2328 | "subkey must be at least 'crypto_kdf_BYTES_MIN' bytes" | |
| 2329 | ) | |
| 2330 | assert( | |
| 2331 | subkey.byteLength <= binding.crypto_kdf_BYTES_MAX, | |
| 2332 | "subkey must be at most 'crypto_kdf_BYTES_MAX' bytes" | |
| 2333 | ) | |
| 2334 | assert(ArrayBuffer.isView(ctx), 'ctx must be a typed array') | |
| 2335 | assert( | |
| 2336 | ctx.byteLength === binding.crypto_kdf_CONTEXTBYTES, | |
| 2337 | "ctx must be 'crypto_kdf_CONTEXTBYTES' bytes" | |
| 2338 | ) | |
| 2339 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 2340 | assert(key.byteLength === binding.crypto_kdf_KEYBYTES, "key must be 'crypto_kdf_KEYBYTES' bytes") | |
| 2341 | ||
| 2342 | const res = binding.crypto_kdf_derive_from_key(subkey, subkeyId, ctx, key) | |
| 2343 | ||
| 2344 | if (res !== 0) throw new Error('status: ' + res) | |
| 2345 | } | |
| 2346 | ||
| 2347 | // crypto_hash | |
| 2348 | ||
| 2349 | exports.crypto_hash = function (out, input) { | |
| 2350 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2351 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2352 | assert(out.byteLength === binding.crypto_hash_BYTES, "out must be 'crypto_hash_BYTES' bytes") | |
| 2353 | ||
| 2354 | const res = binding.crypto_hash(out, input) | |
| 2355 | ||
| 2356 | if (res !== 0) throw new Error('status: ' + res) | |
| 2357 | } | |
| 2358 | ||
| 2359 | exports.crypto_hash_sha256 = function (out, input) { | |
| 2360 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2361 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2362 | assert( | |
| 2363 | out.byteLength === binding.crypto_hash_sha256_BYTES, | |
| 2364 | "out must be 'crypto_hash_sha256_BYTES' bytes" | |
| 2365 | ) | |
| 2366 | ||
| 2367 | const res = binding.crypto_hash_sha256(out, input) | |
| 2368 | ||
| 2369 | if (res !== 0) throw new Error('status: ' + res) | |
| 2370 | } | |
| 2371 | ||
| 2372 | exports.crypto_hash_sha256_init = function (state) { | |
| 2373 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2374 | assert( | |
| 2375 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2376 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2377 | ) | |
| 2378 | ||
| 2379 | const res = binding.crypto_hash_sha256_init(state) | |
| 2380 | ||
| 2381 | if (res !== 0) throw new Error('status: ' + res) | |
| 2382 | } | |
| 2383 | ||
| 2384 | exports.crypto_hash_sha256_update = function (state, input) { | |
| 2385 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2386 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2387 | assert( | |
| 2388 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2389 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2390 | ) | |
| 2391 | ||
| 2392 | const res = binding.crypto_hash_sha256_update(state, input) | |
| 2393 | ||
| 2394 | if (res !== 0) throw new Error('status: ' + res) | |
| 2395 | } | |
| 2396 | ||
| 2397 | exports.crypto_hash_sha256_final = function (state, out) { | |
| 2398 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2399 | assert( | |
| 2400 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2401 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2402 | ) | |
| 2403 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2404 | assert( | |
| 2405 | out.byteLength === binding.crypto_hash_sha256_BYTES, | |
| 2406 | "out must be 'crypto_hash_sha256_BYTES' bytes" | |
| 2407 | ) | |
| 2408 | ||
| 2409 | const res = binding.crypto_hash_sha256_final(state, out) | |
| 2410 | ||
| 2411 | if (res !== 0) throw new Error('status: ' + res) | |
| 2412 | } | |
| 2413 | ||
| 2414 | exports.crypto_hash_sha512 = function (out, input) { | |
| 2415 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2416 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2417 | assert( | |
| 2418 | out.byteLength === binding.crypto_hash_sha512_BYTES, | |
| 2419 | "out must be 'crypto_hash_sha512_BYTES' bytes" | |
| 2420 | ) | |
| 2421 | ||
| 2422 | const res = binding.crypto_hash_sha512(out, input) | |
| 2423 | ||
| 2424 | if (res !== 0) throw new Error('status: ' + res) | |
| 2425 | } | |
| 2426 | ||
| 2427 | exports.crypto_hash_sha512_init = function (state) { | |
| 2428 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2429 | assert( | |
| 2430 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2431 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2432 | ) | |
| 2433 | ||
| 2434 | const res = binding.crypto_hash_sha512_init(state) | |
| 2435 | ||
| 2436 | if (res !== 0) throw new Error('status: ' + res) | |
| 2437 | } | |
| 2438 | ||
| 2439 | exports.crypto_hash_sha512_update = function (state, input) { | |
| 2440 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2441 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2442 | assert( | |
| 2443 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2444 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2445 | ) | |
| 2446 | ||
| 2447 | const res = binding.crypto_hash_sha512_update(state, input) | |
| 2448 | ||
| 2449 | if (res !== 0) throw new Error('status: ' + res) | |
| 2450 | } | |
| 2451 | ||
| 2452 | exports.crypto_hash_sha512_final = function (state, out) { | |
| 2453 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2454 | assert( | |
| 2455 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2456 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2457 | ) | |
| 2458 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2459 | assert( | |
| 2460 | out.byteLength === binding.crypto_hash_sha512_BYTES, | |
| 2461 | "out must be 'crypto_hash_sha512_BYTES' bytes" | |
| 2462 | ) | |
| 2463 | ||
| 2464 | const res = binding.crypto_hash_sha512_final(state, out) | |
| 2465 | ||
| 2466 | if (res !== 0) throw new Error('status: ' + res) | |
| 2467 | } | |
| 2468 | ||
| 2469 | // crypto_aead | |
| 2470 | ||
| 2471 | exports.crypto_aead_xchacha20poly1305_ietf_keygen = function (k) { | |
| 2472 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2473 | assert( | |
| 2474 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2475 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2476 | ) | |
| 2477 | ||
| 2478 | binding.crypto_aead_xchacha20poly1305_ietf_keygen(k) | |
| 2479 | } | |
| 2480 | ||
| 2481 | exports.crypto_aead_xchacha20poly1305_ietf_encrypt = function (c, m, ad, nsec, npub, k) { | |
| 2482 | if (!ad) ad = undefined | |
| 2483 | ||
| 2484 | assert(nsec === null, 'nsec must always be set to null') | |
| 2485 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2486 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2487 | assert( | |
| 2488 | c.byteLength === m.byteLength + binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2489 | "c must be 'm.byteLength + crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2490 | ) | |
| 2491 | assert(c.byteLength <= 0xffffffff, 'c.byteLength must be a 32bit integer') | |
| 2492 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2493 | assert( | |
| 2494 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2495 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2496 | ) | |
| 2497 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2498 | assert( | |
| 2499 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2500 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2501 | ) | |
| 2502 | ||
| 2503 | const res = binding.crypto_aead_xchacha20poly1305_ietf_encrypt(c, m, ad, npub, k) | |
| 2504 | ||
| 2505 | if (res < 0) throw new Error('could not encrypt data') | |
| 2506 | ||
| 2507 | return res | |
| 2508 | } | |
| 2509 | ||
| 2510 | exports.crypto_aead_xchacha20poly1305_ietf_decrypt = function (m, nsec, c, ad, npub, k) { | |
| 2511 | if (!ad) ad = undefined | |
| 2512 | ||
| 2513 | assert(nsec === null, 'nsec must always be set to null') | |
| 2514 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2515 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2516 | assert( | |
| 2517 | m.byteLength === c.byteLength - binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2518 | "m must be 'c.byteLength - crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2519 | ) | |
| 2520 | assert(m.byteLength <= 0xffffffff, 'm.byteLength must be a 32bit integer') | |
| 2521 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2522 | assert( | |
| 2523 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2524 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2525 | ) | |
| 2526 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2527 | assert( | |
| 2528 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2529 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2530 | ) | |
| 2531 | ||
| 2532 | const res = binding.crypto_aead_xchacha20poly1305_ietf_decrypt(m, c, ad, npub, k) | |
| 2533 | ||
| 2534 | if (res < 0) throw new Error('could not verify data') | |
| 2535 | ||
| 2536 | return res | |
| 2537 | } | |
| 2538 | ||
| 2539 | exports.crypto_aead_xchacha20poly1305_ietf_encrypt_detached = function ( | |
| 2540 | c, | |
| 2541 | mac, | |
| 2542 | m, | |
| 2543 | ad, | |
| 2544 | nsec, | |
| 2545 | npub, | |
| 2546 | k | |
| 2547 | ) { | |
| 2548 | if (!ad) ad = undefined | |
| 2549 | ||
| 2550 | assert(nsec === null, 'nsec must always be set to null') | |
| 2551 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2552 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2553 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2554 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2555 | assert( | |
| 2556 | mac.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2557 | "mac must be 'crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2558 | ) | |
| 2559 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2560 | assert( | |
| 2561 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2562 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2563 | ) | |
| 2564 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2565 | assert( | |
| 2566 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2567 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2568 | ) | |
| 2569 | ||
| 2570 | const res = binding.crypto_aead_xchacha20poly1305_ietf_encrypt_detached(c, mac, m, ad, npub, k) | |
| 2571 | ||
| 2572 | if (res < 0) throw new Error('could not encrypt data') | |
| 2573 | ||
| 2574 | return res | |
| 2575 | } | |
| 2576 | ||
| 2577 | exports.crypto_aead_xchacha20poly1305_ietf_decrypt_detached = function ( | |
| 2578 | m, | |
| 2579 | nsec, | |
| 2580 | c, | |
| 2581 | mac, | |
| 2582 | ad, | |
| 2583 | npub, | |
| 2584 | k | |
| 2585 | ) { | |
| 2586 | if (!ad) ad = undefined | |
| 2587 | ||
| 2588 | assert(nsec === null, 'nsec must always be set to null') | |
| 2589 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2590 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2591 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 2592 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2593 | assert( | |
| 2594 | mac.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2595 | "mac must be 'crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2596 | ) | |
| 2597 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2598 | assert( | |
| 2599 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2600 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2601 | ) | |
| 2602 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2603 | assert( | |
| 2604 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2605 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2606 | ) | |
| 2607 | ||
| 2608 | const res = binding.crypto_aead_xchacha20poly1305_ietf_decrypt_detached(m, c, mac, ad, npub, k) | |
| 2609 | ||
| 2610 | if (res !== 0) throw new Error('could not verify data') | |
| 2611 | } | |
| 2612 | ||
| 2613 | exports.crypto_aead_chacha20poly1305_ietf_keygen = function (k) { | |
| 2614 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2615 | assert( | |
| 2616 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2617 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2618 | ) | |
| 2619 | ||
| 2620 | binding.crypto_aead_chacha20poly1305_ietf_keygen(k) | |
| 2621 | } | |
| 2622 | ||
| 2623 | exports.crypto_aead_chacha20poly1305_ietf_encrypt = function (c, m, ad, nsec, npub, k) { | |
| 2624 | if (!ad) ad = undefined | |
| 2625 | ||
| 2626 | assert(nsec === null, 'nsec must always be set to null') | |
| 2627 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2628 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2629 | assert( | |
| 2630 | c.byteLength === m.byteLength + binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2631 | "c must be 'm.byteLength + crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2632 | ) | |
| 2633 | assert(c.byteLength <= 0xffffffff, 'c.byteLength must be a 32bit integer') | |
| 2634 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2635 | assert( | |
| 2636 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2637 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2638 | ) | |
| 2639 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2640 | assert( | |
| 2641 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2642 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2643 | ) | |
| 2644 | ||
| 2645 | const res = binding.crypto_aead_chacha20poly1305_ietf_encrypt(c, m, ad, npub, k) | |
| 2646 | ||
| 2647 | if (res < 0) throw new Error('could not encrypt data') | |
| 2648 | ||
| 2649 | return res | |
| 2650 | } | |
| 2651 | ||
| 2652 | exports.crypto_aead_chacha20poly1305_ietf_decrypt = function (m, nsec, c, ad, npub, k) { | |
| 2653 | if (!ad) ad = undefined | |
| 2654 | ||
| 2655 | assert(nsec === null, 'nsec must always be set to null') | |
| 2656 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2657 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2658 | assert( | |
| 2659 | m.byteLength === c.byteLength - binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2660 | "m must be 'c.byteLength - crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2661 | ) | |
| 2662 | assert(m.byteLength <= 0xffffffff, 'm.byteLength must be a 32bit integer') | |
| 2663 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2664 | assert( | |
| 2665 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2666 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2667 | ) | |
| 2668 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2669 | assert( | |
| 2670 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2671 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2672 | ) | |
| 2673 | ||
| 2674 | const res = binding.crypto_aead_chacha20poly1305_ietf_decrypt(m, c, ad, npub, k) | |
| 2675 | ||
| 2676 | if (res < 0) throw new Error('could not verify data') | |
| 2677 | ||
| 2678 | return res | |
| 2679 | } | |
| 2680 | ||
| 2681 | exports.crypto_aead_chacha20poly1305_ietf_encrypt_detached = function ( | |
| 2682 | c, | |
| 2683 | mac, | |
| 2684 | m, | |
| 2685 | ad, | |
| 2686 | nsec, | |
| 2687 | npub, | |
| 2688 | k | |
| 2689 | ) { | |
| 2690 | if (!ad) ad = undefined | |
| 2691 | ||
| 2692 | assert(nsec === null, 'nsec must always be set to null') | |
| 2693 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2694 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2695 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2696 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2697 | assert( | |
| 2698 | mac.byteLength === binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2699 | "mac must be 'crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2700 | ) | |
| 2701 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2702 | assert( | |
| 2703 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2704 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2705 | ) | |
| 2706 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2707 | assert( | |
| 2708 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2709 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2710 | ) | |
| 2711 | ||
| 2712 | const res = binding.crypto_aead_chacha20poly1305_ietf_encrypt_detached(c, mac, m, ad, npub, k) | |
| 2713 | ||
| 2714 | if (res < 0) throw new Error('could not encrypt data') | |
| 2715 | ||
| 2716 | return res | |
| 2717 | } | |
| 2718 | ||
| 2719 | exports.crypto_aead_chacha20poly1305_ietf_decrypt_detached = function ( | |
| 2720 | m, | |
| 2721 | nsec, | |
| 2722 | c, | |
| 2723 | mac, | |
| 2724 | ad, | |
| 2725 | npub, | |
| 2726 | k | |
| 2727 | ) { | |
| 2728 | if (!ad) ad = undefined | |
| 2729 | ||
| 2730 | assert(nsec === null, 'nsec must always be set to null') | |
| 2731 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2732 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2733 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 2734 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2735 | assert( | |
| 2736 | mac.byteLength === binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2737 | "mac must be 'crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2738 | ) | |
| 2739 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2740 | assert( | |
| 2741 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2742 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2743 | ) | |
| 2744 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2745 | assert( | |
| 2746 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2747 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2748 | ) | |
| 2749 | ||
| 2750 | const res = binding.crypto_aead_chacha20poly1305_ietf_decrypt_detached(m, c, mac, ad, npub, k) | |
| 2751 | ||
| 2752 | if (res !== 0) throw new Error('could not verify data') | |
| 2753 | } | |
| 2754 | ||
| 2755 | // crypto_stream | |
| 2756 | ||
| 2757 | exports.crypto_stream_xor_wrap_init = function (state, n, k) { | |
| 2758 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2759 | assert( | |
| 2760 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2761 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2762 | ) | |
| 2763 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2764 | assert( | |
| 2765 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 2766 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 2767 | ) | |
| 2768 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2769 | assert( | |
| 2770 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 2771 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 2772 | ) | |
| 2773 | ||
| 2774 | binding.crypto_stream_xor_wrap_init(state, n, k) | |
| 2775 | } | |
| 2776 | ||
| 2777 | exports.crypto_stream_xor_wrap_update = function (state, c, m) { | |
| 2778 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2779 | assert( | |
| 2780 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2781 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2782 | ) | |
| 2783 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2784 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2785 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2786 | ||
| 2787 | binding.crypto_stream_xor_wrap_update(state, c, m) | |
| 2788 | } | |
| 2789 | ||
| 2790 | exports.crypto_stream_xor_wrap_final = function (state) { | |
| 2791 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2792 | assert( | |
| 2793 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2794 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2795 | ) | |
| 2796 | ||
| 2797 | binding.crypto_stream_xor_wrap_final(state) | |
| 2798 | } | |
| 2799 | ||
| 2800 | exports.crypto_stream_chacha20_xor_wrap_init = function (state, n, k) { | |
| 2801 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2802 | assert( | |
| 2803 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2804 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2805 | ) | |
| 2806 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2807 | assert( | |
| 2808 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 2809 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 2810 | ) | |
| 2811 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2812 | assert( | |
| 2813 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 2814 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 2815 | ) | |
| 2816 | ||
| 2817 | binding.crypto_stream_chacha20_xor_wrap_init(state, n, k) | |
| 2818 | } | |
| 2819 | ||
| 2820 | exports.crypto_stream_chacha20_xor_wrap_update = function (state, c, m) { | |
| 2821 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2822 | assert( | |
| 2823 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2824 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2825 | ) | |
| 2826 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2827 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2828 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2829 | ||
| 2830 | binding.crypto_stream_chacha20_xor_wrap_update(state, c, m) | |
| 2831 | } | |
| 2832 | ||
| 2833 | exports.crypto_stream_chacha20_xor_wrap_final = function (state) { | |
| 2834 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2835 | assert( | |
| 2836 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2837 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2838 | ) | |
| 2839 | ||
| 2840 | binding.crypto_stream_chacha20_xor_wrap_final(state) | |
| 2841 | } | |
| 2842 | ||
| 2843 | exports.crypto_stream_chacha20_ietf_xor_wrap_init = function (state, n, k) { | |
| 2844 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2845 | assert( | |
| 2846 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2847 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2848 | ) | |
| 2849 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2850 | assert( | |
| 2851 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 2852 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 2853 | ) | |
| 2854 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2855 | assert( | |
| 2856 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 2857 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 2858 | ) | |
| 2859 | ||
| 2860 | binding.crypto_stream_chacha20_ietf_xor_wrap_init(state, n, k) | |
| 2861 | } | |
| 2862 | ||
| 2863 | exports.crypto_stream_chacha20_ietf_xor_wrap_update = function (state, c, m) { | |
| 2864 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2865 | assert( | |
| 2866 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2867 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2868 | ) | |
| 2869 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2870 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2871 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2872 | ||
| 2873 | binding.crypto_stream_chacha20_ietf_xor_wrap_update(state, c, m) | |
| 2874 | } | |
| 2875 | ||
| 2876 | exports.crypto_stream_chacha20_ietf_xor_wrap_final = function (state) { | |
| 2877 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2878 | assert( | |
| 2879 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2880 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2881 | ) | |
| 2882 | ||
| 2883 | binding.crypto_stream_chacha20_ietf_xor_wrap_final(state) | |
| 2884 | } | |
| 2885 | ||
| 2886 | exports.crypto_stream_xchacha20_xor_wrap_init = function (state, n, k) { | |
| 2887 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2888 | assert( | |
| 2889 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2890 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2891 | ) | |
| 2892 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2893 | assert( | |
| 2894 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 2895 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 2896 | ) | |
| 2897 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2898 | assert( | |
| 2899 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 2900 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 2901 | ) | |
| 2902 | ||
| 2903 | binding.crypto_stream_xchacha20_xor_wrap_init(state, n, k) | |
| 2904 | } | |
| 2905 | ||
| 2906 | exports.crypto_stream_xchacha20_xor_wrap_update = function (state, c, m) { | |
| 2907 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2908 | assert( | |
| 2909 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2910 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2911 | ) | |
| 2912 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2913 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2914 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2915 | ||
| 2916 | binding.crypto_stream_xchacha20_xor_wrap_update(state, c, m) | |
| 2917 | } | |
| 2918 | ||
| 2919 | exports.crypto_stream_xchacha20_xor_wrap_final = function (state) { | |
| 2920 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2921 | assert( | |
| 2922 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2923 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2924 | ) | |
| 2925 | ||
| 2926 | binding.crypto_stream_xchacha20_xor_wrap_final(state) | |
| 2927 | } | |
| 2928 | ||
| 2929 | exports.crypto_stream_salsa20_xor_wrap_init = function (state, n, k) { | |
| 2930 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2931 | assert( | |
| 2932 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2933 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2934 | ) | |
| 2935 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2936 | assert( | |
| 2937 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 2938 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 2939 | ) | |
| 2940 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2941 | assert( | |
| 2942 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 2943 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 2944 | ) | |
| 2945 | ||
| 2946 | binding.crypto_stream_salsa20_xor_wrap_init(state, n, k) | |
| 2947 | } | |
| 2948 | ||
| 2949 | exports.crypto_stream_salsa20_xor_wrap_update = function (state, c, m) { | |
| 2950 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2951 | assert( | |
| 2952 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2953 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2954 | ) | |
| 2955 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2956 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2957 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2958 | ||
| 2959 | binding.crypto_stream_salsa20_xor_wrap_update(state, c, m) | |
| 2960 | } | |
| 2961 | ||
| 2962 | exports.crypto_stream_salsa20_xor_wrap_final = function (state) { | |
| 2963 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2964 | assert( | |
| 2965 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2966 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2967 | ) | |
| 2968 | ||
| 2969 | binding.crypto_stream_salsa20_xor_wrap_final(state) | |
| 2970 | } | |
| 2971 | ||
| 2972 | // experimental | |
| 2973 | ||
| 2974 | exports.extension_tweak_ed25519_base = function (n, p, ns) { | |
| 2975 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2976 | assert( | |
| 2977 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 2978 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 2979 | ) | |
| 2980 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2981 | assert( | |
| 2982 | p.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 2983 | "p must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 2984 | ) | |
| 2985 | ||
| 2986 | binding.extension_tweak_ed25519_base(n, p, ns) | |
| 2987 | } | |
| 2988 | ||
| 2989 | exports.extension_tweak_ed25519_sign_detached = function (sig, m, scalar, pk) { | |
| 2990 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 2991 | assert(sig.byteLength === binding.crypto_sign_BYTES, "sig must be 'crypto_sign_BYTES' bytes") | |
| 2992 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2993 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 2994 | assert( | |
| 2995 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 2996 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 2997 | ) | |
| 2998 | ||
| 2999 | if (pk) { | |
| 3000 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3001 | assert( | |
| 3002 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3003 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3004 | ) | |
| 3005 | } | |
| 3006 | ||
| 3007 | const res = binding.extension_tweak_ed25519_sign_detached(sig, m, scalar, pk) | |
| 3008 | ||
| 3009 | if (res !== 0) throw new Error('failed to compute signature') | |
| 3010 | } | |
| 3011 | ||
| 3012 | exports.extension_tweak_ed25519_sk_to_scalar = function (n, sk) { | |
| 3013 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 3014 | assert( | |
| 3015 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3016 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3017 | ) | |
| 3018 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 3019 | assert( | |
| 3020 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 3021 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 3022 | ) | |
| 3023 | ||
| 3024 | binding.extension_tweak_ed25519_sk_to_scalar(n, sk) | |
| 3025 | } | |
| 3026 | ||
| 3027 | exports.extension_tweak_ed25519_scalar = function (scalarOut, scalar, ns) { | |
| 3028 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3029 | assert( | |
| 3030 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3031 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3032 | ) | |
| 3033 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 3034 | assert( | |
| 3035 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3036 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3037 | ) | |
| 3038 | ||
| 3039 | binding.extension_tweak_ed25519_scalar(scalarOut, scalar, ns) | |
| 3040 | } | |
| 3041 | ||
| 3042 | exports.extension_tweak_ed25519_pk = function (tpk, pk, ns) { | |
| 3043 | assert(ArrayBuffer.isView(tpk), 'tpk must be a typed array') | |
| 3044 | assert( | |
| 3045 | tpk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3046 | "tpk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3047 | ) | |
| 3048 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3049 | assert( | |
| 3050 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3051 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3052 | ) | |
| 3053 | ||
| 3054 | const res = binding.extension_tweak_ed25519_pk(tpk, pk, ns) | |
| 3055 | ||
| 3056 | if (res !== 0) throw new Error('failed to tweak public key') | |
| 3057 | } | |
| 3058 | ||
| 3059 | exports.extension_tweak_ed25519_keypair = function (pk, scalarOut, scalarIn, ns) { | |
| 3060 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3061 | assert( | |
| 3062 | pk.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 3063 | "pk must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 3064 | ) | |
| 3065 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3066 | assert( | |
| 3067 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3068 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3069 | ) | |
| 3070 | assert(ArrayBuffer.isView(scalarIn), 'scalarIn must be a typed array') | |
| 3071 | assert( | |
| 3072 | scalarIn.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3073 | "scalarIn must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3074 | ) | |
| 3075 | ||
| 3076 | binding.extension_tweak_ed25519_keypair(pk, scalarOut, scalarIn, ns) | |
| 3077 | } | |
| 3078 | ||
| 3079 | exports.extension_tweak_ed25519_scalar_add = function (scalarOut, scalar, n) { | |
| 3080 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3081 | assert( | |
| 3082 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3083 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3084 | ) | |
| 3085 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 3086 | assert( | |
| 3087 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3088 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3089 | ) | |
| 3090 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 3091 | assert( | |
| 3092 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3093 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3094 | ) | |
| 3095 | ||
| 3096 | binding.extension_tweak_ed25519_scalar_add(scalarOut, scalar, n) | |
| 3097 | } | |
| 3098 | ||
| 3099 | exports.extension_tweak_ed25519_pk_add = function (tpk, pk, p) { | |
| 3100 | assert(ArrayBuffer.isView(tpk), 'tpk must be a typed array') | |
| 3101 | assert( | |
| 3102 | tpk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3103 | "tpk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3104 | ) | |
| 3105 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3106 | assert( | |
| 3107 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3108 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3109 | ) | |
| 3110 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 3111 | assert( | |
| 3112 | p.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3113 | "p must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3114 | ) | |
| 3115 | ||
| 3116 | const res = binding.extension_tweak_ed25519_pk_add(tpk, pk, p) | |
| 3117 | ||
| 3118 | if (res !== 0) throw new Error('failed to add tweak to public key') | |
| 3119 | } | |
| 3120 | ||
| 3121 | exports.extension_tweak_ed25519_keypair_add = function (pk, scalarOut, scalarIn, tweak) { | |
| 3122 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3123 | assert( | |
| 3124 | pk.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 3125 | "pk must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 3126 | ) | |
| 3127 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3128 | assert( | |
| 3129 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3130 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3131 | ) | |
| 3132 | assert(ArrayBuffer.isView(scalarIn), 'scalarIn must be a typed array') | |
| 3133 | assert( | |
| 3134 | scalarIn.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3135 | "scalarIn must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3136 | ) | |
| 3137 | assert(ArrayBuffer.isView(tweak), 'tweak must be a typed array') | |
| 3138 | assert( | |
| 3139 | tweak.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3140 | "tweak must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3141 | ) | |
| 3142 | ||
| 3143 | const res = binding.extension_tweak_ed25519_keypair_add(pk, scalarOut, scalarIn, tweak) | |
| 3144 | ||
| 3145 | if (res !== 0) throw new Error('failed to add tweak to keypair') | |
| 3146 | } | |
| 3147 | ||
| 3148 | exports.extension_pbkdf2_sha512_async = function (out, passwd, salt, iter, outlen, callback) { | |
| 3149 | assert( | |
| 3150 | iter >= binding.extension_pbkdf2_sha512_ITERATIONS_MIN, | |
| 3151 | "iter must be at least 'extension_pbkdf2_sha512_ITERATIONS_MIN'" | |
| 3152 | ) | |
| 3153 | assert( | |
| 3154 | outlen <= binding.extension_pbkdf2_sha512_BYTES_MAX, | |
| 3155 | "outlen must be at most 'extension_pbkdf2_sha512_BYTES_MAX'" | |
| 3156 | ) | |
| 3157 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 3158 | assert(out.byteLength >= outlen, "out must be at least 'outlen' bytes") | |
| 3159 | assert(out.byteLength > 0, 'out must not be empty') | |
| 3160 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 3161 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 3162 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 3163 | assert(salt.byteLength > 0, 'salt must not be empty') | |
| 3164 | ||
| 3165 | const [done, promise] = checkStatus(callback) | |
| 3166 | ||
| 3167 | binding.extension_pbkdf2_sha512_async( | |
| 3168 | out.buffer, | |
| 3169 | out.byteOffset, | |
| 3170 | out.byteLength, | |
| 3171 | ||
| 3172 | passwd.buffer, | |
| 3173 | passwd.byteOffset, | |
| 3174 | passwd.byteLength, | |
| 3175 | ||
| 3176 | salt.buffer, | |
| 3177 | salt.byteOffset, | |
| 3178 | salt.byteLength, | |
| 3179 | ||
| 3180 | iter, | |
| 3181 | outlen, | |
| 3182 | ||
| 3183 | done | |
| 3184 | ) | |
| 3185 | ||
| 3186 | return promise | |
| 3187 | } | |
| 3188 | ||
| 3189 | exports.extension_pbkdf2_sha512 = function (out, passwd, salt, iter, outlen) { | |
| 3190 | assert( | |
| 3191 | iter >= binding.extension_pbkdf2_sha512_ITERATIONS_MIN, | |
| 3192 | "iter must be at least 'extension_pbkdf2_sha512_ITERATIONS_MIN'" | |
| 3193 | ) | |
| 3194 | assert( | |
| 3195 | outlen <= binding.extension_pbkdf2_sha512_BYTES_MAX, | |
| 3196 | "outlen must be at most 'extension_pbkdf2_sha512_BYTES_MAX'" | |
| 3197 | ) | |
| 3198 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 3199 | assert(out.byteLength >= outlen, "out must be at least 'outlen' bytes") | |
| 3200 | assert(out.byteLength > 0, 'out must not be empty') | |
| 3201 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 3202 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 3203 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 3204 | assert(salt.byteLength > 0, 'salt must not be empty') | |
| 3205 | ||
| 3206 | const res = binding.extension_pbkdf2_sha512(out, passwd, salt, iter, outlen) | |
| 3207 | ||
| 3208 | if (res !== 0) throw new Error('failed to add tweak to public key') | |
| 3209 | } | |
| 3210 | ||
| 3211 | function checkStatus(callback, booleanResult = false) { | |
| 3212 | let done, promise | |
| 3213 | ||
| 3214 | if (typeof callback === 'function') { | |
| 3215 | done = function (status) { | |
| 3216 | if (booleanResult) callback(null, status === 0) | |
| 3217 | else if (status === 0) callback(null) | |
| 3218 | else callback(new Error('status: ' + status)) | |
| 3219 | } | |
| 3220 | } else { | |
| 3221 | promise = new Promise(function (resolve, reject) { | |
| 3222 | done = function (status) { | |
| 3223 | if (booleanResult) resolve(status === 0) | |
| 3224 | else if (status === 0) resolve() | |
| 3225 | else reject(new Error('status: ' + status)) | |
| 3226 | } | |
| 3227 | }) | |
| 3228 | } | |
| 3229 | ||
| 3230 | return [done, promise] | |
| 3231 | } |
| … | … | @@ -0,0 +1,62 @@ |
| 1 | { | |
| 2 | "name": "sodium-native", | |
| 3 | "version": "5.1.0", | |
| 4 | "description": "Low level bindings for libsodium", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js", | |
| 8 | "binding.cc", | |
| 9 | "binding.js", | |
| 10 | "extensions", | |
| 11 | "prebuilds", | |
| 12 | "CMakeLists.txt" | |
| 13 | ], | |
| 14 | "imports": { | |
| 15 | "assert": { | |
| 16 | "bare": "bare-assert", | |
| 17 | "node": "assert" | |
| 18 | } | |
| 19 | }, | |
| 20 | "addon": true, | |
| 21 | "dependencies": { | |
| 22 | "bare-assert": "^1.2.0", | |
| 23 | "require-addon": "^1.1.0", | |
| 24 | "which-runtime": "^1.2.1" | |
| 25 | }, | |
| 26 | "devDependencies": { | |
| 27 | "bare-compat-napi": "^1.3.5", | |
| 28 | "brittle": "^3.16.2", | |
| 29 | "cmake-bare": "^1.6.1", | |
| 30 | "cmake-fetch": "^1.4.3", | |
| 31 | "cmake-napi": "^1.2.1", | |
| 32 | "prettier": "^3.6.2", | |
| 33 | "prettier-config-holepunch": "^2.0.0" | |
| 34 | }, | |
| 35 | "scripts": { | |
| 36 | "test": "prettier . --check && npm run test:node && npm run test:bare", | |
| 37 | "test:node": "node test/all.js", | |
| 38 | "test:bare": "bare test/all.js" | |
| 39 | }, | |
| 40 | "standard": { | |
| 41 | "ignore": [ | |
| 42 | "/test/fixtures/*.js" | |
| 43 | ] | |
| 44 | }, | |
| 45 | "engines": { | |
| 46 | "bare": ">=1.16.0" | |
| 47 | }, | |
| 48 | "repository": { | |
| 49 | "type": "git", | |
| 50 | "url": "git+https://github.com/holepunchto/sodium-native.git" | |
| 51 | }, | |
| 52 | "contributors": [ | |
| 53 | "Emil Bay <github@tixz.dk> (http://bayes.dk)", | |
| 54 | "Mathias Buus <mathiasbuus@gmail.com> (https://mafinto.sh)", | |
| 55 | "Christophe Diederichs <chm-diederichs@hyperdivision.dk>" | |
| 56 | ], | |
| 57 | "license": "MIT", | |
| 58 | "bugs": { | |
| 59 | "url": "https://github.com/holepunchto/sodium-native/issues" | |
| 60 | }, | |
| 61 | "homepage": "https://github.com/holepunchto/sodium-native" | |
| 62 | } |
| … | … | @@ -0,0 +1,7 @@ |
| 1 | Copyright 2017 Mathias Buus & Emil Bay | |
| 2 | ||
| 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
| 4 | ||
| 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
| 6 | ||
| 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| … | … | @@ -0,0 +1,288 @@ |
| 1 | # sodium-universal | |
| 2 | ||
| 3 | Universal wrapper for `sodium-javascript` and `sodium-native` working in Node.js and the Browser | |
| 4 | ||
| 5 | ```sh | |
| 6 | npm install sodium-universal | |
| 7 | ``` | |
| 8 | ||
| 9 | `sodium-native` ships with prebuilds for macOS, Windows, and Linux. | |
| 10 | ||
| 11 | Note that the module itself is generated on a prepublish hook. | |
| 12 | ||
| 13 | ## Usage | |
| 14 | ||
| 15 | ```js | |
| 16 | var sodium = require('sodium-universal') | |
| 17 | ||
| 18 | var rnd = Buffer.allocUnsafe(12) // Cryptographically random data | |
| 19 | sodium.randombytes_buf(rnd) | |
| 20 | ||
| 21 | console.log(rnd.toString('hex')) | |
| 22 | ||
| 23 | ``` | |
| 24 | ||
| 25 | Works seamlessly with Node.js: | |
| 26 | ||
| 27 | ```sh | |
| 28 | $ node example.js | |
| 29 | c7dbd46a6cc84ff2e0d1285c | |
| 30 | ``` | |
| 31 | ||
| 32 | And the browser: | |
| 33 | ||
| 34 | First install the peer dep, `sodium-javascript`. | |
| 35 | ||
| 36 | ```sh | |
| 37 | npm install --save sodium-javascript | |
| 38 | ``` | |
| 39 | ||
| 40 | And then browserify | |
| 41 | ||
| 42 | ```sh | |
| 43 | browserify example.js > bundle.js | |
| 44 | ``` | |
| 45 | ||
| 46 | ## Introduction | |
| 47 | ||
| 48 | [`libsodium`](https://download.libsodium.org/doc/) is a collection of cryptographic primitives, providing a low-level foundation to build higher-level cryptographic applications and protocols. It is often put in contrast to RSA based cryptography and OpenSSL, even though they all share overlapping algorithms and target slightly different audiences. Sodium is a collection of modern collection of primitives, fulfilling the same cryptographic tasks, but based on simpler and more efficient algorithms. | |
| 49 | ||
| 50 | This library provides seamless bindings to [`sodium-native`](https://github.com/sodium-friends/sodium-native), which is the original C implementation of `libsodium` exposed as a Node native module. For the browser we expose [`sodium-javascript`](https://github.com/sodium-friends/sodium-javascript), using the `package.json` [`browser`](https://github.com/defunctzombie/package-browser-field-spec) field, which is supported by most bundlers. | |
| 51 | ||
| 52 | ## API | |
| 53 | ||
| 54 | Please refer to [`sodium-native`](https://github.com/sodium-friends/sodium-native#api) and [`sodium-javascript`](https://github.com/sodium-friends/sodium-javascript#api). Note that the two modules do not yet have feature parity, where `sodium-native` is the more featureful of the two. | |
| 55 | ||
| 56 | ## Compatibilty | |
| 57 | ||
| 58 | <!-- Generated by build-scripts/symbols.js with | |
| 59 | sodium-native@3.2.0 | |
| 60 | sodium-javascript@0.6.0 --> | |
| 61 | | C Library Symbol | `sodium-native` | `sodium-javascript` | | |
| 62 | |:--------------------------------------------------------:|:--------------------:|:--------------------:| | |
| 63 | | `crypto_aead_chacha20poly1305_ietf_ABYTES` | :white_check_mark: | :white_check_mark: | | |
| 64 | | `crypto_aead_chacha20poly1305_ietf_decrypt` | :white_check_mark: | :white_check_mark: | | |
| 65 | | `crypto_aead_chacha20poly1305_ietf_decrypt_detached` | :white_check_mark: | :white_check_mark: | | |
| 66 | | `crypto_aead_chacha20poly1305_ietf_encrypt` | :white_check_mark: | :white_check_mark: | | |
| 67 | | `crypto_aead_chacha20poly1305_ietf_encrypt_detached` | :white_check_mark: | :white_check_mark: | | |
| 68 | | `crypto_aead_chacha20poly1305_ietf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 69 | | `crypto_aead_chacha20poly1305_ietf_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 70 | | `crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 71 | | `crypto_aead_chacha20poly1305_ietf_NPUBBYTES` | :white_check_mark: | :white_check_mark: | | |
| 72 | | `crypto_aead_chacha20poly1305_ietf_NSECBYTES` | :white_check_mark: | :white_check_mark: | | |
| 73 | | `crypto_aead_xchacha20poly1305_ietf_ABYTES` | :white_check_mark: | :small_red_triangle: | | |
| 74 | | `crypto_aead_xchacha20poly1305_ietf_decrypt` | :white_check_mark: | :small_red_triangle: | | |
| 75 | | `crypto_aead_xchacha20poly1305_ietf_decrypt_detached` | :white_check_mark: | :small_red_triangle: | | |
| 76 | | `crypto_aead_xchacha20poly1305_ietf_encrypt` | :white_check_mark: | :small_red_triangle: | | |
| 77 | | `crypto_aead_xchacha20poly1305_ietf_encrypt_detached` | :white_check_mark: | :small_red_triangle: | | |
| 78 | | `crypto_aead_xchacha20poly1305_ietf_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 79 | | `crypto_aead_xchacha20poly1305_ietf_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 80 | | `crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 81 | | `crypto_aead_xchacha20poly1305_ietf_NPUBBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 82 | | `crypto_aead_xchacha20poly1305_ietf_NSECBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 83 | | `crypto_auth` | :white_check_mark: | :small_red_triangle: | | |
| 84 | | `crypto_auth_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 85 | | `crypto_auth_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 86 | | `crypto_auth_verify` | :white_check_mark: | :small_red_triangle: | | |
| 87 | | `crypto_box_BEFORENMBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 88 | | `crypto_box_BOXZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 89 | | `crypto_box_detached` | :white_check_mark: | :small_red_triangle: | | |
| 90 | | `crypto_box_easy` | :white_check_mark: | :small_red_triangle: | | |
| 91 | | `crypto_box_keypair` | :white_check_mark: | :white_check_mark: | | |
| 92 | | `crypto_box_MACBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 93 | | `crypto_box_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 94 | | `crypto_box_open_detached` | :white_check_mark: | :small_red_triangle: | | |
| 95 | | `crypto_box_open_easy` | :white_check_mark: | :small_red_triangle: | | |
| 96 | | `crypto_box_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 97 | | `crypto_box_seal` | :white_check_mark: | :white_check_mark: | | |
| 98 | | `crypto_box_seal_open` | :white_check_mark: | :white_check_mark: | | |
| 99 | | `crypto_box_SEALBYTES` | :white_check_mark: | :white_check_mark: | | |
| 100 | | `crypto_box_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 101 | | `crypto_box_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 102 | | `crypto_box_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 103 | | `crypto_box_ZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 104 | | `crypto_core_ed25519_add` | :white_check_mark: | :small_red_triangle: | | |
| 105 | | `crypto_core_ed25519_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 106 | | `crypto_core_ed25519_from_uniform` | :white_check_mark: | :small_red_triangle: | | |
| 107 | | `crypto_core_ed25519_is_valid_point` | :white_check_mark: | :small_red_triangle: | | |
| 108 | | `crypto_core_ed25519_NONREDUCEDSCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 109 | | `crypto_core_ed25519_scalar_add` | :white_check_mark: | :small_red_triangle: | | |
| 110 | | `crypto_core_ed25519_scalar_complement` | :white_check_mark: | :small_red_triangle: | | |
| 111 | | `crypto_core_ed25519_scalar_invert` | :white_check_mark: | :small_red_triangle: | | |
| 112 | | `crypto_core_ed25519_scalar_negate` | :white_check_mark: | :small_red_triangle: | | |
| 113 | | `crypto_core_ed25519_scalar_random` | :white_check_mark: | :small_red_triangle: | | |
| 114 | | `crypto_core_ed25519_scalar_reduce` | :white_check_mark: | :small_red_triangle: | | |
| 115 | | `crypto_core_ed25519_scalar_sub` | :white_check_mark: | :small_red_triangle: | | |
| 116 | | `crypto_core_ed25519_SCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 117 | | `crypto_core_ed25519_sub` | :white_check_mark: | :small_red_triangle: | | |
| 118 | | `crypto_core_ed25519_UNIFORMBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 119 | | `crypto_generichash` | :white_check_mark: | :white_check_mark: | | |
| 120 | | `crypto_generichash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 121 | | `crypto_generichash_BYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 122 | | `crypto_generichash_BYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 123 | | `crypto_generichash_final` | :white_check_mark: | :small_red_triangle: | | |
| 124 | | `crypto_generichash_init` | :white_check_mark: | :small_red_triangle: | | |
| 125 | | `crypto_generichash_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 126 | | `crypto_generichash_KEYBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 127 | | `crypto_generichash_KEYBYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 128 | | `crypto_generichash_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 129 | | `crypto_generichash_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 130 | | `crypto_generichash_update` | :white_check_mark: | :small_red_triangle: | | |
| 131 | | `crypto_hash` | :white_check_mark: | :white_check_mark: | | |
| 132 | | `crypto_hash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 133 | | `crypto_hash_sha256` | :white_check_mark: | :white_check_mark: | | |
| 134 | | `crypto_hash_sha256_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 135 | | `crypto_hash_sha256_final` | :white_check_mark: | :small_red_triangle: | | |
| 136 | | `crypto_hash_sha256_init` | :white_check_mark: | :small_red_triangle: | | |
| 137 | | `crypto_hash_sha256_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 138 | | `crypto_hash_sha256_update` | :white_check_mark: | :small_red_triangle: | | |
| 139 | | `crypto_hash_sha512` | :white_check_mark: | :white_check_mark: | | |
| 140 | | `crypto_hash_sha512_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 141 | | `crypto_hash_sha512_final` | :white_check_mark: | :small_red_triangle: | | |
| 142 | | `crypto_hash_sha512_init` | :white_check_mark: | :small_red_triangle: | | |
| 143 | | `crypto_hash_sha512_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 144 | | `crypto_hash_sha512_update` | :white_check_mark: | :small_red_triangle: | | |
| 145 | | `crypto_kdf_BYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 146 | | `crypto_kdf_BYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 147 | | `crypto_kdf_CONTEXTBYTES` | :white_check_mark: | :white_check_mark: | | |
| 148 | | `crypto_kdf_derive_from_key` | :white_check_mark: | :white_check_mark: | | |
| 149 | | `crypto_kdf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 150 | | `crypto_kdf_keygen` | :white_check_mark: | :white_check_mark: | | |
| 151 | | `crypto_kx_client_session_keys` | :white_check_mark: | :small_red_triangle: | | |
| 152 | | `crypto_kx_keypair` | :white_check_mark: | :white_check_mark: | | |
| 153 | | `crypto_kx_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 154 | | `crypto_kx_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 155 | | `crypto_kx_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 156 | | `crypto_kx_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 157 | | `crypto_kx_server_session_keys` | :white_check_mark: | :small_red_triangle: | | |
| 158 | | `crypto_kx_SESSIONKEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 159 | | `crypto_onetimeauth` | :white_check_mark: | :white_check_mark: | | |
| 160 | | `crypto_onetimeauth_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 161 | | `crypto_onetimeauth_final` | :white_check_mark: | :small_red_triangle: | | |
| 162 | | `crypto_onetimeauth_init` | :white_check_mark: | :small_red_triangle: | | |
| 163 | | `crypto_onetimeauth_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 164 | | `crypto_onetimeauth_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 165 | | `crypto_onetimeauth_update` | :white_check_mark: | :small_red_triangle: | | |
| 166 | | `crypto_onetimeauth_verify` | :white_check_mark: | :white_check_mark: | | |
| 167 | | `crypto_pwhash` | :white_check_mark: | :small_red_triangle: | | |
| 168 | | `crypto_pwhash_BYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 169 | | `crypto_pwhash_BYTES_MIN` | :white_check_mark: | :small_red_triangle: | | |
| 170 | | `crypto_pwhash_SALTBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 171 | | `crypto_pwhash_scryptsalsa208sha256` | :white_check_mark: | :small_red_triangle: | | |
| 172 | | `crypto_pwhash_scryptsalsa208sha256_BYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 173 | | `crypto_pwhash_scryptsalsa208sha256_BYTES_MIN` | :white_check_mark: | :small_red_triangle: | | |
| 174 | | `crypto_pwhash_scryptsalsa208sha256_SALTBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 175 | | `crypto_pwhash_scryptsalsa208sha256_str` | :white_check_mark: | :small_red_triangle: | | |
| 176 | | `crypto_pwhash_scryptsalsa208sha256_str_needs_rehash` | :white_check_mark: | :small_red_triangle: | | |
| 177 | | `crypto_pwhash_scryptsalsa208sha256_str_verify` | :white_check_mark: | :small_red_triangle: | | |
| 178 | | `crypto_pwhash_scryptsalsa208sha256_STRBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 179 | | `crypto_pwhash_str` | :white_check_mark: | :small_red_triangle: | | |
| 180 | | `crypto_pwhash_str_needs_rehash` | :white_check_mark: | :small_red_triangle: | | |
| 181 | | `crypto_pwhash_str_verify` | :white_check_mark: | :small_red_triangle: | | |
| 182 | | `crypto_pwhash_STRBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 183 | | `crypto_scalarmult` | :white_check_mark: | :white_check_mark: | | |
| 184 | | `crypto_scalarmult_base` | :white_check_mark: | :white_check_mark: | | |
| 185 | | `crypto_scalarmult_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 186 | | `crypto_scalarmult_ed25519` | :white_check_mark: | :small_red_triangle: | | |
| 187 | | `crypto_scalarmult_ed25519_base` | :white_check_mark: | :small_red_triangle: | | |
| 188 | | `crypto_scalarmult_ed25519_base_noclamp` | :white_check_mark: | :small_red_triangle: | | |
| 189 | | `crypto_scalarmult_ed25519_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 190 | | `crypto_scalarmult_ed25519_noclamp` | :white_check_mark: | :small_red_triangle: | | |
| 191 | | `crypto_scalarmult_ed25519_SCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 192 | | `crypto_scalarmult_SCALARBYTES` | :white_check_mark: | :white_check_mark: | | |
| 193 | | `crypto_secretbox` | :small_red_triangle: | :white_check_mark: | | |
| 194 | | `crypto_secretbox_BOXZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 195 | | `crypto_secretbox_detached` | :white_check_mark: | :white_check_mark: | | |
| 196 | | `crypto_secretbox_easy` | :white_check_mark: | :white_check_mark: | | |
| 197 | | `crypto_secretbox_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 198 | | `crypto_secretbox_MACBYTES` | :white_check_mark: | :white_check_mark: | | |
| 199 | | `crypto_secretbox_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 200 | | `crypto_secretbox_open` | :small_red_triangle: | :white_check_mark: | | |
| 201 | | `crypto_secretbox_open_detached` | :white_check_mark: | :white_check_mark: | | |
| 202 | | `crypto_secretbox_open_easy` | :white_check_mark: | :white_check_mark: | | |
| 203 | | `crypto_secretbox_ZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 204 | | `crypto_secretstream_xchacha20poly1305_ABYTES` | :white_check_mark: | :small_red_triangle: | | |
| 205 | | `crypto_secretstream_xchacha20poly1305_HEADERBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 206 | | `crypto_secretstream_xchacha20poly1305_init_pull` | :white_check_mark: | :small_red_triangle: | | |
| 207 | | `crypto_secretstream_xchacha20poly1305_init_push` | :white_check_mark: | :small_red_triangle: | | |
| 208 | | `crypto_secretstream_xchacha20poly1305_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 209 | | `crypto_secretstream_xchacha20poly1305_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 210 | | `crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 211 | | `crypto_secretstream_xchacha20poly1305_pull` | :white_check_mark: | :small_red_triangle: | | |
| 212 | | `crypto_secretstream_xchacha20poly1305_push` | :white_check_mark: | :small_red_triangle: | | |
| 213 | | `crypto_secretstream_xchacha20poly1305_rekey` | :white_check_mark: | :small_red_triangle: | | |
| 214 | | `crypto_secretstream_xchacha20poly1305_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 215 | | `crypto_shorthash` | :white_check_mark: | :white_check_mark: | | |
| 216 | | `crypto_shorthash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 217 | | `crypto_shorthash_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 218 | | `crypto_sign` | :white_check_mark: | :white_check_mark: | | |
| 219 | | `crypto_sign_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 220 | | `crypto_sign_detached` | :white_check_mark: | :white_check_mark: | | |
| 221 | | `crypto_sign_ed25519_pk_to_curve25519` | :white_check_mark: | :small_red_triangle: | | |
| 222 | | `crypto_sign_ed25519_sk_to_curve25519` | :white_check_mark: | :small_red_triangle: | | |
| 223 | | `crypto_sign_ed25519_sk_to_pk` | :white_check_mark: | :small_red_triangle: | | |
| 224 | | `crypto_sign_keypair` | :white_check_mark: | :white_check_mark: | | |
| 225 | | `crypto_sign_open` | :white_check_mark: | :white_check_mark: | | |
| 226 | | `crypto_sign_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 227 | | `crypto_sign_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 228 | | `crypto_sign_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 229 | | `crypto_sign_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 230 | | `crypto_sign_verify_detached` | :white_check_mark: | :white_check_mark: | | |
| 231 | | `crypto_stream` | :white_check_mark: | :white_check_mark: | | |
| 232 | | `crypto_stream_chacha20` | :white_check_mark: | :white_check_mark: | | |
| 233 | | `crypto_stream_chacha20_ietf` | :white_check_mark: | :white_check_mark: | | |
| 234 | | `crypto_stream_chacha20_ietf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 235 | | `crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 236 | | `crypto_stream_chacha20_ietf_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 237 | | `crypto_stream_chacha20_ietf_xor` | :white_check_mark: | :white_check_mark: | | |
| 238 | | `crypto_stream_chacha20_ietf_xor_ic` | :white_check_mark: | :white_check_mark: | | |
| 239 | | `crypto_stream_chacha20_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 240 | | `crypto_stream_chacha20_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 241 | | `crypto_stream_chacha20_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 242 | | `crypto_stream_chacha20_xor` | :white_check_mark: | :white_check_mark: | | |
| 243 | | `crypto_stream_chacha20_xor_ic` | :white_check_mark: | :white_check_mark: | | |
| 244 | | `crypto_stream_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 245 | | `crypto_stream_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 246 | | `crypto_stream_salsa20_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 247 | | `crypto_stream_salsa20_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 248 | | `crypto_stream_salsa20_NONCEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 249 | | `crypto_stream_salsa20_xor` | :white_check_mark: | :small_red_triangle: | | |
| 250 | | `crypto_stream_salsa20_xor_ic` | :white_check_mark: | :small_red_triangle: | | |
| 251 | | `crypto_stream_xchacha20` | :white_check_mark: | :small_red_triangle: | | |
| 252 | | `crypto_stream_xchacha20_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 253 | | `crypto_stream_xchacha20_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 254 | | `crypto_stream_xchacha20_NONCEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 255 | | `crypto_stream_xchacha20_xor` | :white_check_mark: | :small_red_triangle: | | |
| 256 | | `crypto_stream_xchacha20_xor_ic` | :white_check_mark: | :small_red_triangle: | | |
| 257 | | `crypto_stream_xor` | :white_check_mark: | :white_check_mark: | | |
| 258 | | `crypto_verify_16` | :small_red_triangle: | :white_check_mark: | | |
| 259 | | `crypto_verify_32` | :small_red_triangle: | :white_check_mark: | | |
| 260 | | `randombytes_buf` | :white_check_mark: | :white_check_mark: | | |
| 261 | | `randombytes_buf_deterministic` | :white_check_mark: | :small_red_triangle: | | |
| 262 | | `randombytes_random` | :white_check_mark: | :small_red_triangle: | | |
| 263 | | `randombytes_SEEDBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 264 | | `randombytes_uniform` | :white_check_mark: | :small_red_triangle: | | |
| 265 | | `sodium_add` | :white_check_mark: | :small_red_triangle: | | |
| 266 | | `sodium_compare` | :white_check_mark: | :small_red_triangle: | | |
| 267 | | `sodium_free` | :white_check_mark: | :small_red_triangle: | | |
| 268 | | `sodium_increment` | :white_check_mark: | :small_red_triangle: | | |
| 269 | | `sodium_is_zero` | :white_check_mark: | :white_check_mark: | | |
| 270 | | `sodium_malloc` | :white_check_mark: | :white_check_mark: | | |
| 271 | | `sodium_memcmp` | :white_check_mark: | :white_check_mark: | | |
| 272 | | `sodium_memzero` | :white_check_mark: | :white_check_mark: | | |
| 273 | | `sodium_mlock` | :white_check_mark: | :small_red_triangle: | | |
| 274 | | `sodium_mprotect_noaccess` | :white_check_mark: | :small_red_triangle: | | |
| 275 | | `sodium_mprotect_readonly` | :white_check_mark: | :small_red_triangle: | | |
| 276 | | `sodium_mprotect_readwrite` | :white_check_mark: | :small_red_triangle: | | |
| 277 | | `sodium_munlock` | :white_check_mark: | :small_red_triangle: | | |
| 278 | | `sodium_pad` | :white_check_mark: | :small_red_triangle: | | |
| 279 | | `sodium_sub` | :white_check_mark: | :small_red_triangle: | | |
| 280 | | `sodium_unpad` | :white_check_mark: | :small_red_triangle: | | |
| 281 | ||
| 282 | ### Missing altogether (Ctrl + F friendly) | |
| 283 | ||
| 284 | `crypto_aead_aes256gcm_ABYTES`, `crypto_aead_aes256gcm_beforenm`, `crypto_aead_aes256gcm_decrypt`, `crypto_aead_aes256gcm_decrypt_afternm`, `crypto_aead_aes256gcm_decrypt_detached`, `crypto_aead_aes256gcm_decrypt_detached_afternm`, `crypto_aead_aes256gcm_encrypt`, `crypto_aead_aes256gcm_encrypt_afternm`, `crypto_aead_aes256gcm_encrypt_detached`, `crypto_aead_aes256gcm_encrypt_detached_afternm`, `crypto_aead_aes256gcm_is_available`, `crypto_aead_aes256gcm_KEYBYTES`, `crypto_aead_aes256gcm_keygen`, `crypto_aead_aes256gcm_MESSAGEBYTES_MAX`, `crypto_aead_aes256gcm_NPUBBYTES`, `crypto_aead_aes256gcm_NSECBYTES`, `crypto_aead_aes256gcm_STATEBYTES`, `crypto_aead_chacha20poly1305_ABYTES`, `crypto_aead_chacha20poly1305_decrypt`, `crypto_aead_chacha20poly1305_decrypt_detached`, `crypto_aead_chacha20poly1305_encrypt`, `crypto_aead_chacha20poly1305_encrypt_detached`, `crypto_aead_chacha20poly1305_KEYBYTES`, `crypto_aead_chacha20poly1305_keygen`, `crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX`, `crypto_aead_chacha20poly1305_NPUBBYTES`, `crypto_aead_chacha20poly1305_NSECBYTES`, `crypto_auth_hmacsha256`, `crypto_auth_hmacsha256_BYTES`, `crypto_auth_hmacsha256_final`, `crypto_auth_hmacsha256_init`, `crypto_auth_hmacsha256_KEYBYTES`, `crypto_auth_hmacsha256_keygen`, `crypto_auth_hmacsha256_STATEBYTES`, `crypto_auth_hmacsha256_update`, `crypto_auth_hmacsha256_verify`, `crypto_auth_hmacsha512`, `crypto_auth_hmacsha512256`, `crypto_auth_hmacsha512256_BYTES`, `crypto_auth_hmacsha512256_final`, `crypto_auth_hmacsha512256_init`, `crypto_auth_hmacsha512256_KEYBYTES`, `crypto_auth_hmacsha512256_keygen`, `crypto_auth_hmacsha512256_STATEBYTES`, `crypto_auth_hmacsha512256_update`, `crypto_auth_hmacsha512256_verify`, `crypto_auth_hmacsha512_BYTES`, `crypto_auth_hmacsha512_final`, `crypto_auth_hmacsha512_init`, `crypto_auth_hmacsha512_KEYBYTES`, `crypto_auth_hmacsha512_keygen`, `crypto_auth_hmacsha512_STATEBYTES`, `crypto_auth_hmacsha512_update`, `crypto_auth_hmacsha512_verify`, `crypto_auth_keygen`, `crypto_auth_primitive`, `crypto_box`, `crypto_box_afternm`, `crypto_box_beforenm`, `crypto_box_curve25519xchacha20poly1305_beforenm`, `crypto_box_curve25519xchacha20poly1305_BEFORENMBYTES`, `crypto_box_curve25519xchacha20poly1305_detached`, `crypto_box_curve25519xchacha20poly1305_detached_afternm`, `crypto_box_curve25519xchacha20poly1305_easy`, `crypto_box_curve25519xchacha20poly1305_easy_afternm`, `crypto_box_curve25519xchacha20poly1305_keypair`, `crypto_box_curve25519xchacha20poly1305_MACBYTES`, `crypto_box_curve25519xchacha20poly1305_MESSAGEBYTES_MAX`, `crypto_box_curve25519xchacha20poly1305_NONCEBYTES`, `crypto_box_curve25519xchacha20poly1305_open_detached`, `crypto_box_curve25519xchacha20poly1305_open_detached_afternm`, `crypto_box_curve25519xchacha20poly1305_open_easy`, `crypto_box_curve25519xchacha20poly1305_open_easy_afternm`, `crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES`, `crypto_box_curve25519xchacha20poly1305_seal`, `crypto_box_curve25519xchacha20poly1305_seal_open`, `crypto_box_curve25519xchacha20poly1305_SEALBYTES`, `crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES`, `crypto_box_curve25519xchacha20poly1305_seed_keypair`, `crypto_box_curve25519xchacha20poly1305_SEEDBYTES`, `crypto_box_curve25519xsalsa20poly1305`, `crypto_box_curve25519xsalsa20poly1305_afternm`, `crypto_box_curve25519xsalsa20poly1305_beforenm`, `crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES`, `crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES`, `crypto_box_curve25519xsalsa20poly1305_keypair`, `crypto_box_curve25519xsalsa20poly1305_MACBYTES`, `crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX`, `crypto_box_curve25519xsalsa20poly1305_NONCEBYTES`, `crypto_box_curve25519xsalsa20poly1305_open`, `crypto_box_curve25519xsalsa20poly1305_open_afternm`, `crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES`, `crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES`, `crypto_box_curve25519xsalsa20poly1305_seed_keypair`, `crypto_box_curve25519xsalsa20poly1305_SEEDBYTES`, `crypto_box_curve25519xsalsa20poly1305_ZEROBYTES`, `crypto_box_detached_afternm`, `crypto_box_easy_afternm`, `crypto_box_MESSAGEBYTES_MAX`, `crypto_box_open`, `crypto_box_open_afternm`, `crypto_box_open_detached_afternm`, `crypto_box_open_easy_afternm`, `crypto_box_primitive`, `crypto_core_ed25519_from_hash`, `crypto_core_ed25519_HASHBYTES`, `crypto_core_ed25519_random`, `crypto_core_ed25519_scalar_mul`, `crypto_core_hchacha20`, `crypto_core_hchacha20_CONSTBYTES`, `crypto_core_hchacha20_INPUTBYTES`, `crypto_core_hchacha20_KEYBYTES`, `crypto_core_hchacha20_OUTPUTBYTES`, `crypto_core_hsalsa20`, `crypto_core_hsalsa20_CONSTBYTES`, `crypto_core_hsalsa20_INPUTBYTES`, `crypto_core_hsalsa20_KEYBYTES`, `crypto_core_hsalsa20_OUTPUTBYTES`, `crypto_core_ristretto255_add`, `crypto_core_ristretto255_BYTES`, `crypto_core_ristretto255_from_hash`, `crypto_core_ristretto255_HASHBYTES`, `crypto_core_ristretto255_is_valid_point`, `crypto_core_ristretto255_NONREDUCEDSCALARBYTES`, `crypto_core_ristretto255_random`, `crypto_core_ristretto255_scalar_add`, `crypto_core_ristretto255_scalar_complement`, `crypto_core_ristretto255_scalar_invert`, `crypto_core_ristretto255_scalar_mul`, `crypto_core_ristretto255_scalar_negate`, `crypto_core_ristretto255_scalar_random`, `crypto_core_ristretto255_scalar_reduce`, `crypto_core_ristretto255_scalar_sub`, `crypto_core_ristretto255_SCALARBYTES`, `crypto_core_ristretto255_sub`, `crypto_core_salsa20`, `crypto_core_salsa2012`, `crypto_core_salsa2012_CONSTBYTES`, `crypto_core_salsa2012_INPUTBYTES`, `crypto_core_salsa2012_KEYBYTES`, `crypto_core_salsa2012_OUTPUTBYTES`, `crypto_core_salsa208`, `crypto_core_salsa208_CONSTBYTES`, `crypto_core_salsa208_INPUTBYTES`, `crypto_core_salsa208_KEYBYTES`, `crypto_core_salsa208_OUTPUTBYTES`, `crypto_core_salsa20_CONSTBYTES`, `crypto_core_salsa20_INPUTBYTES`, `crypto_core_salsa20_KEYBYTES`, `crypto_core_salsa20_OUTPUTBYTES`, `crypto_generichash_blake2b`, `crypto_generichash_blake2b_BYTES`, `crypto_generichash_blake2b_BYTES_MAX`, `crypto_generichash_blake2b_BYTES_MIN`, `crypto_generichash_blake2b_final`, `crypto_generichash_blake2b_init`, `crypto_generichash_blake2b_init_salt_personal`, `crypto_generichash_blake2b_KEYBYTES`, `crypto_generichash_blake2b_KEYBYTES_MAX`, `crypto_generichash_blake2b_KEYBYTES_MIN`, `crypto_generichash_blake2b_keygen`, `crypto_generichash_blake2b_PERSONALBYTES`, `crypto_generichash_blake2b_salt_personal`, `crypto_generichash_blake2b_SALTBYTES`, `crypto_generichash_blake2b_STATEBYTES`, `crypto_generichash_blake2b_update`, `crypto_generichash_primitive`, `crypto_hash_primitive`, `crypto_kdf_blake2b_BYTES_MAX`, `crypto_kdf_blake2b_BYTES_MIN`, `crypto_kdf_blake2b_CONTEXTBYTES`, `crypto_kdf_blake2b_derive_from_key`, `crypto_kdf_blake2b_KEYBYTES`, `crypto_kdf_primitive`, `crypto_kx_primitive`, `crypto_onetimeauth_keygen`, `crypto_onetimeauth_poly1305`, `crypto_onetimeauth_poly1305_BYTES`, `crypto_onetimeauth_poly1305_final`, `crypto_onetimeauth_poly1305_init`, `crypto_onetimeauth_poly1305_KEYBYTES`, `crypto_onetimeauth_poly1305_keygen`, `crypto_onetimeauth_poly1305_STATEBYTES`, `crypto_onetimeauth_poly1305_update`, `crypto_onetimeauth_poly1305_verify`, `crypto_onetimeauth_primitive`, `crypto_pwhash_alg_argon2i13`, `crypto_pwhash_alg_argon2id13`, `crypto_pwhash_alg_default`, `crypto_pwhash_argon2i`, `crypto_pwhash_argon2i_alg_argon2i13`, `crypto_pwhash_argon2i_BYTES_MAX`, `crypto_pwhash_argon2i_BYTES_MIN`, `crypto_pwhash_argon2i_memlimit_interactive`, `crypto_pwhash_argon2i_memlimit_max`, `crypto_pwhash_argon2i_memlimit_min`, `crypto_pwhash_argon2i_memlimit_moderate`, `crypto_pwhash_argon2i_memlimit_sensitive`, `crypto_pwhash_argon2i_opslimit_interactive`, `crypto_pwhash_argon2i_opslimit_max`, `crypto_pwhash_argon2i_opslimit_min`, `crypto_pwhash_argon2i_opslimit_moderate`, `crypto_pwhash_argon2i_opslimit_sensitive`, `crypto_pwhash_argon2i_passwd_max`, `crypto_pwhash_argon2i_passwd_min`, `crypto_pwhash_argon2i_SALTBYTES`, `crypto_pwhash_argon2i_str`, `crypto_pwhash_argon2i_str_needs_rehash`, `crypto_pwhash_argon2i_str_verify`, `crypto_pwhash_argon2i_STRBYTES`, `crypto_pwhash_argon2i_strprefix`, `crypto_pwhash_argon2id`, `crypto_pwhash_argon2id_alg_argon2id13`, `crypto_pwhash_argon2id_BYTES_MAX`, `crypto_pwhash_argon2id_BYTES_MIN`, `crypto_pwhash_argon2id_memlimit_interactive`, `crypto_pwhash_argon2id_memlimit_max`, `crypto_pwhash_argon2id_memlimit_min`, `crypto_pwhash_argon2id_memlimit_moderate`, `crypto_pwhash_argon2id_memlimit_sensitive`, `crypto_pwhash_argon2id_opslimit_interactive`, `crypto_pwhash_argon2id_opslimit_max`, `crypto_pwhash_argon2id_opslimit_min`, `crypto_pwhash_argon2id_opslimit_moderate`, `crypto_pwhash_argon2id_opslimit_sensitive`, `crypto_pwhash_argon2id_passwd_max`, `crypto_pwhash_argon2id_passwd_min`, `crypto_pwhash_argon2id_SALTBYTES`, `crypto_pwhash_argon2id_str`, `crypto_pwhash_argon2id_str_needs_rehash`, `crypto_pwhash_argon2id_str_verify`, `crypto_pwhash_argon2id_STRBYTES`, `crypto_pwhash_argon2id_strprefix`, `crypto_pwhash_memlimit_interactive`, `crypto_pwhash_memlimit_max`, `crypto_pwhash_memlimit_min`, `crypto_pwhash_memlimit_moderate`, `crypto_pwhash_memlimit_sensitive`, `crypto_pwhash_opslimit_interactive`, `crypto_pwhash_opslimit_max`, `crypto_pwhash_opslimit_min`, `crypto_pwhash_opslimit_moderate`, `crypto_pwhash_opslimit_sensitive`, `crypto_pwhash_passwd_max`, `crypto_pwhash_passwd_min`, `crypto_pwhash_primitive`, `crypto_pwhash_scryptsalsa208sha256_ll`, `crypto_pwhash_scryptsalsa208sha256_memlimit_interactive`, `crypto_pwhash_scryptsalsa208sha256_memlimit_max`, `crypto_pwhash_scryptsalsa208sha256_memlimit_min`, `crypto_pwhash_scryptsalsa208sha256_memlimit_sensitive`, `crypto_pwhash_scryptsalsa208sha256_opslimit_interactive`, `crypto_pwhash_scryptsalsa208sha256_opslimit_max`, `crypto_pwhash_scryptsalsa208sha256_opslimit_min`, `crypto_pwhash_scryptsalsa208sha256_opslimit_sensitive`, `crypto_pwhash_scryptsalsa208sha256_passwd_max`, `crypto_pwhash_scryptsalsa208sha256_passwd_min`, `crypto_pwhash_scryptsalsa208sha256_strprefix`, `crypto_pwhash_str_alg`, `crypto_pwhash_strprefix`, `crypto_scalarmult_curve25519`, `crypto_scalarmult_curve25519_base`, `crypto_scalarmult_curve25519_BYTES`, `crypto_scalarmult_curve25519_SCALARBYTES`, `crypto_scalarmult_primitive`, `crypto_scalarmult_ristretto255`, `crypto_scalarmult_ristretto255_base`, `crypto_scalarmult_ristretto255_BYTES`, `crypto_scalarmult_ristretto255_SCALARBYTES`, `crypto_secretbox_keygen`, `crypto_secretbox_MESSAGEBYTES_MAX`, `crypto_secretbox_primitive`, `crypto_secretbox_xchacha20poly1305_detached`, `crypto_secretbox_xchacha20poly1305_easy`, `crypto_secretbox_xchacha20poly1305_KEYBYTES`, `crypto_secretbox_xchacha20poly1305_MACBYTES`, `crypto_secretbox_xchacha20poly1305_MESSAGEBYTES_MAX`, `crypto_secretbox_xchacha20poly1305_NONCEBYTES`, `crypto_secretbox_xchacha20poly1305_open_detached`, `crypto_secretbox_xchacha20poly1305_open_easy`, `crypto_secretbox_xsalsa20poly1305`, `crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES`, `crypto_secretbox_xsalsa20poly1305_KEYBYTES`, `crypto_secretbox_xsalsa20poly1305_keygen`, `crypto_secretbox_xsalsa20poly1305_MACBYTES`, `crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX`, `crypto_secretbox_xsalsa20poly1305_NONCEBYTES`, `crypto_secretbox_xsalsa20poly1305_open`, `crypto_secretbox_xsalsa20poly1305_ZEROBYTES`, `crypto_secretstream_xchacha20poly1305_tag_final`, `crypto_secretstream_xchacha20poly1305_tag_message`, `crypto_secretstream_xchacha20poly1305_tag_push`, `crypto_secretstream_xchacha20poly1305_tag_rekey`, `crypto_shorthash_keygen`, `crypto_shorthash_primitive`, `crypto_shorthash_siphash24`, `crypto_shorthash_siphash24_BYTES`, `crypto_shorthash_siphash24_KEYBYTES`, `crypto_shorthash_siphashx24`, `crypto_shorthash_siphashx24_BYTES`, `crypto_shorthash_siphashx24_KEYBYTES`, `crypto_sign_ed25519`, `crypto_sign_ed25519_BYTES`, `crypto_sign_ed25519_detached`, `crypto_sign_ed25519_keypair`, `crypto_sign_ed25519_MESSAGEBYTES_MAX`, `crypto_sign_ed25519_open`, `crypto_sign_ed25519_PUBLICKEYBYTES`, `crypto_sign_ed25519_SECRETKEYBYTES`, `crypto_sign_ed25519_seed_keypair`, `crypto_sign_ed25519_SEEDBYTES`, `crypto_sign_ed25519_sk_to_seed`, `crypto_sign_ed25519_verify_detached`, `crypto_sign_ed25519ph_final_create`, `crypto_sign_ed25519ph_final_verify`, `crypto_sign_ed25519ph_init`, `crypto_sign_ed25519ph_STATEBYTES`, `crypto_sign_ed25519ph_update`, `crypto_sign_edwards25519sha512batch`, `crypto_sign_edwards25519sha512batch_keypair`, `crypto_sign_edwards25519sha512batch_open`, `crypto_sign_final_create`, `crypto_sign_final_verify`, `crypto_sign_init`, `crypto_sign_MESSAGEBYTES_MAX`, `crypto_sign_primitive`, `crypto_sign_STATEBYTES`, `crypto_sign_update`, `crypto_stream_chacha20_ietf_keygen`, `crypto_stream_chacha20_keygen`, `crypto_stream_keygen`, `crypto_stream_MESSAGEBYTES_MAX`, `crypto_stream_primitive`, `crypto_stream_salsa20`, `crypto_stream_salsa2012`, `crypto_stream_salsa2012_KEYBYTES`, `crypto_stream_salsa2012_keygen`, `crypto_stream_salsa2012_MESSAGEBYTES_MAX`, `crypto_stream_salsa2012_NONCEBYTES`, `crypto_stream_salsa2012_xor`, `crypto_stream_salsa208`, `crypto_stream_salsa208_KEYBYTES`, `crypto_stream_salsa208_keygen`, `crypto_stream_salsa208_MESSAGEBYTES_MAX`, `crypto_stream_salsa208_NONCEBYTES`, `crypto_stream_salsa208_xor`, `crypto_stream_salsa20_keygen`, `crypto_stream_xchacha20_keygen`, `crypto_stream_xsalsa20`, `crypto_stream_xsalsa20_KEYBYTES`, `crypto_stream_xsalsa20_keygen`, `crypto_stream_xsalsa20_MESSAGEBYTES_MAX`, `crypto_stream_xsalsa20_NONCEBYTES`, `crypto_stream_xsalsa20_xor`, `crypto_stream_xsalsa20_xor_ic`, `crypto_verify_16_BYTES`, `crypto_verify_32_BYTES`, `crypto_verify_64`, `crypto_verify_64_BYTES`, `randombytes`, `randombytes_close`, `randombytes_implementation_name`, `randombytes_internal_implementation`, `randombytes_set_implementation`, `randombytes_stir`, `randombytes_sysrandom_implementation`, `sodium_allocarray`, `sodium_base642bin`, `sodium_base64_encoded_len`, `sodium_bin2base64`, `sodium_bin2hex`, `sodium_hex2bin`, `sodium_init`, `sodium_library_minimal`, `sodium_library_version_major`, `sodium_library_version_minor`, `sodium_misuse`, `sodium_runtime_has_aesni`, `sodium_runtime_has_avx`, `sodium_runtime_has_avx2`, `sodium_runtime_has_avx512f`, `sodium_runtime_has_neon`, `sodium_runtime_has_pclmul`, `sodium_runtime_has_rdrand`, `sodium_runtime_has_sse2`, `sodium_runtime_has_sse3`, `sodium_runtime_has_sse41`, `sodium_runtime_has_ssse3`, `sodium_set_misuse_handler`, `sodium_stackzero`, `sodium_version_string` | |
| 285 | ||
| 286 | ## License | |
| 287 | ||
| 288 | [MIT](LICENSE.md) |
| … | … | @@ -0,0 +1,33 @@ |
| 1 | #!/usr/bin/env node | |
| 2 | ||
| 3 | const fs = require('fs') | |
| 4 | const path = require('path') | |
| 5 | const root = path.dirname(require.resolve('sodium-javascript/package.json')) | |
| 6 | ||
| 7 | const tmp = "module.exports = require('sodium-native')\n" | |
| 8 | ||
| 9 | function recurse (dir) { | |
| 10 | const ls = fs.readdirSync(dir) | |
| 11 | const subdir = path.relative(root, dir) | |
| 12 | if (subdir) fs.mkdirSync(subdir, { recursive: true }) | |
| 13 | ||
| 14 | for (const file of ls) { | |
| 15 | if (file === 'internal') recurse(path.join(dir, file)) | |
| 16 | if (!/\.js$/i.test(file)) continue | |
| 17 | if (file === 'example.js') continue | |
| 18 | if (file === 'test.js') continue | |
| 19 | ||
| 20 | fs.writeFileSync(path.join(__dirname, '..', subdir, file), tmp) | |
| 21 | } | |
| 22 | } | |
| 23 | ||
| 24 | recurse(root) | |
| 25 | ||
| 26 | const pkg = require('sodium-javascript/package.json') | |
| 27 | const myPkg = require('../package.json') | |
| 28 | ||
| 29 | for (const key of Object.keys(pkg.dependencies)) { | |
| 30 | myPkg.dependencies[key] = pkg.dependencies[key] | |
| 31 | } | |
| 32 | ||
| 33 | fs.writeFileSync(path.join(__dirname, '../package.json'), JSON.stringify(myPkg, null, 2) + '\n') |
| … | … | @@ -0,0 +1,32 @@ |
| 1 | var exec = require('child_process').execSync | |
| 2 | var path = require('path') | |
| 3 | ||
| 4 | var symbols = exec(`nm -gU "${path.join(require.resolve('sodium-native'), '../prebuilds/darwin-x64/libsodium.dylib')}"`).toString().trim().split('\n') | |
| 5 | var native = new Set(Object.keys(require('sodium-native')).sort()) | |
| 6 | var js = new Set(Object.keys(require('sodium-javascript')).sort()) | |
| 7 | ||
| 8 | symbols = symbols.map(l => l.split(' ').pop().replace(/^_/, '').replace(/_[^_]*bytes.*/, (s) => s.toUpperCase())) | |
| 9 | ||
| 10 | var missing = [] | |
| 11 | ||
| 12 | console.log(` | |
| 13 | <!-- Generated by build-scripts/symbols.js with | |
| 14 | sodium-native@${require('sodium-native/package.json').version} | |
| 15 | sodium-javascript@${require('sodium-javascript/package.json').version} -->\ | |
| 16 | `) | |
| 17 | ||
| 18 | const max = symbols.filter(s => native.has(s) || js.has(s)).map(s => s.length).reduce((a, b) => Math.max(a, b)) | |
| 19 | const lines = ':-' + ''.padEnd(max, '--') + '-:' | |
| 20 | const center = s => s.padStart(Math.floor((max + s.length) / 2), ' ').padEnd(max, ' ') | |
| 21 | ||
| 22 | console.log(`| ${center('C Library Symbol')} | \`sodium-native\` | \`sodium-javascript\` |`) | |
| 23 | console.log(`|${lines}|:--------------------:|:--------------------:|`) | |
| 24 | ||
| 25 | for (var i = 0; i < symbols.length; i++) { | |
| 26 | var s = symbols[i] | |
| 27 | if (!(native.has(s) || js.has(s))) missing.push(s) | |
| 28 | else console.log(`| \`${(s + '`').padEnd(max + 1, ' ')} | ${native.has(s) ? ' :white_check_mark: ' : ':small_red_triangle:'} | ${js.has(s) ? ' :white_check_mark: ' : ':small_red_triangle:'} |`) | |
| 29 | } | |
| 30 | ||
| 31 | console.log('\n### Missing altogether (Ctrl + F friendly)\n') | |
| 32 | console.log(missing.map(s => '`' + s + '`').join(', ')) |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | const fs = require('fs') | |
| 2 | const path = require('path') | |
| 3 | const { Transform, PassThrough } = require('stream') | |
| 4 | ||
| 5 | module.exports = function (file, opts) { | |
| 6 | const basedir = path.resolve(__dirname, '..') | |
| 7 | const relname = path.relative(basedir, file) | |
| 8 | ||
| 9 | if (opts._flags.browserField === false) { | |
| 10 | return new PassThrough() | |
| 11 | } | |
| 12 | ||
| 13 | return new Transform({ | |
| 14 | transform (data, enc, cb) { | |
| 15 | cb() | |
| 16 | }, | |
| 17 | flush (cb) { | |
| 18 | const filename = require.resolve('sodium-javascript/' + relname) | |
| 19 | fs.readFile(filename, (err, buf) => { | |
| 20 | if (err) return cb(err) | |
| 21 | this.push(buf) | |
| 22 | cb(null) | |
| 23 | }) | |
| 24 | } | |
| 25 | }) | |
| 26 | } |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1,49 @@ |
| 1 | { | |
| 2 | "name": "sodium-universal", | |
| 3 | "version": "5.0.1", | |
| 4 | "description": "Universal wrapper for sodium-javascript and sodium-native working in Node.js and the Browser", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "sodium-native": "^5.0.1" | |
| 8 | }, | |
| 9 | "peerDependencies": { | |
| 10 | "sodium-javascript": "~0.8.0" | |
| 11 | }, | |
| 12 | "peerDependenciesMeta": { | |
| 13 | "sodium-javascript": { | |
| 14 | "optional": true | |
| 15 | } | |
| 16 | }, | |
| 17 | "scripts": { | |
| 18 | "prepublish": "./build-scripts/generate.js" | |
| 19 | }, | |
| 20 | "browser": { | |
| 21 | "sodium-native": "sodium-javascript" | |
| 22 | }, | |
| 23 | "browserify": { | |
| 24 | "transform": [ | |
| 25 | "./build-scripts/transform.js" | |
| 26 | ] | |
| 27 | }, | |
| 28 | "repository": { | |
| 29 | "type": "git", | |
| 30 | "url": "git+https://github.com/holepunchto/sodium-universal.git" | |
| 31 | }, | |
| 32 | "keywords": [ | |
| 33 | "libsodium", | |
| 34 | "sodium", | |
| 35 | "sodium-native", | |
| 36 | "sodium-javascript", | |
| 37 | "browserify" | |
| 38 | ], | |
| 39 | "contributors": [ | |
| 40 | "Emil Bay <github@tixz.dk> (http://bayes.dk)", | |
| 41 | "Mathias Buus <mathiasbuus@gmail.com> (https://mafinto.sh)", | |
| 42 | "Christophe Diederichs <chm-diederichs@hyperdivision.dk>" | |
| 43 | ], | |
| 44 | "license": "MIT", | |
| 45 | "bugs": { | |
| 46 | "url": "https://github.com/holepunchto/sodium-universal/issues" | |
| 47 | }, | |
| 48 | "homepage": "https://github.com/holepunchto/sodium-universal#readme" | |
| 49 | } |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1,30 @@ |
| 1 | { | |
| 2 | "name": "noise-curve-ed", | |
| 3 | "version": "2.1.0", | |
| 4 | "description": "Ed25519 elliptic curve operations for [`noise-handshake`](https://github.com/chm-diederichs/noise-handshake)", | |
| 5 | "main": "index.js", | |
| 6 | "scripts": { | |
| 7 | "test": "npx standard && tape test.js" | |
| 8 | }, | |
| 9 | "repository": { | |
| 10 | "type": "git", | |
| 11 | "url": "git+https://github.com/chm-diederichs/noise-curve-ed.git" | |
| 12 | }, | |
| 13 | "keywords": [], | |
| 14 | "author": "", | |
| 15 | "license": "ISC", | |
| 16 | "bugs": { | |
| 17 | "url": "https://github.com/chm-diederichs/noise-curve-ed/issues" | |
| 18 | }, | |
| 19 | "homepage": "https://github.com/chm-diederichs/noise-curve-ed#readme", | |
| 20 | "dependencies": { | |
| 21 | "b4a": "^1.1.0", | |
| 22 | "nanoassert": "^2.0.0", | |
| 23 | "sodium-universal": "^5.0.0" | |
| 24 | }, | |
| 25 | "devDependencies": { | |
| 26 | "noise-handshake": "^3.0.0", | |
| 27 | "standard": "^16.0.3", | |
| 28 | "tape": "^5.2.2" | |
| 29 | } | |
| 30 | } |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | const Noise = require('noise-handshake') | |
| 2 | const test = require('tape') | |
| 3 | const curve = require('./') | |
| 4 | ||
| 5 | test('XX', t => { | |
| 6 | const initiator = new Noise('XX', true, null, { curve }) | |
| 7 | const responder = new Noise('XX', false, null, { curve }) | |
| 8 | ||
| 9 | initiator.initialise(Buffer.alloc(0)) | |
| 10 | responder.initialise(Buffer.alloc(0)) | |
| 11 | ||
| 12 | const message = initiator.send() | |
| 13 | responder.recv(message) | |
| 14 | ||
| 15 | const reply = responder.send() | |
| 16 | initiator.recv(reply) | |
| 17 | ||
| 18 | t.deepEqual(initiator.rx, responder.tx) | |
| 19 | t.deepEqual(initiator.tx, responder.rx) | |
| 20 | t.end() | |
| 21 | }) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,95 @@ |
| 1 | # noise-handshake | |
| 2 | ||
| 3 | ## Usage | |
| 4 | ```js | |
| 5 | const Noise = require('noise-handshake') | |
| 6 | const Cipher = require('noise-handshake/cipher') | |
| 7 | const initiator = new Noise('IK', true) | |
| 8 | const responder = new Noise('IK', false) | |
| 9 | ||
| 10 | const prologue = Buffer.alloc(0) | |
| 11 | ||
| 12 | // preshared key | |
| 13 | initiator.initialise(prologue, responder.s.publicKey) | |
| 14 | responder.initialise(prologue) | |
| 15 | ||
| 16 | // -> e, es, s, ss | |
| 17 | const message = initiator.send() | |
| 18 | responder.recv(message) | |
| 19 | ||
| 20 | // <- e, ee, se | |
| 21 | const reply = responder.send() | |
| 22 | initiator.recv(reply) | |
| 23 | ||
| 24 | console.log(initiator.complete) // true | |
| 25 | ||
| 26 | // convention is to use rx for | |
| 27 | // sending and tx for receiving | |
| 28 | ||
| 29 | // initiator.rx === responder.tx | |
| 30 | // responder.rx === initiator.tx | |
| 31 | ||
| 32 | // instantiate a cipher using shared secrets | |
| 33 | const send = new Cipher(initiator.tx) | |
| 34 | const recieve = new Cipher(responder.rx) | |
| 35 | ||
| 36 | const msg = Buffer.from('hello, world') | |
| 37 | ||
| 38 | const enc = send.encrypt(msg) | |
| 39 | console.log(recieve.decrypt(enc)) // hello, world | |
| 40 | ``` | |
| 41 | ||
| 42 | ## API | |
| 43 | ||
| 44 | #### `const peer = new Noise(pattern, initiator, staticKeypair, [opts])` | |
| 45 | ||
| 46 | Create a new handshake state for a given pattern. Initiator should be either `true` or `false` depending on the role. A preexisting keypair may be passed as `staticKeypair` | |
| 47 | ||
| 48 | `opts` may be used to pass in the following: | |
| 49 | - `curve`: module for performing Noise over other curves. | |
| 50 | - `psk`: a 32-byte buffer containing a pre-shared key for patterns containing `psk0`. (Other psk positions are not currently supported.) | |
| 51 | ||
| 52 | Curve modules should export the following: | |
| 53 | ``` | |
| 54 | { | |
| 55 | DHLEN, | |
| 56 | PKLEN, | |
| 57 | SKLEN, | |
| 58 | ALG, | |
| 59 | generateKeyPair, | |
| 60 | dh | |
| 61 | } | |
| 62 | ``` | |
| 63 | ||
| 64 | See [dh.js](./dh) for an example. | |
| 65 | ||
| 66 | #### `peer.initialise(prologue, remoteStatic)` | |
| 67 | ||
| 68 | Initialise the handshake state with a prologue and any preshared keys. | |
| 69 | ||
| 70 | #### `const buf = send([payload])` | |
| 71 | ||
| 72 | Send the next message in the handshake, add an optional payload buffer to be included in the message, payload is a zero length buffer by default. | |
| 73 | ||
| 74 | #### `const payload = peer.recv(buf)` | |
| 75 | ||
| 76 | Receive a handshake message from the peer and return the encrypted payload. | |
| 77 | ||
| 78 | #### `peer.complete` | |
| 79 | ||
| 80 | `true` or `false`. Indicates whether `rx` and `tx` have been created yet. | |
| 81 | ||
| 82 | When complete, the working handshake state shall be cleared *only* the following state shall remain on the object: | |
| 83 | ||
| 84 | ```js | |
| 85 | { | |
| 86 | tx, // session key to decrypt messages from remote peer | |
| 87 | rx, // session key to encrypt messages to remote peer | |
| 88 | rs, // the remote peer's public key, | |
| 89 | hash, // a hash of the entire handshake state | |
| 90 | } | |
| 91 | ``` | |
| 92 | ||
| 93 | ## License | |
| 94 | ||
| 95 | Apache-2.0 |
| … | … | @@ -0,0 +1,93 @@ |
| 1 | const sodium = require('sodium-universal') | |
| 2 | const b4a = require('b4a') | |
| 3 | ||
| 4 | module.exports = class CipherState { | |
| 5 | constructor (key) { | |
| 6 | this.key = key || null | |
| 7 | this.nonce = 0 | |
| 8 | this.CIPHER_ALG = 'ChaChaPoly' | |
| 9 | } | |
| 10 | ||
| 11 | initialiseKey (key) { | |
| 12 | this.key = key | |
| 13 | this.nonce = 0 | |
| 14 | } | |
| 15 | ||
| 16 | setNonce (nonce) { | |
| 17 | this.nonce = nonce | |
| 18 | } | |
| 19 | ||
| 20 | encrypt (plaintext, ad) { | |
| 21 | if (!this.hasKey) return plaintext | |
| 22 | if (!ad) ad = b4a.alloc(0) | |
| 23 | ||
| 24 | const ciphertext = encryptWithAD(this.key, this.nonce, ad, plaintext) | |
| 25 | if (ciphertext.length > 65535) throw new Error(`ciphertext length of ${ciphertext.length} exceeds maximum Noise message length of 65535`) | |
| 26 | this.nonce++ | |
| 27 | ||
| 28 | return ciphertext | |
| 29 | } | |
| 30 | ||
| 31 | decrypt (ciphertext, ad) { | |
| 32 | if (!this.hasKey) return ciphertext | |
| 33 | if (!ad) ad = b4a.alloc(0) | |
| 34 | if (ciphertext.length > 65535) throw new Error(`ciphertext length of ${ciphertext.length} exceeds maximum Noise message length of 65535`) | |
| 35 | ||
| 36 | const plaintext = decryptWithAD(this.key, this.nonce, ad, ciphertext) | |
| 37 | this.nonce++ | |
| 38 | ||
| 39 | return plaintext | |
| 40 | } | |
| 41 | ||
| 42 | get hasKey () { | |
| 43 | return this.key !== null | |
| 44 | } | |
| 45 | ||
| 46 | _clear () { | |
| 47 | sodium.sodium_memzero(this.key) | |
| 48 | this.key = null | |
| 49 | this.nonce = null | |
| 50 | } | |
| 51 | ||
| 52 | static get MACBYTES () { | |
| 53 | return 16 | |
| 54 | } | |
| 55 | ||
| 56 | static get NONCEBYTES () { | |
| 57 | return 8 | |
| 58 | } | |
| 59 | ||
| 60 | static get KEYBYTES () { | |
| 61 | return 32 | |
| 62 | } | |
| 63 | } | |
| 64 | ||
| 65 | function encryptWithAD (key, counter, additionalData, plaintext) { | |
| 66 | // for our purposes, additionalData will always be a pubkey so we encode from hex | |
| 67 | if (!b4a.isBuffer(additionalData)) additionalData = b4a.from(additionalData, 'hex') | |
| 68 | if (!b4a.isBuffer(plaintext)) plaintext = b4a.from(plaintext, 'hex') | |
| 69 | ||
| 70 | const nonce = b4a.alloc(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES) | |
| 71 | const view = new DataView(nonce.buffer, nonce.byteOffset, nonce.byteLength) | |
| 72 | view.setUint32(4, counter, true) | |
| 73 | ||
| 74 | const ciphertext = b4a.alloc(plaintext.byteLength + sodium.crypto_aead_chacha20poly1305_ietf_ABYTES) | |
| 75 | ||
| 76 | sodium.crypto_aead_chacha20poly1305_ietf_encrypt(ciphertext, plaintext, additionalData, null, nonce, key) | |
| 77 | return ciphertext | |
| 78 | } | |
| 79 | ||
| 80 | function decryptWithAD (key, counter, additionalData, ciphertext) { | |
| 81 | // for our purposes, additionalData will always be a pubkey so we encode from hex | |
| 82 | if (!b4a.isBuffer(additionalData)) additionalData = b4a.from(additionalData, 'hex') | |
| 83 | if (!b4a.isBuffer(ciphertext)) ciphertext = b4a.from(ciphertext, 'hex') | |
| 84 | ||
| 85 | const nonce = b4a.alloc(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES) | |
| 86 | const view = new DataView(nonce.buffer, nonce.byteOffset, nonce.byteLength) | |
| 87 | view.setUint32(4, counter, true) | |
| 88 | ||
| 89 | const plaintext = b4a.alloc(ciphertext.byteLength - sodium.crypto_aead_chacha20poly1305_ietf_ABYTES) | |
| 90 | ||
| 91 | sodium.crypto_aead_chacha20poly1305_ietf_decrypt(plaintext, null, ciphertext, additionalData, nonce, key) | |
| 92 | return plaintext | |
| 93 | } |
| … | … | @@ -0,0 +1,71 @@ |
| 1 | /* eslint-disable camelcase */ | |
| 2 | const { | |
| 3 | crypto_kx_SEEDBYTES, | |
| 4 | crypto_kx_keypair, | |
| 5 | crypto_kx_seed_keypair, | |
| 6 | crypto_scalarmult_BYTES, | |
| 7 | crypto_scalarmult_SCALARBYTES, | |
| 8 | crypto_scalarmult, | |
| 9 | crypto_scalarmult_base | |
| 10 | } = require('sodium-universal') | |
| 11 | ||
| 12 | const assert = require('nanoassert') | |
| 13 | const b4a = require('b4a') | |
| 14 | ||
| 15 | const DHLEN = crypto_scalarmult_BYTES | |
| 16 | const PKLEN = crypto_scalarmult_BYTES | |
| 17 | const SKLEN = crypto_scalarmult_SCALARBYTES | |
| 18 | const SEEDLEN = crypto_kx_SEEDBYTES | |
| 19 | const ALG = '25519' | |
| 20 | ||
| 21 | module.exports = { | |
| 22 | DHLEN, | |
| 23 | PKLEN, | |
| 24 | SKLEN, | |
| 25 | SEEDLEN, | |
| 26 | ALG, | |
| 27 | generateKeyPair, | |
| 28 | generateSeedKeyPair, | |
| 29 | dh | |
| 30 | } | |
| 31 | ||
| 32 | function generateKeyPair (privKey) { | |
| 33 | const keyPair = {} | |
| 34 | ||
| 35 | keyPair.secretKey = privKey || b4a.alloc(SKLEN) | |
| 36 | keyPair.publicKey = b4a.alloc(PKLEN) | |
| 37 | ||
| 38 | if (privKey) { | |
| 39 | crypto_scalarmult_base(keyPair.publicKey, keyPair.secretKey) | |
| 40 | } else { | |
| 41 | crypto_kx_keypair(keyPair.publicKey, keyPair.secretKey) | |
| 42 | } | |
| 43 | ||
| 44 | return keyPair | |
| 45 | } | |
| 46 | ||
| 47 | function generateSeedKeyPair (seed) { | |
| 48 | assert(seed.byteLength === SKLEN) | |
| 49 | ||
| 50 | const keyPair = {} | |
| 51 | keyPair.secretKey = b4a.alloc(SKLEN) | |
| 52 | keyPair.publicKey = b4a.alloc(PKLEN) | |
| 53 | ||
| 54 | crypto_kx_seed_keypair(keyPair.publicKey, keyPair.secretKey, seed) | |
| 55 | return keyPair | |
| 56 | } | |
| 57 | ||
| 58 | function dh (publicKey, { secretKey }) { | |
| 59 | assert(secretKey.byteLength === SKLEN) | |
| 60 | assert(publicKey.byteLength === PKLEN) | |
| 61 | ||
| 62 | const output = b4a.alloc(DHLEN) | |
| 63 | ||
| 64 | crypto_scalarmult( | |
| 65 | output, | |
| 66 | secretKey, | |
| 67 | publicKey | |
| 68 | ) | |
| 69 | ||
| 70 | return output | |
| 71 | } |
| … | … | @@ -0,0 +1,47 @@ |
| 1 | const hmacBlake2b = require('./hmac') | |
| 2 | const b4a = require('b4a') | |
| 3 | ||
| 4 | const HASHLEN = 64 | |
| 5 | ||
| 6 | module.exports = { | |
| 7 | hkdf, | |
| 8 | HASHLEN | |
| 9 | } | |
| 10 | ||
| 11 | // HMAC-based Extract-and-Expand KDF | |
| 12 | // https://www.ietf.org/rfc/rfc5869.txt | |
| 13 | ||
| 14 | function hkdf (salt, inputKeyMaterial, info = '', length = 2 * HASHLEN) { | |
| 15 | const pseudoRandomKey = hkdfExtract(salt, inputKeyMaterial) | |
| 16 | return hkdfExpand(pseudoRandomKey, info, length) | |
| 17 | } | |
| 18 | ||
| 19 | function hkdfExtract (salt, inputKeyMaterial) { | |
| 20 | const hmac = b4a.alloc(HASHLEN) | |
| 21 | return hmacDigest(hmac, salt, inputKeyMaterial) | |
| 22 | } | |
| 23 | ||
| 24 | function hkdfExpand (key, info, length) { | |
| 25 | // Put in dedicated slab to avoid keeping shared slab from being gc'ed | |
| 26 | const buffer = b4a.allocUnsafeSlow(length) | |
| 27 | ||
| 28 | const infoBuf = b4a.from(info) | |
| 29 | let prev = infoBuf | |
| 30 | ||
| 31 | const result = [] | |
| 32 | for (let i = 0; i < length; i += HASHLEN) { | |
| 33 | const pos = b4a.from([(i / HASHLEN) + 1]) | |
| 34 | ||
| 35 | const out = buffer.subarray(i, i + HASHLEN) | |
| 36 | result.push(out) | |
| 37 | ||
| 38 | prev = hmacDigest(out, key, [prev, infoBuf, pos]) | |
| 39 | } | |
| 40 | ||
| 41 | return result | |
| 42 | } | |
| 43 | ||
| 44 | function hmacDigest (out, key, input) { | |
| 45 | hmacBlake2b(out, input, key) | |
| 46 | return out | |
| 47 | } |
| … | … | @@ -0,0 +1,38 @@ |
| 1 | /* eslint-disable camelcase */ | |
| 2 | const b4a = require('b4a') | |
| 3 | const { sodium_memzero, crypto_generichash, crypto_generichash_batch } = require('sodium-universal') | |
| 4 | ||
| 5 | const HASHLEN = 64 | |
| 6 | const BLOCKLEN = 128 | |
| 7 | const scratch = b4a.alloc(BLOCKLEN * 3) | |
| 8 | const HMACKey = scratch.subarray(BLOCKLEN * 0, BLOCKLEN * 1) | |
| 9 | const OuterKeyPad = scratch.subarray(BLOCKLEN * 1, BLOCKLEN * 2) | |
| 10 | const InnerKeyPad = scratch.subarray(BLOCKLEN * 2, BLOCKLEN * 3) | |
| 11 | ||
| 12 | // Post-fill is done in the cases where someone caught an exception that | |
| 13 | // happened before we were able to clear data at the end | |
| 14 | ||
| 15 | module.exports = function hmac (out, batch, key) { | |
| 16 | if (key.byteLength > BLOCKLEN) { | |
| 17 | crypto_generichash(HMACKey.subarray(0, HASHLEN), key) | |
| 18 | sodium_memzero(HMACKey.subarray(HASHLEN)) | |
| 19 | } else { | |
| 20 | // Covers key <= BLOCKLEN | |
| 21 | HMACKey.set(key) | |
| 22 | sodium_memzero(HMACKey.subarray(key.byteLength)) | |
| 23 | } | |
| 24 | ||
| 25 | for (let i = 0; i < HMACKey.byteLength; i++) { | |
| 26 | OuterKeyPad[i] = 0x5c ^ HMACKey[i] | |
| 27 | InnerKeyPad[i] = 0x36 ^ HMACKey[i] | |
| 28 | } | |
| 29 | sodium_memzero(HMACKey) | |
| 30 | ||
| 31 | crypto_generichash_batch(out, [InnerKeyPad].concat(batch)) | |
| 32 | sodium_memzero(InnerKeyPad) | |
| 33 | crypto_generichash_batch(out, [OuterKeyPad, out]) | |
| 34 | sodium_memzero(OuterKeyPad) | |
| 35 | } | |
| 36 | ||
| 37 | module.exports.BYTES = HASHLEN | |
| 38 | module.exports.KEYBYTES = BLOCKLEN |
| … | … | @@ -0,0 +1,279 @@ |
| 1 | cmake_minimum_required(VERSION 3.25) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) | |
| 5 | find_package(cmake-napi REQUIRED PATHS node_modules/cmake-napi) | |
| 6 | find_package(cmake-npm REQUIRED PATHS node_modules/cmake-npm) | |
| 7 | ||
| 8 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) | |
| 9 | ||
| 10 | project(sodium_native C ASM CXX) | |
| 11 | ||
| 12 | fetch_package("github:jedisct1/libsodium#e18eee6" SOURCE_DIR sodium) | |
| 13 | ||
| 14 | fetch_package("github:holepunchto/libjstl#098664c") | |
| 15 | ||
| 16 | bare_target(target) | |
| 17 | ||
| 18 | if(target MATCHES "win32") | |
| 19 | add_compile_options(/MT$<$<CONFIG:Debug>:d>) | |
| 20 | endif() | |
| 21 | ||
| 22 | file(COPY_FILE "${sodium}/builds/msvc/version.h" "${sodium}/src/libsodium/include/sodium/version.h") | |
| 23 | ||
| 24 | file(GLOB_RECURSE sodium_headers CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.h") | |
| 25 | file(GLOB_RECURSE sodium_sources CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.c") | |
| 26 | file(GLOB_RECURSE sodium_asm_sources CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.S") | |
| 27 | ||
| 28 | add_library(sodium OBJECT) | |
| 29 | ||
| 30 | target_sources( | |
| 31 | sodium | |
| 32 | INTERFACE | |
| 33 | ${sodium_headers} | |
| 34 | PRIVATE | |
| 35 | ${sodium_sources} | |
| 36 | ) | |
| 37 | ||
| 38 | target_include_directories( | |
| 39 | sodium | |
| 40 | INTERFACE | |
| 41 | "${sodium}/src/libsodium/include" | |
| 42 | PRIVATE | |
| 43 | "${sodium}/src/libsodium/include/sodium" | |
| 44 | ) | |
| 45 | ||
| 46 | if(NOT target MATCHES "win32") | |
| 47 | target_compile_options( | |
| 48 | sodium | |
| 49 | PRIVATE | |
| 50 | -fvisibility=hidden | |
| 51 | -fno-strict-aliasing | |
| 52 | -fwrapv | |
| 53 | -flax-vector-conversions | |
| 54 | ) | |
| 55 | endif() | |
| 56 | ||
| 57 | target_compile_definitions( | |
| 58 | sodium | |
| 59 | PUBLIC | |
| 60 | SODIUM_STATIC=1 | |
| 61 | PRIVATE | |
| 62 | _GNU_SOURCE=1 | |
| 63 | CONFIGURED=1 | |
| 64 | DEV_MODE=1 | |
| 65 | HAVE_ATOMIC_OPS=1 | |
| 66 | HAVE_C11_MEMORY_FENCES=1 | |
| 67 | HAVE_CET_H=1 | |
| 68 | HAVE_GCC_MEMORY_FENCES=1 | |
| 69 | HAVE_INLINE_ASM=1 | |
| 70 | HAVE_INTTYPES_H=1 | |
| 71 | HAVE_STDINT_H=1 | |
| 72 | HAVE_TI_MODE=1 | |
| 73 | ) | |
| 74 | ||
| 75 | if(target MATCHES "darwin|ios") | |
| 76 | target_compile_definitions( | |
| 77 | sodium | |
| 78 | PRIVATE | |
| 79 | ASM_HIDE_SYMBOL=.private_extern | |
| 80 | TLS=_Thread_local | |
| 81 | HAVE_ARC4RANDOM=1 | |
| 82 | HAVE_ARC4RANDOM_BUF=1 | |
| 83 | HAVE_CATCHABLE_ABRT=1 | |
| 84 | HAVE_CATCHABLE_SEGV=1 | |
| 85 | HAVE_CLOCK_GETTIME=1 | |
| 86 | HAVE_GETPID=1 | |
| 87 | HAVE_MADVISE=1 | |
| 88 | HAVE_MEMSET_S=1 | |
| 89 | HAVE_MLOCK=1 | |
| 90 | HAVE_MMAP=1 | |
| 91 | HAVE_MPROTECT=1 | |
| 92 | HAVE_NANOSLEEP=1 | |
| 93 | HAVE_POSIX_MEMALIGN=1 | |
| 94 | HAVE_PTHREAD=1 | |
| 95 | HAVE_PTHREAD_PRIO_INHERIT=1 | |
| 96 | HAVE_RAISE=1 | |
| 97 | HAVE_SYSCONF=1 | |
| 98 | HAVE_SYS_MMAN_H=1 | |
| 99 | HAVE_SYS_PARAM_H=1 | |
| 100 | HAVE_WEAK_SYMBOLS=1 | |
| 101 | ) | |
| 102 | ||
| 103 | if(NOT target MATCHES "ios") | |
| 104 | target_compile_definitions( | |
| 105 | sodium | |
| 106 | PRIVATE | |
| 107 | HAVE_GETENTROPY=1 | |
| 108 | HAVE_SYS_RANDOM_H=1 | |
| 109 | ) | |
| 110 | endif() | |
| 111 | endif() | |
| 112 | ||
| 113 | if(target MATCHES "linux") | |
| 114 | target_compile_definitions( | |
| 115 | sodium | |
| 116 | PRIVATE | |
| 117 | ASM_HIDE_SYMBOL=.hidden | |
| 118 | TLS=_Thread_local | |
| 119 | HAVE_CATCHABLE_ABRT=1 | |
| 120 | HAVE_CATCHABLE_SEGV=1 | |
| 121 | HAVE_CLOCK_GETTIME=1 | |
| 122 | HAVE_GETPID=1 | |
| 123 | HAVE_MADVISE=1 | |
| 124 | HAVE_MLOCK=1 | |
| 125 | HAVE_MMAP=1 | |
| 126 | HAVE_MPROTECT=1 | |
| 127 | HAVE_NANOSLEEP=1 | |
| 128 | HAVE_POSIX_MEMALIGN=1 | |
| 129 | HAVE_PTHREAD_PRIO_INHERIT=1 | |
| 130 | HAVE_PTHREAD=1 | |
| 131 | HAVE_RAISE=1 | |
| 132 | HAVE_SYSCONF=1 | |
| 133 | HAVE_SYS_AUXV_H=1 | |
| 134 | HAVE_SYS_MMAN_H=1 | |
| 135 | HAVE_SYS_PARAM_H=1 | |
| 136 | HAVE_SYS_RANDOM_H=1 | |
| 137 | HAVE_WEAK_SYMBOLS=1 | |
| 138 | ) | |
| 139 | endif() | |
| 140 | ||
| 141 | if(target MATCHES "win32") | |
| 142 | target_compile_definitions( | |
| 143 | sodium | |
| 144 | PRIVATE | |
| 145 | _CRT_SECURE_NO_WARNINGS=1 | |
| 146 | HAVE_RAISE=1 | |
| 147 | ) | |
| 148 | endif() | |
| 149 | ||
| 150 | if(target MATCHES "x64") | |
| 151 | target_compile_definitions( | |
| 152 | sodium | |
| 153 | PRIVATE | |
| 154 | HAVE_CPUID=1 | |
| 155 | HAVE_RDRAND=1 | |
| 156 | HAVE_EMMINTRIN_H=1 # SSE2 | |
| 157 | HAVE_PMMINTRIN_H=1 # SSE3 | |
| 158 | HAVE_TMMINTRIN_H=1 # SSSE3 | |
| 159 | HAVE_SMMINTRIN_H=1 # SSE4.1 | |
| 160 | HAVE_WMMINTRIN_H=1 # AES | |
| 161 | HAVE_AVXINTRIN_H=1 # AVX | |
| 162 | HAVE_AVX2INTRIN_H=1 # AVX2 | |
| 163 | HAVE_AVX512FINTRIN_H # AVX512F | |
| 164 | ) | |
| 165 | ||
| 166 | if(NOT target MATCHES "win32") | |
| 167 | target_compile_definitions( | |
| 168 | sodium | |
| 169 | PRIVATE | |
| 170 | HAVE_AMD64_ASM=1 | |
| 171 | HAVE_AVX_ASM=1 | |
| 172 | ) | |
| 173 | ||
| 174 | target_sources( | |
| 175 | sodium | |
| 176 | PRIVATE | |
| 177 | ${sodium_asm_sources} | |
| 178 | ) | |
| 179 | endif() | |
| 180 | endif() | |
| 181 | ||
| 182 | if(target MATCHES "arm64") | |
| 183 | target_compile_definitions( | |
| 184 | sodium | |
| 185 | PRIVATE | |
| 186 | HAVE_ARMCRYPTO=1 | |
| 187 | ) | |
| 188 | endif() | |
| 189 | ||
| 190 | if(CMAKE_C_BYTE_ORDER MATCHES "BIG_ENDIAN") | |
| 191 | target_compile_definitions( | |
| 192 | sodium | |
| 193 | PRIVATE | |
| 194 | NATIVE_BIG_ENDIAN=1 | |
| 195 | ) | |
| 196 | else() | |
| 197 | target_compile_definitions( | |
| 198 | sodium | |
| 199 | PRIVATE | |
| 200 | NATIVE_LITTLE_ENDIAN=1 | |
| 201 | ) | |
| 202 | endif() | |
| 203 | ||
| 204 | if(target MATCHES "linux|android") | |
| 205 | target_link_options( | |
| 206 | sodium | |
| 207 | PUBLIC | |
| 208 | -Wl,-z,noexecstack | |
| 209 | ) | |
| 210 | endif() | |
| 211 | ||
| 212 | add_library(sodium_extensions OBJECT) | |
| 213 | ||
| 214 | target_sources( | |
| 215 | sodium_extensions | |
| 216 | PRIVATE | |
| 217 | extensions/tweak/tweak.c | |
| 218 | extensions/tweak/tweak.h | |
| 219 | extensions/pbkdf2/pbkdf2.c | |
| 220 | extensions/pbkdf2/pbkdf2.h | |
| 221 | ) | |
| 222 | ||
| 223 | target_link_libraries( | |
| 224 | sodium_extensions | |
| 225 | PUBLIC | |
| 226 | sodium | |
| 227 | ) | |
| 228 | ||
| 229 | add_bare_module(sodium_native_bare) | |
| 230 | ||
| 231 | target_sources( | |
| 232 | ${sodium_native_bare} | |
| 233 | PRIVATE | |
| 234 | binding.cc | |
| 235 | ) | |
| 236 | ||
| 237 | target_link_libraries( | |
| 238 | ${sodium_native_bare} | |
| 239 | PRIVATE | |
| 240 | $<TARGET_OBJECTS:sodium> | |
| 241 | $<TARGET_OBJECTS:sodium_extensions> | |
| 242 | jstl | |
| 243 | PUBLIC | |
| 244 | sodium | |
| 245 | sodium_extensions | |
| 246 | ) | |
| 247 | ||
| 248 | add_napi_module(sodium_native_node) | |
| 249 | ||
| 250 | target_sources( | |
| 251 | ${sodium_native_node} | |
| 252 | PRIVATE | |
| 253 | binding.cc | |
| 254 | ) | |
| 255 | ||
| 256 | target_compile_definitions( | |
| 257 | ${sodium_native_node} | |
| 258 | PRIVATE | |
| 259 | NAPI_VERSION=9 | |
| 260 | ) | |
| 261 | ||
| 262 | target_link_libraries( | |
| 263 | ${sodium_native_node} | |
| 264 | PRIVATE | |
| 265 | $<TARGET_OBJECTS:sodium> | |
| 266 | $<TARGET_OBJECTS:sodium_extensions> | |
| 267 | jstl | |
| 268 | PUBLIC | |
| 269 | sodium | |
| 270 | sodium_extensions | |
| 271 | ) | |
| 272 | ||
| 273 | resolve_node_module(bare-compat-napi compat) | |
| 274 | ||
| 275 | target_include_directories( | |
| 276 | ${sodium_native_node} | |
| 277 | PRIVATE | |
| 278 | "${compat}/include" | |
| 279 | ) |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2016 Mathias Buus and Emil Bay | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,51 @@ |
| 1 | # sodium-native | |
| 2 | ||
| 3 | Low level bindings for [libsodium](https://github.com/jedisct1/libsodium). | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install sodium-native | |
| 7 | ``` | |
| 8 | ||
| 9 | The goal of this project is to be thin, stable, unopionated wrapper around libsodium. | |
| 10 | ||
| 11 | All methods exposed are more or less a direct translation of the libsodium c-api. | |
| 12 | This means that most data types are buffers and you have to manage allocating return values and passing them in as arguments instead of receiving them as return values. | |
| 13 | ||
| 14 | This makes this API harder to use than other libsodium wrappers out there, but also means that you'll be able to get a lot of perf / memory improvements as you can do stuff like inline encryption / decryption, reuse buffers etc. | |
| 15 | ||
| 16 | This also makes this library useful as a foundation for more high level crypto abstractions that you want to make. | |
| 17 | ||
| 18 | ## Usage | |
| 19 | ||
| 20 | ```js | |
| 21 | var sodium = require('sodium-native') | |
| 22 | ||
| 23 | var nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES) | |
| 24 | var key = sodium.sodium_malloc(sodium.crypto_secretbox_KEYBYTES) // secure buffer | |
| 25 | var message = Buffer.from('Hello, World!') | |
| 26 | var ciphertext = Buffer.alloc(message.length + sodium.crypto_secretbox_MACBYTES) | |
| 27 | ||
| 28 | sodium.randombytes_buf(nonce) // insert random data into nonce | |
| 29 | sodium.randombytes_buf(key) // insert random data into key | |
| 30 | ||
| 31 | // encrypted message is stored in ciphertext. | |
| 32 | sodium.crypto_secretbox_easy(ciphertext, message, nonce, key) | |
| 33 | ||
| 34 | console.log('Encrypted message:', ciphertext) | |
| 35 | ||
| 36 | var plainText = Buffer.alloc(ciphertext.length - sodium.crypto_secretbox_MACBYTES) | |
| 37 | ||
| 38 | if (!sodium.crypto_secretbox_open_easy(plainText, ciphertext, nonce, key)) { | |
| 39 | console.log('Decryption failed!') | |
| 40 | } else { | |
| 41 | console.log('Decrypted message:', plainText, '(' + plainText.toString() + ')') | |
| 42 | } | |
| 43 | ``` | |
| 44 | ||
| 45 | ## Documentation | |
| 46 | ||
| 47 | Complete documentation may be found on the [sodium-friends website](https://sodium-friends.github.io/docs/docs/getstarted) | |
| 48 | ||
| 49 | ## License | |
| 50 | ||
| 51 | MIT |
| … | … | @@ -0,0 +1,4094 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <bare.h> | |
| 3 | #include <js.h> | |
| 4 | #include <jstl.h> | |
| 5 | #include <sodium.h> | |
| 6 | #include <stdbool.h> | |
| 7 | #include <stdint.h> | |
| 8 | #include <string.h> | |
| 9 | #include <uv.h> | |
| 10 | ||
| 11 | #include "extensions/pbkdf2/pbkdf2.h" | |
| 12 | #include "extensions/tweak/tweak.h" | |
| 13 | ||
| 14 | #define assert_bounds(arraybuffer) \ | |
| 15 | assert(arraybuffer##_offset + arraybuffer##_len <= arraybuffer.size()) | |
| 16 | ||
| 17 | static inline void | |
| 18 | sn_sodium_memzero( | |
| 19 | js_env_t *, | |
| 20 | js_receiver_t, | |
| 21 | js_typedarray_span_t<> buf | |
| 22 | ) { | |
| 23 | sodium_memzero(buf.data(), buf.size_bytes()); | |
| 24 | } | |
| 25 | ||
| 26 | static inline int | |
| 27 | sn_sodium_mlock( | |
| 28 | js_env_t *, | |
| 29 | js_receiver_t, | |
| 30 | js_typedarray_span_t<> buf | |
| 31 | ) { | |
| 32 | return sodium_mlock(buf.data(), buf.size_bytes()); | |
| 33 | } | |
| 34 | ||
| 35 | static inline int | |
| 36 | sn_sodium_munlock( | |
| 37 | js_env_t *, | |
| 38 | js_receiver_t, | |
| 39 | js_typedarray_span_t<> buf | |
| 40 | ) { | |
| 41 | return sodium_munlock(buf.data(), buf.size_bytes()); | |
| 42 | } | |
| 43 | ||
| 44 | static void | |
| 45 | sn_external_arraybuffer_finalize(js_env_t *env, uint8_t *ptr) { | |
| 46 | sodium_free(ptr); | |
| 47 | } | |
| 48 | ||
| 49 | static js_arraybuffer_t | |
| 50 | sn_sodium_malloc(js_env_t *env, js_receiver_t, uint32_t len) { | |
| 51 | int err; | |
| 52 | ||
| 53 | auto ptr = reinterpret_cast<uint8_t *>(sodium_malloc(len)); | |
| 54 | ||
| 55 | js_arraybuffer_t buffer; | |
| 56 | ||
| 57 | err = js_create_external_arraybuffer<sn_external_arraybuffer_finalize>(env, ptr, len, buffer); | |
| 58 | assert(err == 0); | |
| 59 | ||
| 60 | return buffer; | |
| 61 | } | |
| 62 | ||
| 63 | static void | |
| 64 | sn_sodium_free(js_env_t *env, js_receiver_t, js_arraybuffer_t buf) { | |
| 65 | int err; | |
| 66 | ||
| 67 | std::span<uint8_t> view; | |
| 68 | err = js_get_arraybuffer_info(env, buf, view); | |
| 69 | assert(err == 0); | |
| 70 | ||
| 71 | if (view.empty()) return; | |
| 72 | ||
| 73 | err = js_detach_arraybuffer(env, buf); | |
| 74 | assert(err == 0); | |
| 75 | } | |
| 76 | ||
| 77 | static inline int | |
| 78 | sn_sodium_mprotect_noaccess(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 79 | return sodium_mprotect_noaccess(buf.data()); | |
| 80 | } | |
| 81 | ||
| 82 | static inline int | |
| 83 | sn_sodium_mprotect_readonly(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 84 | return sodium_mprotect_readonly(buf.data()); | |
| 85 | } | |
| 86 | ||
| 87 | static inline int | |
| 88 | sn_sodium_mprotect_readwrite(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 89 | return sodium_mprotect_readwrite(buf.data()); | |
| 90 | } | |
| 91 | ||
| 92 | static inline uint32_t // TODO: test envless | |
| 93 | sn_randombytes_random(js_env_t *env, js_receiver_t) { | |
| 94 | return randombytes_random(); | |
| 95 | } | |
| 96 | ||
| 97 | static inline uint32_t // TODO: test envless | |
| 98 | sn_randombytes_uniform(js_env_t *env, js_receiver_t, uint32_t upper_bound) { | |
| 99 | return randombytes_uniform(upper_bound); | |
| 100 | } | |
| 101 | ||
| 102 | static inline void | |
| 103 | sn_randombytes_buf( | |
| 104 | js_env_t *env, | |
| 105 | js_receiver_t, | |
| 106 | js_arraybuffer_span_t buf, | |
| 107 | uint32_t buf_offset, | |
| 108 | uint32_t buf_len | |
| 109 | ) { | |
| 110 | assert_bounds(buf); | |
| 111 | ||
| 112 | randombytes_buf(&buf[buf_offset], buf_len); | |
| 113 | } | |
| 114 | ||
| 115 | static inline void | |
| 116 | sn_randombytes_buf_deterministic( | |
| 117 | js_env_t *env, | |
| 118 | js_receiver_t, | |
| 119 | ||
| 120 | js_arraybuffer_span_t buf, | |
| 121 | uint32_t buf_offset, | |
| 122 | uint32_t buf_len, | |
| 123 | ||
| 124 | js_arraybuffer_span_t seed, | |
| 125 | uint32_t seed_offset, | |
| 126 | uint32_t seed_len | |
| 127 | ) { | |
| 128 | assert_bounds(buf); | |
| 129 | assert_bounds(seed); | |
| 130 | ||
| 131 | assert(seed_len == randombytes_SEEDBYTES); | |
| 132 | ||
| 133 | randombytes_buf_deterministic(&buf[buf_offset], buf_len, &seed[seed_offset]); | |
| 134 | } | |
| 135 | ||
| 136 | static inline bool | |
| 137 | sn_sodium_memcmp(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 138 | if (a.size_bytes() != b.size_bytes()) return false; | |
| 139 | ||
| 140 | return sodium_memcmp(a.data(), b.data(), a.size_bytes()) == 0; | |
| 141 | } | |
| 142 | ||
| 143 | static inline void | |
| 144 | sn_sodium_increment(js_env_t *, js_receiver_t, js_typedarray_span_t<> n) { | |
| 145 | sodium_increment(n.data(), n.size_bytes()); | |
| 146 | } | |
| 147 | ||
| 148 | static inline void | |
| 149 | sn_sodium_add(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 150 | sodium_add(a.data(), b.data(), a.size_bytes()); | |
| 151 | } | |
| 152 | ||
| 153 | static inline void | |
| 154 | sn_sodium_sub(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 155 | sodium_sub(a.data(), b.data(), a.size_bytes()); | |
| 156 | } | |
| 157 | ||
| 158 | static inline int32_t | |
| 159 | sn_sodium_compare(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 160 | return sodium_compare(a.data(), b.data(), a.size_bytes()); | |
| 161 | } | |
| 162 | ||
| 163 | static inline bool | |
| 164 | sn_sodium_is_zero(js_env_t *, js_receiver_t, js_typedarray_span_t<> buffer, uint32_t len) { | |
| 165 | assert(len <= buffer.size_bytes()); | |
| 166 | ||
| 167 | return sodium_is_zero(buffer.data(), len) != 0; | |
| 168 | } | |
| 169 | ||
| 170 | static inline uint32_t | |
| 171 | sn_sodium_pad(js_env_t *, js_receiver_t, js_typedarray_span_t<> buf, uint32_t unpadded_buflen, uint32_t blocksize) { | |
| 172 | size_t padded_buflen; | |
| 173 | ||
| 174 | sodium_pad(&padded_buflen, buf.data(), unpadded_buflen, blocksize, buf.size_bytes()); | |
| 175 | ||
| 176 | return padded_buflen; | |
| 177 | } | |
| 178 | ||
| 179 | static inline uint32_t | |
| 180 | sn_sodium_unpad(js_env_t *, js_receiver_t, js_typedarray_span_t<> buf, uint32_t padded_buflen, uint32_t blocksize) { | |
| 181 | size_t unpadded_buflen; | |
| 182 | ||
| 183 | sodium_unpad(&unpadded_buflen, buf.data(), padded_buflen, blocksize); | |
| 184 | ||
| 185 | return unpadded_buflen; | |
| 186 | } | |
| 187 | ||
| 188 | static inline int | |
| 189 | sn_crypto_sign_keypair(js_env_t *, js_receiver_t, js_typedarray_span_t<> pk, js_typedarray_span_t<> sk) { | |
| 190 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 191 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 192 | ||
| 193 | return crypto_sign_keypair(pk.data(), sk.data()); | |
| 194 | } | |
| 195 | ||
| 196 | static inline int | |
| 197 | sn_crypto_sign_seed_keypair( | |
| 198 | js_env_t *, | |
| 199 | js_receiver_t, | |
| 200 | js_typedarray_span_t<> pk, | |
| 201 | js_typedarray_span_t<> sk, | |
| 202 | js_typedarray_span_t<> seed | |
| 203 | ) { | |
| 204 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 205 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 206 | assert(seed.size_bytes() == crypto_sign_SEEDBYTES); | |
| 207 | ||
| 208 | return crypto_sign_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 209 | } | |
| 210 | ||
| 211 | static inline int | |
| 212 | sn_crypto_sign( | |
| 213 | js_env_t *, | |
| 214 | js_receiver_t, | |
| 215 | js_typedarray_span_t<> sm, | |
| 216 | js_typedarray_span_t<> m, | |
| 217 | js_typedarray_span_t<> sk | |
| 218 | ) { | |
| 219 | assert(sm.size_bytes() == crypto_sign_BYTES + m.size_bytes()); | |
| 220 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 221 | ||
| 222 | return crypto_sign(sm.data(), NULL, m.data(), m.size_bytes(), sk.data()); | |
| 223 | } | |
| 224 | ||
| 225 | static inline bool | |
| 226 | sn_crypto_sign_open( | |
| 227 | js_env_t *, | |
| 228 | js_receiver_t, | |
| 229 | js_typedarray_span_t<> m, | |
| 230 | js_typedarray_span_t<> sm, | |
| 231 | js_typedarray_span_t<> pk | |
| 232 | ) { | |
| 233 | assert(m.size_bytes() == sm.size_bytes() - crypto_sign_BYTES); | |
| 234 | assert(sm.size_bytes() >= crypto_sign_BYTES); | |
| 235 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 236 | ||
| 237 | return crypto_sign_open(m.data(), NULL, sm.data(), sm.size_bytes(), pk.data()) == 0; | |
| 238 | } | |
| 239 | ||
| 240 | static inline int | |
| 241 | sn_crypto_sign_detached( | |
| 242 | js_env_t *env, | |
| 243 | js_receiver_t, | |
| 244 | js_typedarray_span_t<> sig, | |
| 245 | js_typedarray_span_t<> m, | |
| 246 | js_typedarray_span_t<> sk | |
| 247 | ) { | |
| 248 | assert(sig.size_bytes() == crypto_sign_BYTES); | |
| 249 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 250 | ||
| 251 | return crypto_sign_detached(sig.data(), NULL, m.data(), m.size_bytes(), sk.data()); | |
| 252 | } | |
| 253 | ||
| 254 | static inline bool | |
| 255 | sn_crypto_sign_verify_detached( | |
| 256 | js_env_t *env, | |
| 257 | js_receiver_t, | |
| 258 | ||
| 259 | js_arraybuffer_span_t sig, | |
| 260 | uint32_t sig_offset, | |
| 261 | uint32_t sig_len, | |
| 262 | ||
| 263 | js_arraybuffer_span_t m, | |
| 264 | uint32_t m_offset, | |
| 265 | uint32_t m_len, | |
| 266 | ||
| 267 | js_arraybuffer_span_t pk, | |
| 268 | uint32_t pk_offset, | |
| 269 | uint32_t pk_len | |
| 270 | ) { | |
| 271 | assert_bounds(sig); | |
| 272 | assert_bounds(m); | |
| 273 | assert_bounds(pk); | |
| 274 | ||
| 275 | assert(sig_len >= crypto_sign_BYTES); | |
| 276 | assert(pk_len == crypto_sign_PUBLICKEYBYTES); | |
| 277 | ||
| 278 | return crypto_sign_verify_detached(&sig[sig_offset], &m[m_offset], m_len, &pk[pk_offset]) == 0; | |
| 279 | } | |
| 280 | ||
| 281 | static inline int | |
| 282 | sn_crypto_sign_ed25519_sk_to_pk( | |
| 283 | js_env_t *, | |
| 284 | js_receiver_t, | |
| 285 | js_typedarray_span_t<> pk, | |
| 286 | js_typedarray_span_t<> sk | |
| 287 | ) { | |
| 288 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 289 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 290 | ||
| 291 | return crypto_sign_ed25519_sk_to_pk(pk.data(), sk.data()); | |
| 292 | } | |
| 293 | ||
| 294 | static inline int | |
| 295 | sn_crypto_sign_ed25519_pk_to_curve25519( | |
| 296 | js_env_t *, | |
| 297 | js_receiver_t, | |
| 298 | js_typedarray_span_t<> x25519_pk, | |
| 299 | js_typedarray_span_t<> ed25519_pk | |
| 300 | ) { | |
| 301 | assert(x25519_pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 302 | assert(ed25519_pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 303 | ||
| 304 | return crypto_sign_ed25519_pk_to_curve25519(x25519_pk.data(), ed25519_pk.data()); | |
| 305 | } | |
| 306 | ||
| 307 | static inline int | |
| 308 | sn_crypto_sign_ed25519_sk_to_curve25519( | |
| 309 | js_env_t *, | |
| 310 | js_receiver_t, | |
| 311 | js_typedarray_span_t<> x25519_sk, | |
| 312 | js_typedarray_span_t<> ed25519_sk | |
| 313 | ) { | |
| 314 | assert(x25519_sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 315 | assert( | |
| 316 | ed25519_sk.size_bytes() == crypto_sign_SECRETKEYBYTES || | |
| 317 | ed25519_sk.size_bytes() == crypto_box_SECRETKEYBYTES | |
| 318 | ); | |
| 319 | ||
| 320 | return crypto_sign_ed25519_sk_to_curve25519(x25519_sk.data(), ed25519_sk.data()); | |
| 321 | } | |
| 322 | ||
| 323 | static inline int | |
| 324 | sn_crypto_generichash( | |
| 325 | js_env_t *env, | |
| 326 | js_receiver_t, | |
| 327 | ||
| 328 | js_arraybuffer_span_t out, | |
| 329 | uint32_t out_offset, | |
| 330 | uint32_t out_len, | |
| 331 | ||
| 332 | js_arraybuffer_span_t in, | |
| 333 | uint32_t in_offset, | |
| 334 | uint32_t in_len, | |
| 335 | ||
| 336 | js_arraybuffer_span_t key, | |
| 337 | uint32_t key_offset, | |
| 338 | uint32_t key_len | |
| 339 | ) { | |
| 340 | assert_bounds(out); | |
| 341 | assert( | |
| 342 | out_len >= crypto_generichash_BYTES_MIN && | |
| 343 | out_len <= crypto_generichash_BYTES_MAX | |
| 344 | ); | |
| 345 | ||
| 346 | assert_bounds(in); | |
| 347 | ||
| 348 | uint8_t *key_data = NULL; | |
| 349 | if (key_len) { | |
| 350 | assert_bounds(key); | |
| 351 | assert( | |
| 352 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 353 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 354 | ); | |
| 355 | key_data = &key[key_offset]; | |
| 356 | } | |
| 357 | ||
| 358 | return crypto_generichash(&out[out_offset], out_len, &in[in_offset], in_len, key_data, key_len); | |
| 359 | } | |
| 360 | ||
| 361 | static inline int | |
| 362 | sn_crypto_generichash_batch( | |
| 363 | js_env_t *env, | |
| 364 | js_receiver_t, | |
| 365 | js_typedarray_t<uint8_t> out, | |
| 366 | std::vector<js_typedarray_span_t<>> batch, | |
| 367 | bool use_key, | |
| 368 | js_typedarray_t<uint8_t> key | |
| 369 | ) { | |
| 370 | int err; | |
| 371 | ||
| 372 | uint8_t *out_data; | |
| 373 | size_t out_len; | |
| 374 | err = js_get_typedarray_info(env, out, out_data, out_len); | |
| 375 | assert(err == 0); | |
| 376 | assert( | |
| 377 | out_len >= crypto_generichash_BYTES_MIN && | |
| 378 | out_len <= crypto_generichash_BYTES_MAX | |
| 379 | ); | |
| 380 | ||
| 381 | uint8_t *key_data = NULL; | |
| 382 | size_t key_len = 0; | |
| 383 | if (use_key) { | |
| 384 | err = js_get_typedarray_info(env, key, key_data, key_len); | |
| 385 | assert(err == 0); | |
| 386 | assert( | |
| 387 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 388 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 389 | ); | |
| 390 | } | |
| 391 | ||
| 392 | crypto_generichash_state state; | |
| 393 | err = crypto_generichash_init(&state, key_data, key_len, out_len); | |
| 394 | if (err != 0) return err; | |
| 395 | ||
| 396 | for (auto &buf : batch) { | |
| 397 | err = crypto_generichash_update(&state, buf.data(), buf.size()); | |
| 398 | if (err != 0) return err; | |
| 399 | } | |
| 400 | ||
| 401 | return crypto_generichash_final(&state, out_data, out_len); | |
| 402 | } | |
| 403 | ||
| 404 | static inline void | |
| 405 | sn_crypto_generichash_keygen( | |
| 406 | js_env_t *env, | |
| 407 | js_receiver_t, | |
| 408 | ||
| 409 | js_arraybuffer_span_t key, | |
| 410 | uint32_t key_offset, | |
| 411 | uint32_t key_len | |
| 412 | ) { | |
| 413 | assert_bounds(key); | |
| 414 | assert(key_len == crypto_generichash_KEYBYTES); | |
| 415 | ||
| 416 | crypto_generichash_keygen(&key[key_offset]); | |
| 417 | } | |
| 418 | ||
| 419 | static inline int | |
| 420 | sn_crypto_generichash_init( | |
| 421 | js_env_t *env, | |
| 422 | js_receiver_t, | |
| 423 | ||
| 424 | js_arraybuffer_span_t state, | |
| 425 | uint32_t state_offset, | |
| 426 | uint32_t state_len, | |
| 427 | ||
| 428 | js_arraybuffer_span_t key, | |
| 429 | uint32_t key_offset, | |
| 430 | uint32_t key_len, | |
| 431 | ||
| 432 | uint32_t out_len | |
| 433 | ) { | |
| 434 | assert_bounds(state); | |
| 435 | ||
| 436 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 437 | ||
| 438 | uint8_t *key_data = NULL; | |
| 439 | if (key_len) { | |
| 440 | assert_bounds(key); | |
| 441 | assert( | |
| 442 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 443 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 444 | ); | |
| 445 | key_data = &key[key_offset]; | |
| 446 | } | |
| 447 | ||
| 448 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 449 | ||
| 450 | return crypto_generichash_init(state_data, key_data, key_len, out_len); | |
| 451 | } | |
| 452 | ||
| 453 | static inline int | |
| 454 | sn_crypto_generichash_update( | |
| 455 | js_env_t *env, | |
| 456 | js_receiver_t, | |
| 457 | ||
| 458 | js_arraybuffer_span_t state, | |
| 459 | uint32_t state_offset, | |
| 460 | uint32_t state_len, | |
| 461 | ||
| 462 | js_arraybuffer_span_t in, | |
| 463 | uint32_t in_offset, | |
| 464 | uint32_t in_len | |
| 465 | ) { | |
| 466 | assert_bounds(state); | |
| 467 | assert_bounds(in); | |
| 468 | ||
| 469 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 470 | ||
| 471 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 472 | ||
| 473 | return crypto_generichash_update(state_data, &in[in_offset], in_len); | |
| 474 | } | |
| 475 | ||
| 476 | static inline int | |
| 477 | sn_crypto_generichash_final( | |
| 478 | js_env_t *env, | |
| 479 | js_receiver_t, | |
| 480 | ||
| 481 | js_arraybuffer_span_t state, | |
| 482 | uint32_t state_offset, | |
| 483 | uint32_t state_len, | |
| 484 | ||
| 485 | js_arraybuffer_span_t out, | |
| 486 | uint32_t out_offset, | |
| 487 | uint32_t out_len | |
| 488 | ) { | |
| 489 | assert_bounds(state); | |
| 490 | assert_bounds(out); | |
| 491 | ||
| 492 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 493 | ||
| 494 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 495 | ||
| 496 | return crypto_generichash_final(state_data, &out[out_offset], out_len); | |
| 497 | } | |
| 498 | ||
| 499 | static inline int | |
| 500 | sn_crypto_box_keypair(js_env_t *, js_receiver_t, js_typedarray_span_t<> pk, js_typedarray_span_t<> sk) { | |
| 501 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 502 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 503 | ||
| 504 | return crypto_box_keypair(pk.data(), sk.data()); | |
| 505 | } | |
| 506 | ||
| 507 | static inline int | |
| 508 | sn_crypto_box_seed_keypair( | |
| 509 | js_env_t *, | |
| 510 | js_receiver_t, | |
| 511 | js_typedarray_span_t<> pk, | |
| 512 | js_typedarray_span_t<> sk, | |
| 513 | js_typedarray_span_t<> seed | |
| 514 | ) { | |
| 515 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 516 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 517 | assert(seed.size_bytes() == crypto_box_SEEDBYTES); | |
| 518 | ||
| 519 | return crypto_box_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 520 | } | |
| 521 | ||
| 522 | static inline int | |
| 523 | sn_crypto_box_easy( | |
| 524 | js_env_t *, | |
| 525 | js_receiver_t, | |
| 526 | js_typedarray_span_t<> c, | |
| 527 | js_typedarray_span_t<> m, | |
| 528 | js_typedarray_span_t<> n, | |
| 529 | js_typedarray_span_t<> pk, | |
| 530 | js_typedarray_span_t<> sk | |
| 531 | ) { | |
| 532 | assert(c.size_bytes() == m.size_bytes() + crypto_box_MACBYTES); | |
| 533 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 534 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 535 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 536 | ||
| 537 | return crypto_box_easy(c.data(), m.data(), m.size_bytes(), n.data(), pk.data(), sk.data()); | |
| 538 | } | |
| 539 | ||
| 540 | static inline bool | |
| 541 | sn_crypto_box_open_easy( | |
| 542 | js_env_t *, | |
| 543 | js_receiver_t, | |
| 544 | js_typedarray_span_t<> m, | |
| 545 | js_typedarray_span_t<> c, | |
| 546 | js_typedarray_span_t<> n, | |
| 547 | js_typedarray_span_t<> pk, | |
| 548 | js_typedarray_span_t<> sk | |
| 549 | ) { | |
| 550 | assert(c.size_bytes() >= crypto_box_MACBYTES); | |
| 551 | assert(m.size_bytes() == c.size_bytes() - crypto_box_MACBYTES); | |
| 552 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 553 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 554 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 555 | ||
| 556 | return crypto_box_open_easy(m.data(), c.data(), c.size_bytes(), n.data(), pk.data(), sk.data()) == 0; | |
| 557 | } | |
| 558 | ||
| 559 | static inline int | |
| 560 | sn_crypto_box_detached( | |
| 561 | js_env_t *, | |
| 562 | js_receiver_t, | |
| 563 | js_typedarray_span_t<> c, | |
| 564 | js_typedarray_span_t<> mac, | |
| 565 | js_typedarray_span_t<> m, | |
| 566 | js_typedarray_span_t<> n, | |
| 567 | js_typedarray_span_t<> pk, | |
| 568 | js_typedarray_span_t<> sk | |
| 569 | ) { | |
| 570 | assert(c.size_bytes() == m.size_bytes()); | |
| 571 | assert(mac.size_bytes() == crypto_box_MACBYTES); | |
| 572 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 573 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 574 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 575 | ||
| 576 | return crypto_box_detached(c.data(), mac.data(), m.data(), m.size_bytes(), n.data(), pk.data(), sk.data()); | |
| 577 | } | |
| 578 | ||
| 579 | static inline bool | |
| 580 | sn_crypto_box_open_detached( | |
| 581 | js_env_t *, | |
| 582 | js_receiver_t, | |
| 583 | js_typedarray_span_t<> m, | |
| 584 | js_typedarray_span_t<> c, | |
| 585 | js_typedarray_span_t<> mac, | |
| 586 | js_typedarray_span_t<> n, | |
| 587 | js_typedarray_span_t<> pk, | |
| 588 | js_typedarray_span_t<> sk | |
| 589 | ) { | |
| 590 | assert(m.size_bytes() == c.size_bytes()); | |
| 591 | assert(mac.size_bytes() == crypto_box_MACBYTES); | |
| 592 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 593 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 594 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 595 | ||
| 596 | return crypto_box_open_detached(m.data(), c.data(), mac.data(), c.size_bytes(), n.data(), pk.data(), sk.data()) == 0; | |
| 597 | } | |
| 598 | ||
| 599 | static inline int | |
| 600 | sn_crypto_box_seal( | |
| 601 | js_env_t *, | |
| 602 | js_receiver_t, | |
| 603 | js_typedarray_span_t<> c, | |
| 604 | js_typedarray_span_t<> m, | |
| 605 | js_typedarray_span_t<> pk | |
| 606 | ) { | |
| 607 | assert(c.size_bytes() == m.size_bytes() + crypto_box_SEALBYTES); | |
| 608 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 609 | ||
| 610 | return crypto_box_seal(c.data(), m.data(), m.size_bytes(), pk.data()); | |
| 611 | } | |
| 612 | ||
| 613 | static inline bool | |
| 614 | sn_crypto_box_seal_open( | |
| 615 | js_env_t *env, | |
| 616 | js_receiver_t, | |
| 617 | ||
| 618 | js_arraybuffer_span_t m, | |
| 619 | uint32_t m_offset, | |
| 620 | uint32_t m_len, | |
| 621 | ||
| 622 | js_arraybuffer_span_t c, | |
| 623 | uint32_t c_offset, | |
| 624 | uint32_t c_len, | |
| 625 | ||
| 626 | js_arraybuffer_span_t pk, | |
| 627 | uint32_t pk_offset, | |
| 628 | uint32_t pk_len, | |
| 629 | ||
| 630 | js_arraybuffer_span_t sk, | |
| 631 | uint32_t sk_offset, | |
| 632 | uint32_t sk_len | |
| 633 | ) { | |
| 634 | assert_bounds(m); | |
| 635 | assert_bounds(c); | |
| 636 | assert_bounds(pk); | |
| 637 | assert_bounds(sk); | |
| 638 | ||
| 639 | assert(m_len == c_len - crypto_box_SEALBYTES); | |
| 640 | assert(c_len >= crypto_box_SEALBYTES); | |
| 641 | assert(sk_len == crypto_box_SECRETKEYBYTES); | |
| 642 | assert(pk_len == crypto_box_PUBLICKEYBYTES); | |
| 643 | ||
| 644 | return crypto_box_seal_open(&m[m_offset], &c[c_offset], c_len, &pk[pk_offset], &sk[sk_offset]) == 0; | |
| 645 | } | |
| 646 | ||
| 647 | static inline int | |
| 648 | sn_crypto_secretbox_easy( | |
| 649 | js_env_t *, | |
| 650 | js_receiver_t, | |
| 651 | js_typedarray_span_t<> c, | |
| 652 | js_typedarray_span_t<> m, | |
| 653 | js_typedarray_span_t<> n, | |
| 654 | js_typedarray_span_t<> k | |
| 655 | ) { | |
| 656 | assert(c.size_bytes() == m.size_bytes() + crypto_secretbox_MACBYTES); | |
| 657 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 658 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 659 | ||
| 660 | return crypto_secretbox_easy(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 661 | } | |
| 662 | ||
| 663 | static inline bool | |
| 664 | sn_crypto_secretbox_open_easy( | |
| 665 | js_env_t *, | |
| 666 | js_receiver_t, | |
| 667 | js_typedarray_span_t<> m, | |
| 668 | js_typedarray_span_t<> c, | |
| 669 | js_typedarray_span_t<> n, | |
| 670 | js_typedarray_span_t<> k | |
| 671 | ) { | |
| 672 | assert(m.size_bytes() == c.size_bytes() - crypto_secretbox_MACBYTES); | |
| 673 | assert(c.size_bytes() >= crypto_secretbox_MACBYTES); | |
| 674 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 675 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 676 | ||
| 677 | return crypto_secretbox_open_easy(m.data(), c.data(), c.size_bytes(), n.data(), k.data()) == 0; | |
| 678 | } | |
| 679 | ||
| 680 | static inline int | |
| 681 | sn_crypto_secretbox_detached( | |
| 682 | js_env_t *, | |
| 683 | js_receiver_t, | |
| 684 | js_typedarray_span_t<> c, | |
| 685 | js_typedarray_span_t<> mac, | |
| 686 | js_typedarray_span_t<> m, | |
| 687 | js_typedarray_span_t<> n, | |
| 688 | js_typedarray_span_t<> k | |
| 689 | ) { | |
| 690 | assert(c.size_bytes() == m.size_bytes()); | |
| 691 | assert(mac.size_bytes() == crypto_secretbox_MACBYTES); | |
| 692 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 693 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 694 | ||
| 695 | return crypto_secretbox_detached(c.data(), mac.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 696 | } | |
| 697 | ||
| 698 | static inline bool | |
| 699 | sn_crypto_secretbox_open_detached( | |
| 700 | js_env_t *, | |
| 701 | js_receiver_t, | |
| 702 | js_typedarray_span_t<> m, | |
| 703 | js_typedarray_span_t<> c, | |
| 704 | js_typedarray_span_t<> mac, | |
| 705 | js_typedarray_span_t<> n, | |
| 706 | js_typedarray_span_t<> k | |
| 707 | ) { | |
| 708 | assert(m.size_bytes() == c.size_bytes()); | |
| 709 | assert(mac.size_bytes() == crypto_secretbox_MACBYTES); | |
| 710 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 711 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 712 | ||
| 713 | return crypto_secretbox_open_detached(m.data(), c.data(), mac.data(), c.size_bytes(), n.data(), k.data()) == 0; | |
| 714 | } | |
| 715 | ||
| 716 | static inline int | |
| 717 | sn_crypto_stream( | |
| 718 | js_env_t *, | |
| 719 | js_receiver_t, | |
| 720 | js_typedarray_span_t<> c, | |
| 721 | js_typedarray_span_t<> n, | |
| 722 | js_typedarray_span_t<> k | |
| 723 | ) { | |
| 724 | assert(n.size_bytes() == crypto_stream_NONCEBYTES); | |
| 725 | assert(k.size_bytes() == crypto_stream_KEYBYTES); | |
| 726 | ||
| 727 | return crypto_stream(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 728 | } | |
| 729 | ||
| 730 | static inline int | |
| 731 | sn_crypto_stream_xor( | |
| 732 | js_env_t *env, | |
| 733 | js_receiver_t, | |
| 734 | ||
| 735 | js_arraybuffer_span_t c, | |
| 736 | uint32_t c_offset, | |
| 737 | uint32_t c_len, | |
| 738 | ||
| 739 | js_arraybuffer_span_t m, | |
| 740 | uint32_t m_offset, | |
| 741 | uint32_t m_len, | |
| 742 | ||
| 743 | js_arraybuffer_span_t n, | |
| 744 | uint32_t n_offset, | |
| 745 | uint32_t n_len, | |
| 746 | ||
| 747 | js_arraybuffer_span_t k, | |
| 748 | uint32_t k_offset, | |
| 749 | uint32_t k_len | |
| 750 | ) { | |
| 751 | assert_bounds(c); | |
| 752 | assert_bounds(m); | |
| 753 | assert_bounds(n); | |
| 754 | assert_bounds(k); | |
| 755 | ||
| 756 | assert(c_len == m_len); | |
| 757 | assert(n_len == crypto_stream_NONCEBYTES); | |
| 758 | assert(k_len == crypto_stream_KEYBYTES); | |
| 759 | ||
| 760 | return crypto_stream_xor(&c[c_offset], &m[m_offset], m_len, &n[n_offset], &k[k_offset]); | |
| 761 | } | |
| 762 | ||
| 763 | static inline int | |
| 764 | sn_crypto_stream_chacha20( | |
| 765 | js_env_t *, | |
| 766 | js_receiver_t, | |
| 767 | js_typedarray_span_t<> c, | |
| 768 | js_typedarray_span_t<> n, | |
| 769 | js_typedarray_span_t<> k | |
| 770 | ) { | |
| 771 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 772 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 773 | ||
| 774 | return crypto_stream_chacha20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 775 | } | |
| 776 | ||
| 777 | static inline int | |
| 778 | sn_crypto_stream_chacha20_xor( | |
| 779 | js_env_t *, | |
| 780 | js_receiver_t, | |
| 781 | js_typedarray_span_t<> c, | |
| 782 | js_typedarray_span_t<> m, | |
| 783 | js_typedarray_span_t<> n, | |
| 784 | js_typedarray_span_t<> k | |
| 785 | ) { | |
| 786 | assert(c.size_bytes() == m.size_bytes()); | |
| 787 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 788 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 789 | ||
| 790 | return crypto_stream_chacha20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 791 | } | |
| 792 | ||
| 793 | static inline int | |
| 794 | sn_crypto_stream_chacha20_xor_ic( | |
| 795 | js_env_t *, | |
| 796 | js_receiver_t, | |
| 797 | js_typedarray_span_t<> c, | |
| 798 | js_typedarray_span_t<> m, | |
| 799 | js_typedarray_span_t<> n, | |
| 800 | uint32_t ic, | |
| 801 | js_typedarray_span_t<> k | |
| 802 | ) { | |
| 803 | assert(c.size_bytes() == m.size_bytes()); | |
| 804 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 805 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 806 | ||
| 807 | return crypto_stream_chacha20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 808 | } | |
| 809 | ||
| 810 | static inline int | |
| 811 | sn_crypto_stream_chacha20_ietf( | |
| 812 | js_env_t *, | |
| 813 | js_receiver_t, | |
| 814 | js_typedarray_span_t<> c, | |
| 815 | js_typedarray_span_t<> n, | |
| 816 | js_typedarray_span_t<> k | |
| 817 | ) { | |
| 818 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 819 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 820 | ||
| 821 | return crypto_stream_chacha20_ietf(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 822 | } | |
| 823 | ||
| 824 | static inline int | |
| 825 | sn_crypto_stream_chacha20_ietf_xor( | |
| 826 | js_env_t *, | |
| 827 | js_receiver_t, | |
| 828 | js_typedarray_span_t<> c, | |
| 829 | js_typedarray_span_t<> m, | |
| 830 | js_typedarray_span_t<> n, | |
| 831 | js_typedarray_span_t<> k | |
| 832 | ) { | |
| 833 | assert(c.size_bytes() == m.size_bytes()); | |
| 834 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 835 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 836 | ||
| 837 | return crypto_stream_chacha20_ietf_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 838 | } | |
| 839 | ||
| 840 | static inline int | |
| 841 | sn_crypto_stream_chacha20_ietf_xor_ic( | |
| 842 | js_env_t *, | |
| 843 | js_receiver_t, | |
| 844 | js_typedarray_span_t<> c, | |
| 845 | js_typedarray_span_t<> m, | |
| 846 | js_typedarray_span_t<> n, | |
| 847 | uint32_t ic, | |
| 848 | js_typedarray_span_t<> k | |
| 849 | ) { | |
| 850 | assert(c.size_bytes() == m.size_bytes()); | |
| 851 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 852 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 853 | ||
| 854 | return crypto_stream_chacha20_ietf_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 855 | } | |
| 856 | ||
| 857 | static inline int | |
| 858 | sn_crypto_stream_xchacha20( | |
| 859 | js_env_t *, | |
| 860 | js_receiver_t, | |
| 861 | js_typedarray_span_t<> c, | |
| 862 | js_typedarray_span_t<> n, | |
| 863 | js_typedarray_span_t<> k | |
| 864 | ) { | |
| 865 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 866 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 867 | ||
| 868 | return crypto_stream_xchacha20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 869 | } | |
| 870 | ||
| 871 | static inline int | |
| 872 | sn_crypto_stream_xchacha20_xor( | |
| 873 | js_env_t *, | |
| 874 | js_receiver_t, | |
| 875 | js_typedarray_span_t<> c, | |
| 876 | js_typedarray_span_t<> m, | |
| 877 | js_typedarray_span_t<> n, | |
| 878 | js_typedarray_span_t<> k | |
| 879 | ) { | |
| 880 | assert(c.size_bytes() == m.size_bytes()); | |
| 881 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 882 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 883 | ||
| 884 | return crypto_stream_xchacha20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 885 | } | |
| 886 | ||
| 887 | static inline int | |
| 888 | sn_crypto_stream_xchacha20_xor_ic( | |
| 889 | js_env_t *, | |
| 890 | js_receiver_t, | |
| 891 | js_typedarray_span_t<> c, | |
| 892 | js_typedarray_span_t<> m, | |
| 893 | js_typedarray_span_t<> n, | |
| 894 | uint32_t ic, | |
| 895 | js_typedarray_span_t<> k | |
| 896 | ) { | |
| 897 | assert(c.size_bytes() == m.size_bytes()); | |
| 898 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 899 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 900 | ||
| 901 | return crypto_stream_xchacha20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 902 | } | |
| 903 | ||
| 904 | static inline int | |
| 905 | sn_crypto_stream_salsa20( | |
| 906 | js_env_t *, | |
| 907 | js_receiver_t, | |
| 908 | js_typedarray_span_t<> c, | |
| 909 | js_typedarray_span_t<> n, | |
| 910 | js_typedarray_span_t<> k | |
| 911 | ) { | |
| 912 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 913 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 914 | ||
| 915 | return crypto_stream_salsa20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 916 | } | |
| 917 | ||
| 918 | static inline int | |
| 919 | sn_crypto_stream_salsa20_xor( | |
| 920 | js_env_t *, | |
| 921 | js_receiver_t, | |
| 922 | js_typedarray_span_t<> c, | |
| 923 | js_typedarray_span_t<> m, | |
| 924 | js_typedarray_span_t<> n, | |
| 925 | js_typedarray_span_t<> k | |
| 926 | ) { | |
| 927 | assert(c.size_bytes() == m.size_bytes()); | |
| 928 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 929 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 930 | ||
| 931 | return crypto_stream_salsa20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 932 | } | |
| 933 | ||
| 934 | static inline int | |
| 935 | sn_crypto_stream_salsa20_xor_ic( | |
| 936 | js_env_t *, | |
| 937 | js_receiver_t, | |
| 938 | js_typedarray_span_t<> c, | |
| 939 | js_typedarray_span_t<> m, | |
| 940 | js_typedarray_span_t<> n, | |
| 941 | uint32_t ic, | |
| 942 | js_typedarray_span_t<> k | |
| 943 | ) { | |
| 944 | assert(c.size_bytes() == m.size_bytes()); | |
| 945 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 946 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 947 | ||
| 948 | return crypto_stream_salsa20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 949 | } | |
| 950 | ||
| 951 | static inline int | |
| 952 | sn_crypto_auth( | |
| 953 | js_env_t *, | |
| 954 | js_receiver_t, | |
| 955 | js_typedarray_span_t<> out, | |
| 956 | js_typedarray_span_t<> in, | |
| 957 | js_typedarray_span_t<> k | |
| 958 | ) { | |
| 959 | assert(out.size_bytes() == crypto_auth_BYTES); | |
| 960 | assert(k.size_bytes() == crypto_auth_KEYBYTES); | |
| 961 | ||
| 962 | return crypto_auth(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 963 | } | |
| 964 | ||
| 965 | static inline bool | |
| 966 | sn_crypto_auth_verify( | |
| 967 | js_env_t *, | |
| 968 | js_receiver_t, | |
| 969 | js_typedarray_span_t<> h, | |
| 970 | js_typedarray_span_t<> in, | |
| 971 | js_typedarray_span_t<> k | |
| 972 | ) { | |
| 973 | assert(h.size_bytes() == crypto_auth_BYTES); | |
| 974 | assert(k.size_bytes() == crypto_auth_KEYBYTES); | |
| 975 | ||
| 976 | return crypto_auth_verify(h.data(), in.data(), in.size_bytes(), k.data()) == 0; | |
| 977 | } | |
| 978 | ||
| 979 | static inline int | |
| 980 | sn_crypto_onetimeauth( | |
| 981 | js_env_t *, | |
| 982 | js_receiver_t, | |
| 983 | js_typedarray_span_t<> out, | |
| 984 | js_typedarray_span_t<> in, | |
| 985 | js_typedarray_span_t<> k | |
| 986 | ) { | |
| 987 | assert(out.size_bytes() == crypto_onetimeauth_BYTES); | |
| 988 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 989 | ||
| 990 | return crypto_onetimeauth(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 991 | } | |
| 992 | ||
| 993 | static inline int | |
| 994 | sn_crypto_onetimeauth_init( | |
| 995 | js_env_t *, | |
| 996 | js_receiver_t, | |
| 997 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 998 | js_typedarray_span_t<> k | |
| 999 | ) { | |
| 1000 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 1001 | ||
| 1002 | return crypto_onetimeauth_init(&*state, k.data()); | |
| 1003 | } | |
| 1004 | ||
| 1005 | static inline int | |
| 1006 | sn_crypto_onetimeauth_update( | |
| 1007 | js_env_t *, | |
| 1008 | js_receiver_t, | |
| 1009 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 1010 | js_typedarray_span_t<> in | |
| 1011 | ) { | |
| 1012 | return crypto_onetimeauth_update(&*state, in.data(), in.size_bytes()); | |
| 1013 | } | |
| 1014 | ||
| 1015 | static inline int | |
| 1016 | sn_crypto_onetimeauth_final( | |
| 1017 | js_env_t *, | |
| 1018 | js_receiver_t, | |
| 1019 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 1020 | js_typedarray_span_t<> out | |
| 1021 | ) { | |
| 1022 | assert(out.size_bytes() == crypto_onetimeauth_BYTES); | |
| 1023 | ||
| 1024 | return crypto_onetimeauth_final(&*state, out.data()); | |
| 1025 | } | |
| 1026 | ||
| 1027 | static inline bool | |
| 1028 | sn_crypto_onetimeauth_verify( | |
| 1029 | js_env_t *, | |
| 1030 | js_receiver_t, | |
| 1031 | js_typedarray_span_t<> h, | |
| 1032 | js_typedarray_span_t<> in, | |
| 1033 | js_typedarray_span_t<> k | |
| 1034 | ) { | |
| 1035 | assert(h.size_bytes() == crypto_onetimeauth_BYTES); | |
| 1036 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 1037 | ||
| 1038 | return crypto_onetimeauth_verify(h.data(), in.data(), in.size_bytes(), k.data()) == 0; | |
| 1039 | } | |
| 1040 | ||
| 1041 | // CHECK: memlimit can be >32bit | |
| 1042 | static inline int | |
| 1043 | sn_crypto_pwhash( | |
| 1044 | js_env_t *, | |
| 1045 | js_receiver_t, | |
| 1046 | js_typedarray_span_t<> out, | |
| 1047 | js_typedarray_span_of_t<char> passwd, | |
| 1048 | js_typedarray_span_t<> salt, | |
| 1049 | uint64_t opslimit, | |
| 1050 | uint64_t memlimit, | |
| 1051 | int32_t alg | |
| 1052 | ) { | |
| 1053 | assert(out.size_bytes() >= crypto_pwhash_BYTES_MIN); | |
| 1054 | assert(out.size_bytes() <= crypto_pwhash_BYTES_MAX); | |
| 1055 | assert(salt.size_bytes() == crypto_pwhash_SALTBYTES); | |
| 1056 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1057 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1058 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1059 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1060 | assert(alg == 1 || alg == 2); // Argon2i or Argon2id | |
| 1061 | ||
| 1062 | return crypto_pwhash( | |
| 1063 | out.data(), | |
| 1064 | out.size_bytes(), | |
| 1065 | passwd.data(), | |
| 1066 | passwd.size_bytes(), | |
| 1067 | salt.data(), | |
| 1068 | opslimit, | |
| 1069 | memlimit, | |
| 1070 | alg | |
| 1071 | ); | |
| 1072 | } | |
| 1073 | ||
| 1074 | static inline int | |
| 1075 | sn_crypto_pwhash_str( | |
| 1076 | js_env_t *, | |
| 1077 | js_receiver_t, | |
| 1078 | js_typedarray_span_of_t<char> out, | |
| 1079 | js_typedarray_span_of_t<char> passwd, | |
| 1080 | uint64_t opslimit, | |
| 1081 | uint64_t memlimit | |
| 1082 | ) { | |
| 1083 | assert(out.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1084 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1085 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1086 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1087 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1088 | ||
| 1089 | return crypto_pwhash_str( | |
| 1090 | out.data(), | |
| 1091 | passwd.data(), | |
| 1092 | passwd.size_bytes(), | |
| 1093 | opslimit, | |
| 1094 | memlimit | |
| 1095 | ); | |
| 1096 | } | |
| 1097 | ||
| 1098 | static inline bool | |
| 1099 | sn_crypto_pwhash_str_verify( | |
| 1100 | js_env_t *, | |
| 1101 | js_receiver_t, | |
| 1102 | js_typedarray_span_of_t<char> str, | |
| 1103 | js_typedarray_span_of_t<char> passwd | |
| 1104 | ) { | |
| 1105 | assert(str.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1106 | ||
| 1107 | return crypto_pwhash_str_verify(str.data(), passwd.data(), passwd.size_bytes()) == 0; | |
| 1108 | } | |
| 1109 | ||
| 1110 | // CHECK: returns 1, 0, -1 | |
| 1111 | static inline bool | |
| 1112 | sn_crypto_pwhash_str_needs_rehash( | |
| 1113 | js_env_t *, | |
| 1114 | js_receiver_t, | |
| 1115 | js_typedarray_span_of_t<char> str, | |
| 1116 | uint64_t opslimit, | |
| 1117 | uint64_t memlimit | |
| 1118 | ) { | |
| 1119 | assert(str.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1120 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1121 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1122 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1123 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1124 | ||
| 1125 | return crypto_pwhash_str_needs_rehash(str.data(), opslimit, memlimit) != 0; | |
| 1126 | } | |
| 1127 | ||
| 1128 | // CHECK: memlimit can be >32bit | |
| 1129 | static inline int | |
| 1130 | sn_crypto_pwhash_scryptsalsa208sha256( | |
| 1131 | js_env_t *, | |
| 1132 | js_receiver_t, | |
| 1133 | js_typedarray_span_t<> out, | |
| 1134 | js_typedarray_span_of_t<char> passwd, | |
| 1135 | js_typedarray_span_t<> salt, | |
| 1136 | uint64_t opslimit, | |
| 1137 | uint64_t memlimit | |
| 1138 | ) { | |
| 1139 | assert(out.size_bytes() >= crypto_pwhash_scryptsalsa208sha256_BYTES_MIN); | |
| 1140 | assert(out.size_bytes() <= crypto_pwhash_scryptsalsa208sha256_BYTES_MAX); | |
| 1141 | assert(salt.size_bytes() == crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 1142 | assert(opslimit >= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 1143 | assert(opslimit <= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 1144 | assert(memlimit >= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 1145 | assert(memlimit <= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 1146 | ||
| 1147 | return crypto_pwhash_scryptsalsa208sha256( | |
| 1148 | out.data(), | |
| 1149 | out.size_bytes(), | |
| 1150 | passwd.data(), | |
| 1151 | passwd.size_bytes(), | |
| 1152 | salt.data(), | |
| 1153 | opslimit, | |
| 1154 | memlimit | |
| 1155 | ); | |
| 1156 | } | |
| 1157 | ||
| 1158 | static inline int | |
| 1159 | sn_crypto_pwhash_scryptsalsa208sha256_str( | |
| 1160 | js_env_t *, | |
| 1161 | js_receiver_t, | |
| 1162 | js_typedarray_span_of_t<char> out, | |
| 1163 | js_typedarray_span_of_t<char> passwd, | |
| 1164 | uint64_t opslimit, | |
| 1165 | uint64_t memlimit | |
| 1166 | ) { | |
| 1167 | assert(out.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1168 | assert(opslimit >= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 1169 | assert(opslimit <= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 1170 | assert(memlimit >= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 1171 | assert(memlimit <= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 1172 | ||
| 1173 | return crypto_pwhash_scryptsalsa208sha256_str( | |
| 1174 | out.data(), | |
| 1175 | passwd.data(), | |
| 1176 | passwd.size_bytes(), | |
| 1177 | opslimit, | |
| 1178 | memlimit | |
| 1179 | ); | |
| 1180 | } | |
| 1181 | ||
| 1182 | static inline bool | |
| 1183 | sn_crypto_pwhash_scryptsalsa208sha256_str_verify( | |
| 1184 | js_env_t *, | |
| 1185 | js_receiver_t, | |
| 1186 | js_typedarray_span_of_t<char> str, | |
| 1187 | js_typedarray_span_of_t<char> passwd | |
| 1188 | ) { | |
| 1189 | assert(str.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1190 | ||
| 1191 | return crypto_pwhash_scryptsalsa208sha256_str_verify(str.data(), passwd.data(), passwd.size_bytes()) == 0; | |
| 1192 | } | |
| 1193 | ||
| 1194 | static inline bool | |
| 1195 | sn_crypto_pwhash_scryptsalsa208sha256_str_needs_rehash( | |
| 1196 | js_env_t *, | |
| 1197 | js_receiver_t, | |
| 1198 | js_typedarray_span_of_t<char> str, | |
| 1199 | uint64_t opslimit, | |
| 1200 | uint64_t memlimit | |
| 1201 | ) { | |
| 1202 | assert(str.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1203 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1204 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1205 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1206 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1207 | ||
| 1208 | return crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(str.data(), opslimit, memlimit) != 0; | |
| 1209 | } | |
| 1210 | ||
| 1211 | static inline int | |
| 1212 | sn_crypto_kx_keypair( | |
| 1213 | js_env_t *, | |
| 1214 | js_receiver_t, | |
| 1215 | js_typedarray_span_t<> pk, | |
| 1216 | js_typedarray_span_t<> sk | |
| 1217 | ) { | |
| 1218 | assert(pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1219 | assert(sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1220 | ||
| 1221 | return crypto_kx_keypair(pk.data(), sk.data()); | |
| 1222 | } | |
| 1223 | ||
| 1224 | static inline int | |
| 1225 | sn_crypto_kx_seed_keypair( | |
| 1226 | js_env_t *, | |
| 1227 | js_receiver_t, | |
| 1228 | js_typedarray_span_t<> pk, | |
| 1229 | js_typedarray_span_t<> sk, | |
| 1230 | js_typedarray_span_t<> seed | |
| 1231 | ) { | |
| 1232 | assert(pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1233 | assert(sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1234 | assert(seed.size_bytes() == crypto_kx_SEEDBYTES); | |
| 1235 | ||
| 1236 | return crypto_kx_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 1237 | } | |
| 1238 | ||
| 1239 | static inline int | |
| 1240 | sn_crypto_kx_client_session_keys( | |
| 1241 | js_env_t *, | |
| 1242 | js_receiver_t, | |
| 1243 | std::optional<js_typedarray_span_t<>> rx, | |
| 1244 | std::optional<js_typedarray_span_t<>> tx, | |
| 1245 | js_typedarray_span_t<> client_pk, | |
| 1246 | js_typedarray_span_t<> client_sk, | |
| 1247 | js_typedarray_span_t<> server_pk | |
| 1248 | ) { | |
| 1249 | assert(rx.has_value() || tx.has_value()); | |
| 1250 | ||
| 1251 | if (rx) assert(rx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1252 | if (tx) assert(tx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1253 | ||
| 1254 | assert(client_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1255 | assert(client_sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1256 | assert(server_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1257 | ||
| 1258 | return crypto_kx_client_session_keys( | |
| 1259 | rx ? rx->data() : nullptr, | |
| 1260 | tx ? tx->data() : nullptr, | |
| 1261 | client_pk.data(), | |
| 1262 | client_sk.data(), | |
| 1263 | server_pk.data() | |
| 1264 | ); | |
| 1265 | } | |
| 1266 | ||
| 1267 | static inline int | |
| 1268 | sn_crypto_kx_server_session_keys( | |
| 1269 | js_env_t *, | |
| 1270 | js_receiver_t, | |
| 1271 | std::optional<js_typedarray_span_t<>> rx, | |
| 1272 | std::optional<js_typedarray_span_t<>> tx, | |
| 1273 | js_typedarray_span_t<> server_pk, | |
| 1274 | js_typedarray_span_t<> server_sk, | |
| 1275 | js_typedarray_span_t<> client_pk | |
| 1276 | ) { | |
| 1277 | assert(rx.has_value() || tx.has_value()); | |
| 1278 | ||
| 1279 | if (rx) assert(rx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1280 | if (tx) assert(tx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1281 | ||
| 1282 | assert(server_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1283 | assert(server_sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1284 | assert(client_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1285 | ||
| 1286 | return crypto_kx_server_session_keys( | |
| 1287 | rx ? rx->data() : nullptr, | |
| 1288 | tx ? tx->data() : nullptr, | |
| 1289 | server_pk.data(), | |
| 1290 | server_sk.data(), | |
| 1291 | client_pk.data() | |
| 1292 | ); | |
| 1293 | } | |
| 1294 | ||
| 1295 | static inline int | |
| 1296 | sn_crypto_scalarmult_base( | |
| 1297 | js_env_t *, | |
| 1298 | js_receiver_t, | |
| 1299 | js_typedarray_span_t<> q, | |
| 1300 | js_typedarray_span_t<> n | |
| 1301 | ) { | |
| 1302 | assert(q.size_bytes() == crypto_scalarmult_BYTES); | |
| 1303 | assert(n.size_bytes() == crypto_scalarmult_SCALARBYTES); | |
| 1304 | ||
| 1305 | return crypto_scalarmult_base(q.data(), n.data()); | |
| 1306 | } | |
| 1307 | ||
| 1308 | static inline int | |
| 1309 | sn_crypto_scalarmult( | |
| 1310 | js_env_t *, | |
| 1311 | js_receiver_t, | |
| 1312 | js_typedarray_span_t<> q, | |
| 1313 | js_typedarray_span_t<> n, | |
| 1314 | js_typedarray_span_t<> p | |
| 1315 | ) { | |
| 1316 | assert(q.size_bytes() == crypto_scalarmult_BYTES); | |
| 1317 | assert(n.size_bytes() == crypto_scalarmult_SCALARBYTES); | |
| 1318 | assert(p.size_bytes() == crypto_scalarmult_BYTES); | |
| 1319 | ||
| 1320 | return crypto_scalarmult(q.data(), n.data(), p.data()); | |
| 1321 | } | |
| 1322 | ||
| 1323 | static inline int | |
| 1324 | sn_crypto_scalarmult_ed25519_base( | |
| 1325 | js_env_t *, | |
| 1326 | js_receiver_t, | |
| 1327 | js_typedarray_span_t<> q, | |
| 1328 | js_typedarray_span_t<> n | |
| 1329 | ) { | |
| 1330 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1331 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1332 | ||
| 1333 | return crypto_scalarmult_ed25519_base(q.data(), n.data()); | |
| 1334 | } | |
| 1335 | ||
| 1336 | static inline int | |
| 1337 | sn_crypto_scalarmult_ed25519( | |
| 1338 | js_env_t *, | |
| 1339 | js_receiver_t, | |
| 1340 | js_typedarray_span_t<> q, | |
| 1341 | js_typedarray_span_t<> n, | |
| 1342 | js_typedarray_span_t<> p | |
| 1343 | ) { | |
| 1344 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1345 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1346 | assert(p.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1347 | ||
| 1348 | return crypto_scalarmult_ed25519(q.data(), n.data(), p.data()); | |
| 1349 | } | |
| 1350 | ||
| 1351 | static inline bool | |
| 1352 | sn_crypto_core_ed25519_is_valid_point( | |
| 1353 | js_env_t *, | |
| 1354 | js_receiver_t, | |
| 1355 | js_typedarray_span_t<> p | |
| 1356 | ) { | |
| 1357 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1358 | ||
| 1359 | return crypto_core_ed25519_is_valid_point(p.data()) != 0; | |
| 1360 | } | |
| 1361 | ||
| 1362 | static inline int | |
| 1363 | sn_crypto_core_ed25519_from_uniform( | |
| 1364 | js_env_t *, | |
| 1365 | js_receiver_t, | |
| 1366 | js_typedarray_span_t<> p, | |
| 1367 | js_typedarray_span_t<> r | |
| 1368 | ) { | |
| 1369 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1370 | assert(r.size_bytes() == crypto_core_ed25519_UNIFORMBYTES); | |
| 1371 | ||
| 1372 | return crypto_core_ed25519_from_uniform(p.data(), r.data()); | |
| 1373 | } | |
| 1374 | ||
| 1375 | static inline int | |
| 1376 | sn_crypto_scalarmult_ed25519_base_noclamp( | |
| 1377 | js_env_t *, | |
| 1378 | js_receiver_t, | |
| 1379 | js_typedarray_span_t<> q, | |
| 1380 | js_typedarray_span_t<> n | |
| 1381 | ) { | |
| 1382 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1383 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1384 | ||
| 1385 | return crypto_scalarmult_ed25519_base_noclamp(q.data(), n.data()); | |
| 1386 | } | |
| 1387 | ||
| 1388 | static inline int | |
| 1389 | sn_crypto_scalarmult_ed25519_noclamp( | |
| 1390 | js_env_t *, | |
| 1391 | js_receiver_t, | |
| 1392 | js_typedarray_span_t<> q, | |
| 1393 | js_typedarray_span_t<> n, | |
| 1394 | js_typedarray_span_t<> p | |
| 1395 | ) { | |
| 1396 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1397 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1398 | assert(p.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1399 | ||
| 1400 | return crypto_scalarmult_ed25519_noclamp(q.data(), n.data(), p.data()); | |
| 1401 | } | |
| 1402 | ||
| 1403 | static inline int | |
| 1404 | sn_crypto_core_ed25519_add( | |
| 1405 | js_env_t *, | |
| 1406 | js_receiver_t, | |
| 1407 | js_typedarray_span_t<> r, | |
| 1408 | js_typedarray_span_t<> p, | |
| 1409 | js_typedarray_span_t<> q | |
| 1410 | ) { | |
| 1411 | assert(r.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1412 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1413 | assert(q.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1414 | ||
| 1415 | return crypto_core_ed25519_add(r.data(), p.data(), q.data()); | |
| 1416 | } | |
| 1417 | ||
| 1418 | static inline int | |
| 1419 | sn_crypto_core_ed25519_sub( | |
| 1420 | js_env_t *, | |
| 1421 | js_receiver_t, | |
| 1422 | js_typedarray_span_t<> r, | |
| 1423 | js_typedarray_span_t<> p, | |
| 1424 | js_typedarray_span_t<> q | |
| 1425 | ) { | |
| 1426 | assert(r.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1427 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1428 | assert(q.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1429 | ||
| 1430 | return crypto_core_ed25519_sub(r.data(), p.data(), q.data()); | |
| 1431 | } | |
| 1432 | ||
| 1433 | static inline void | |
| 1434 | sn_crypto_core_ed25519_scalar_random( | |
| 1435 | js_env_t *, | |
| 1436 | js_receiver_t, | |
| 1437 | js_typedarray_span_t<> r | |
| 1438 | ) { | |
| 1439 | assert(r.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1440 | ||
| 1441 | crypto_core_ed25519_scalar_random(r.data()); | |
| 1442 | } | |
| 1443 | ||
| 1444 | static inline void | |
| 1445 | sn_crypto_core_ed25519_scalar_reduce( | |
| 1446 | js_env_t *, | |
| 1447 | js_receiver_t, | |
| 1448 | js_typedarray_span_t<> r, | |
| 1449 | js_typedarray_span_t<> s | |
| 1450 | ) { | |
| 1451 | assert(r.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1452 | assert(s.size_bytes() == crypto_core_ed25519_NONREDUCEDSCALARBYTES); | |
| 1453 | ||
| 1454 | crypto_core_ed25519_scalar_reduce(r.data(), s.data()); | |
| 1455 | } | |
| 1456 | ||
| 1457 | static inline void | |
| 1458 | sn_crypto_core_ed25519_scalar_invert( | |
| 1459 | js_env_t *, | |
| 1460 | js_receiver_t, | |
| 1461 | js_typedarray_span_t<> recip, | |
| 1462 | js_typedarray_span_t<> s | |
| 1463 | ) { | |
| 1464 | assert(recip.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1465 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1466 | ||
| 1467 | crypto_core_ed25519_scalar_invert(recip.data(), s.data()); | |
| 1468 | } | |
| 1469 | ||
| 1470 | static inline void | |
| 1471 | sn_crypto_core_ed25519_scalar_negate( | |
| 1472 | js_env_t *, | |
| 1473 | js_receiver_t, | |
| 1474 | js_typedarray_span_t<> neg, | |
| 1475 | js_typedarray_span_t<> s | |
| 1476 | ) { | |
| 1477 | assert(neg.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1478 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1479 | ||
| 1480 | crypto_core_ed25519_scalar_negate(neg.data(), s.data()); | |
| 1481 | } | |
| 1482 | ||
| 1483 | static inline void | |
| 1484 | sn_crypto_core_ed25519_scalar_complement( | |
| 1485 | js_env_t *, | |
| 1486 | js_receiver_t, | |
| 1487 | js_typedarray_span_t<> comp, | |
| 1488 | js_typedarray_span_t<> s | |
| 1489 | ) { | |
| 1490 | assert(comp.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1491 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1492 | ||
| 1493 | crypto_core_ed25519_scalar_complement(comp.data(), s.data()); | |
| 1494 | } | |
| 1495 | ||
| 1496 | static inline void | |
| 1497 | sn_crypto_core_ed25519_scalar_add( | |
| 1498 | js_env_t *, | |
| 1499 | js_receiver_t, | |
| 1500 | js_typedarray_span_t<> z, | |
| 1501 | js_typedarray_span_t<> x, | |
| 1502 | js_typedarray_span_t<> y | |
| 1503 | ) { | |
| 1504 | assert(z.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1505 | assert(x.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1506 | assert(y.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1507 | ||
| 1508 | crypto_core_ed25519_scalar_add(z.data(), x.data(), y.data()); | |
| 1509 | } | |
| 1510 | ||
| 1511 | static inline void | |
| 1512 | sn_crypto_core_ed25519_scalar_sub( | |
| 1513 | js_env_t *, | |
| 1514 | js_receiver_t, | |
| 1515 | js_typedarray_span_t<> z, | |
| 1516 | js_typedarray_span_t<> x, | |
| 1517 | js_typedarray_span_t<> y | |
| 1518 | ) { | |
| 1519 | assert(z.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1520 | assert(x.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1521 | assert(y.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1522 | ||
| 1523 | crypto_core_ed25519_scalar_sub(z.data(), x.data(), y.data()); | |
| 1524 | } | |
| 1525 | ||
| 1526 | static inline int | |
| 1527 | sn_crypto_shorthash( | |
| 1528 | js_env_t *, | |
| 1529 | js_receiver_t, | |
| 1530 | js_typedarray_span_t<> out, | |
| 1531 | js_typedarray_span_t<> in, | |
| 1532 | js_typedarray_span_t<> k | |
| 1533 | ) { | |
| 1534 | assert(out.size_bytes() == crypto_shorthash_BYTES); | |
| 1535 | assert(k.size_bytes() == crypto_shorthash_KEYBYTES); | |
| 1536 | ||
| 1537 | return crypto_shorthash(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 1538 | } | |
| 1539 | ||
| 1540 | static inline void | |
| 1541 | sn_crypto_kdf_keygen( | |
| 1542 | js_env_t *, | |
| 1543 | js_receiver_t, | |
| 1544 | js_typedarray_span_t<> key | |
| 1545 | ) { | |
| 1546 | assert(key.size_bytes() == crypto_kdf_KEYBYTES); | |
| 1547 | ||
| 1548 | crypto_kdf_keygen(key.data()); | |
| 1549 | } | |
| 1550 | ||
| 1551 | static inline int | |
| 1552 | sn_crypto_kdf_derive_from_key( | |
| 1553 | js_env_t *, | |
| 1554 | js_receiver_t, | |
| 1555 | js_typedarray_span_t<> subkey, | |
| 1556 | uint64_t subkey_id, | |
| 1557 | js_typedarray_span_of_t<char> ctx, | |
| 1558 | js_typedarray_span_t<> key | |
| 1559 | ) { | |
| 1560 | assert(subkey.size_bytes() >= crypto_kdf_BYTES_MIN); | |
| 1561 | assert(subkey.size_bytes() <= crypto_kdf_BYTES_MAX); | |
| 1562 | assert(ctx.size_bytes() == crypto_kdf_CONTEXTBYTES); | |
| 1563 | assert(key.size_bytes() == crypto_kdf_KEYBYTES); | |
| 1564 | ||
| 1565 | return crypto_kdf_derive_from_key( | |
| 1566 | subkey.data(), | |
| 1567 | subkey.size_bytes(), | |
| 1568 | subkey_id, | |
| 1569 | ctx.data(), | |
| 1570 | key.data() | |
| 1571 | ); | |
| 1572 | } | |
| 1573 | ||
| 1574 | static inline int | |
| 1575 | sn_crypto_hash( | |
| 1576 | js_env_t *, | |
| 1577 | js_receiver_t, | |
| 1578 | js_typedarray_span_t<> out, | |
| 1579 | js_typedarray_span_t<> in | |
| 1580 | ) { | |
| 1581 | assert(out.size_bytes() == crypto_hash_BYTES); | |
| 1582 | ||
| 1583 | return crypto_hash(out.data(), in.data(), in.size_bytes()); | |
| 1584 | } | |
| 1585 | ||
| 1586 | static inline int | |
| 1587 | sn_crypto_hash_sha256( | |
| 1588 | js_env_t *, | |
| 1589 | js_receiver_t, | |
| 1590 | js_typedarray_span_t<> out, | |
| 1591 | js_typedarray_span_t<> in | |
| 1592 | ) { | |
| 1593 | assert(out.size_bytes() == crypto_hash_sha256_BYTES); | |
| 1594 | ||
| 1595 | return crypto_hash_sha256(out.data(), in.data(), in.size_bytes()); | |
| 1596 | } | |
| 1597 | ||
| 1598 | static inline int | |
| 1599 | sn_crypto_hash_sha256_init( | |
| 1600 | js_env_t *, | |
| 1601 | js_receiver_t, | |
| 1602 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state | |
| 1603 | ) { | |
| 1604 | return crypto_hash_sha256_init(&*state); | |
| 1605 | } | |
| 1606 | ||
| 1607 | static inline int | |
| 1608 | sn_crypto_hash_sha256_update( | |
| 1609 | js_env_t *, | |
| 1610 | js_receiver_t, | |
| 1611 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state, | |
| 1612 | js_typedarray_span_t<> in | |
| 1613 | ) { | |
| 1614 | return crypto_hash_sha256_update(&*state, in.data(), in.size_bytes()); | |
| 1615 | } | |
| 1616 | ||
| 1617 | static inline int | |
| 1618 | sn_crypto_hash_sha256_final( | |
| 1619 | js_env_t *, | |
| 1620 | js_receiver_t, | |
| 1621 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state, | |
| 1622 | js_typedarray_span_t<> out | |
| 1623 | ) { | |
| 1624 | assert(out.size_bytes() == crypto_hash_sha256_BYTES); | |
| 1625 | ||
| 1626 | return crypto_hash_sha256_final(&*state, out.data()); | |
| 1627 | } | |
| 1628 | ||
| 1629 | static inline int | |
| 1630 | sn_crypto_hash_sha512( | |
| 1631 | js_env_t *, | |
| 1632 | js_receiver_t, | |
| 1633 | js_typedarray_span_t<> out, | |
| 1634 | js_typedarray_span_t<> in | |
| 1635 | ) { | |
| 1636 | assert(out.size_bytes() == crypto_hash_sha512_BYTES); | |
| 1637 | ||
| 1638 | return crypto_hash_sha512(out.data(), in.data(), in.size_bytes()); | |
| 1639 | } | |
| 1640 | ||
| 1641 | static inline int | |
| 1642 | sn_crypto_hash_sha512_init( | |
| 1643 | js_env_t *, | |
| 1644 | js_receiver_t, | |
| 1645 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state | |
| 1646 | ) { | |
| 1647 | return crypto_hash_sha512_init(&*state); | |
| 1648 | } | |
| 1649 | ||
| 1650 | static inline int | |
| 1651 | sn_crypto_hash_sha512_update( | |
| 1652 | js_env_t *, | |
| 1653 | js_receiver_t, | |
| 1654 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state, | |
| 1655 | js_typedarray_span_t<> in | |
| 1656 | ) { | |
| 1657 | return crypto_hash_sha512_update(&*state, in.data(), in.size_bytes()); | |
| 1658 | } | |
| 1659 | ||
| 1660 | static inline int | |
| 1661 | sn_crypto_hash_sha512_final( | |
| 1662 | js_env_t *, | |
| 1663 | js_receiver_t, | |
| 1664 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state, | |
| 1665 | js_typedarray_span_t<> out | |
| 1666 | ) { | |
| 1667 | assert(out.size_bytes() == crypto_hash_sha512_BYTES); | |
| 1668 | ||
| 1669 | return crypto_hash_sha512_final(&*state, out.data()); | |
| 1670 | } | |
| 1671 | ||
| 1672 | static inline void | |
| 1673 | sn_crypto_aead_xchacha20poly1305_ietf_keygen( | |
| 1674 | js_env_t *, | |
| 1675 | js_receiver_t, | |
| 1676 | js_typedarray_span_t<> k | |
| 1677 | ) { | |
| 1678 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1679 | ||
| 1680 | crypto_aead_xchacha20poly1305_ietf_keygen(k.data()); | |
| 1681 | } | |
| 1682 | ||
| 1683 | static inline uint64_t | |
| 1684 | sn_crypto_aead_xchacha20poly1305_ietf_encrypt( | |
| 1685 | js_env_t *, | |
| 1686 | js_receiver_t, | |
| 1687 | js_typedarray_span_t<> c, | |
| 1688 | js_typedarray_span_t<> m, | |
| 1689 | std::optional<js_typedarray_span_t<>> ad, | |
| 1690 | js_typedarray_span_t<> npub, | |
| 1691 | js_typedarray_span_t<> k | |
| 1692 | ) { | |
| 1693 | assert(c.size_bytes() == m.size_bytes() + crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1694 | assert(c.size_bytes() <= 0xffffffff); | |
| 1695 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1696 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1697 | ||
| 1698 | uint8_t *ad_data = nullptr; | |
| 1699 | size_t ad_size = 0; | |
| 1700 | if (ad) { | |
| 1701 | ad_data = ad->data(); | |
| 1702 | ad_size = ad->size_bytes(); | |
| 1703 | } | |
| 1704 | ||
| 1705 | unsigned long long clen = 0; | |
| 1706 | int status = crypto_aead_xchacha20poly1305_ietf_encrypt( | |
| 1707 | c.data(), | |
| 1708 | &clen, | |
| 1709 | m.data(), | |
| 1710 | m.size_bytes(), | |
| 1711 | ad_data, | |
| 1712 | ad_size, | |
| 1713 | nullptr, | |
| 1714 | npub.data(), | |
| 1715 | k.data() | |
| 1716 | ); | |
| 1717 | ||
| 1718 | if (status < 0) return status; | |
| 1719 | ||
| 1720 | return clen; | |
| 1721 | } | |
| 1722 | ||
| 1723 | static inline uint64_t | |
| 1724 | sn_crypto_aead_xchacha20poly1305_ietf_decrypt( | |
| 1725 | js_env_t *, | |
| 1726 | js_receiver_t, | |
| 1727 | js_typedarray_span_t<> m, | |
| 1728 | js_typedarray_span_t<> c, | |
| 1729 | std::optional<js_typedarray_span_t<>> ad, | |
| 1730 | js_typedarray_span_t<> npub, | |
| 1731 | js_typedarray_span_t<> k | |
| 1732 | ) { | |
| 1733 | assert(m.size_bytes() == c.size_bytes() - crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1734 | assert(m.size_bytes() <= 0xffffffff); | |
| 1735 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1736 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1737 | ||
| 1738 | uint8_t *ad_data = nullptr; | |
| 1739 | size_t ad_size = 0; | |
| 1740 | if (ad) { | |
| 1741 | ad_data = ad->data(); | |
| 1742 | ad_size = ad->size_bytes(); | |
| 1743 | } | |
| 1744 | ||
| 1745 | unsigned long long mlen = 0; | |
| 1746 | int status = crypto_aead_xchacha20poly1305_ietf_decrypt( | |
| 1747 | m.data(), | |
| 1748 | &mlen, | |
| 1749 | nullptr, | |
| 1750 | c.data(), | |
| 1751 | c.size_bytes(), | |
| 1752 | ad_data, | |
| 1753 | ad_size, | |
| 1754 | npub.data(), | |
| 1755 | k.data() | |
| 1756 | ); | |
| 1757 | ||
| 1758 | if (status < 0) return status; | |
| 1759 | ||
| 1760 | return mlen; | |
| 1761 | } | |
| 1762 | ||
| 1763 | static inline uint64_t | |
| 1764 | sn_crypto_aead_xchacha20poly1305_ietf_encrypt_detached( | |
| 1765 | js_env_t *, | |
| 1766 | js_receiver_t, | |
| 1767 | js_typedarray_span_t<> c, | |
| 1768 | js_typedarray_span_t<> mac, | |
| 1769 | js_typedarray_span_t<> m, | |
| 1770 | std::optional<js_typedarray_span_t<>> ad, | |
| 1771 | js_typedarray_span_t<> npub, | |
| 1772 | js_typedarray_span_t<> k | |
| 1773 | ) { | |
| 1774 | assert(c.size_bytes() == m.size_bytes()); | |
| 1775 | assert(mac.size_bytes() == crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1776 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1777 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1778 | ||
| 1779 | uint8_t *ad_data = nullptr; | |
| 1780 | size_t ad_size = 0; | |
| 1781 | if (ad) { | |
| 1782 | ad_data = ad->data(); | |
| 1783 | ad_size = ad->size_bytes(); | |
| 1784 | } | |
| 1785 | ||
| 1786 | unsigned long long maclen = 0; | |
| 1787 | int status = crypto_aead_xchacha20poly1305_ietf_encrypt_detached( | |
| 1788 | c.data(), | |
| 1789 | mac.data(), | |
| 1790 | &maclen, | |
| 1791 | m.data(), | |
| 1792 | m.size_bytes(), | |
| 1793 | ad_data, | |
| 1794 | ad_size, | |
| 1795 | nullptr, | |
| 1796 | npub.data(), | |
| 1797 | k.data() | |
| 1798 | ); | |
| 1799 | ||
| 1800 | if (status < 0) return status; | |
| 1801 | ||
| 1802 | return maclen; | |
| 1803 | } | |
| 1804 | ||
| 1805 | static inline int | |
| 1806 | sn_crypto_aead_xchacha20poly1305_ietf_decrypt_detached( | |
| 1807 | js_env_t *, | |
| 1808 | js_receiver_t, | |
| 1809 | js_typedarray_span_t<> m, | |
| 1810 | js_typedarray_span_t<> c, | |
| 1811 | js_typedarray_span_t<> mac, | |
| 1812 | std::optional<js_typedarray_span_t<>> ad, | |
| 1813 | js_typedarray_span_t<> npub, | |
| 1814 | js_typedarray_span_t<> k | |
| 1815 | ) { | |
| 1816 | assert(m.size_bytes() == c.size_bytes()); | |
| 1817 | assert(mac.size_bytes() == crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1818 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1819 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1820 | ||
| 1821 | uint8_t *ad_data = nullptr; | |
| 1822 | size_t ad_size = 0; | |
| 1823 | if (ad) { | |
| 1824 | ad_data = ad->data(); | |
| 1825 | ad_size = ad->size_bytes(); | |
| 1826 | } | |
| 1827 | ||
| 1828 | return crypto_aead_xchacha20poly1305_ietf_decrypt_detached( | |
| 1829 | m.data(), | |
| 1830 | nullptr, | |
| 1831 | c.data(), | |
| 1832 | c.size_bytes(), | |
| 1833 | mac.data(), | |
| 1834 | ad_data, | |
| 1835 | ad_size, | |
| 1836 | npub.data(), | |
| 1837 | k.data() | |
| 1838 | ); | |
| 1839 | } | |
| 1840 | ||
| 1841 | static inline void | |
| 1842 | sn_crypto_aead_chacha20poly1305_ietf_keygen( | |
| 1843 | js_env_t *, | |
| 1844 | js_receiver_t, | |
| 1845 | js_typedarray_span_t<> k | |
| 1846 | ) { | |
| 1847 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1848 | ||
| 1849 | crypto_aead_chacha20poly1305_ietf_keygen(k.data()); | |
| 1850 | } | |
| 1851 | ||
| 1852 | static inline uint64_t | |
| 1853 | sn_crypto_aead_chacha20poly1305_ietf_encrypt( | |
| 1854 | js_env_t *, | |
| 1855 | js_receiver_t, | |
| 1856 | js_typedarray_span_t<> c, | |
| 1857 | js_typedarray_span_t<> m, | |
| 1858 | std::optional<js_typedarray_span_t<>> ad, | |
| 1859 | js_typedarray_span_t<> npub, | |
| 1860 | js_typedarray_span_t<> k | |
| 1861 | ) { | |
| 1862 | assert(c.size_bytes() == m.size_bytes() + crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1863 | assert(c.size_bytes() <= 0xffffffff); | |
| 1864 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1865 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1866 | ||
| 1867 | uint8_t *ad_data = nullptr; | |
| 1868 | size_t ad_size = 0; | |
| 1869 | if (ad) { | |
| 1870 | ad_data = ad->data(); | |
| 1871 | ad_size = ad->size_bytes(); | |
| 1872 | } | |
| 1873 | ||
| 1874 | unsigned long long clen = 0; | |
| 1875 | int status = crypto_aead_chacha20poly1305_ietf_encrypt( | |
| 1876 | c.data(), | |
| 1877 | &clen, | |
| 1878 | m.data(), | |
| 1879 | m.size_bytes(), | |
| 1880 | ad_data, | |
| 1881 | ad_size, | |
| 1882 | nullptr, | |
| 1883 | npub.data(), | |
| 1884 | k.data() | |
| 1885 | ); | |
| 1886 | ||
| 1887 | if (status < 0) return status; | |
| 1888 | ||
| 1889 | return clen; | |
| 1890 | } | |
| 1891 | ||
| 1892 | static inline uint64_t | |
| 1893 | sn_crypto_aead_chacha20poly1305_ietf_decrypt( | |
| 1894 | js_env_t *, | |
| 1895 | js_receiver_t, | |
| 1896 | js_typedarray_span_t<> m, | |
| 1897 | js_typedarray_span_t<> c, | |
| 1898 | std::optional<js_typedarray_span_t<>> ad, | |
| 1899 | js_typedarray_span_t<> npub, | |
| 1900 | js_typedarray_span_t<> k | |
| 1901 | ) { | |
| 1902 | assert(m.size_bytes() == c.size_bytes() - crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1903 | assert(m.size_bytes() <= 0xffffffff); | |
| 1904 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1905 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1906 | ||
| 1907 | uint8_t *ad_data = nullptr; | |
| 1908 | size_t ad_size = 0; | |
| 1909 | if (ad) { | |
| 1910 | ad_data = ad->data(); | |
| 1911 | ad_size = ad->size_bytes(); | |
| 1912 | } | |
| 1913 | ||
| 1914 | unsigned long long mlen = 0; | |
| 1915 | int status = crypto_aead_chacha20poly1305_ietf_decrypt( | |
| 1916 | m.data(), | |
| 1917 | &mlen, | |
| 1918 | nullptr, | |
| 1919 | c.data(), | |
| 1920 | c.size_bytes(), | |
| 1921 | ad_data, | |
| 1922 | ad_size, | |
| 1923 | npub.data(), | |
| 1924 | k.data() | |
| 1925 | ); | |
| 1926 | ||
| 1927 | if (status < 0) return status; | |
| 1928 | ||
| 1929 | return mlen; | |
| 1930 | } | |
| 1931 | ||
| 1932 | static inline uint64_t | |
| 1933 | sn_crypto_aead_chacha20poly1305_ietf_encrypt_detached( | |
| 1934 | js_env_t *, | |
| 1935 | js_receiver_t, | |
| 1936 | js_typedarray_span_t<> c, | |
| 1937 | js_typedarray_span_t<> mac, | |
| 1938 | js_typedarray_span_t<> m, | |
| 1939 | std::optional<js_typedarray_span_t<>> ad, | |
| 1940 | js_typedarray_span_t<> npub, | |
| 1941 | js_typedarray_span_t<> k | |
| 1942 | ) { | |
| 1943 | assert(c.size_bytes() == m.size_bytes()); | |
| 1944 | assert(mac.size_bytes() == crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1945 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1946 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1947 | ||
| 1948 | uint8_t *ad_data = nullptr; | |
| 1949 | size_t ad_size = 0; | |
| 1950 | if (ad) { | |
| 1951 | ad_data = ad->data(); | |
| 1952 | ad_size = ad->size_bytes(); | |
| 1953 | } | |
| 1954 | ||
| 1955 | unsigned long long maclen = 0; | |
| 1956 | int status = crypto_aead_chacha20poly1305_ietf_encrypt_detached( | |
| 1957 | c.data(), | |
| 1958 | mac.data(), | |
| 1959 | &maclen, | |
| 1960 | m.data(), | |
| 1961 | m.size_bytes(), | |
| 1962 | ad_data, | |
| 1963 | ad_size, | |
| 1964 | nullptr, | |
| 1965 | npub.data(), | |
| 1966 | k.data() | |
| 1967 | ); | |
| 1968 | ||
| 1969 | if (status < 0) return status; | |
| 1970 | ||
| 1971 | return maclen; | |
| 1972 | } | |
| 1973 | ||
| 1974 | static inline int | |
| 1975 | sn_crypto_aead_chacha20poly1305_ietf_decrypt_detached( | |
| 1976 | js_env_t *, | |
| 1977 | js_receiver_t, | |
| 1978 | js_typedarray_span_t<> m, | |
| 1979 | js_typedarray_span_t<> c, | |
| 1980 | js_typedarray_span_t<> mac, | |
| 1981 | std::optional<js_typedarray_span_t<>> ad, | |
| 1982 | js_typedarray_span_t<> npub, | |
| 1983 | js_typedarray_span_t<> k | |
| 1984 | ) { | |
| 1985 | assert(m.size_bytes() == c.size_bytes()); | |
| 1986 | assert(mac.size_bytes() == crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1987 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1988 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1989 | ||
| 1990 | uint8_t *ad_data = nullptr; | |
| 1991 | size_t ad_size = 0; | |
| 1992 | if (ad) { | |
| 1993 | ad_data = ad->data(); | |
| 1994 | ad_size = ad->size_bytes(); | |
| 1995 | } | |
| 1996 | ||
| 1997 | return crypto_aead_chacha20poly1305_ietf_decrypt_detached( | |
| 1998 | m.data(), | |
| 1999 | nullptr, | |
| 2000 | c.data(), | |
| 2001 | c.size_bytes(), | |
| 2002 | mac.data(), | |
| 2003 | ad_data, | |
| 2004 | ad_size, | |
| 2005 | npub.data(), | |
| 2006 | k.data() | |
| 2007 | ); | |
| 2008 | } | |
| 2009 | ||
| 2010 | static inline void | |
| 2011 | sn_crypto_secretstream_xchacha20poly1305_keygen( | |
| 2012 | js_env_t *env, | |
| 2013 | js_receiver_t, | |
| 2014 | ||
| 2015 | js_arraybuffer_span_t k, | |
| 2016 | uint32_t k_offset, | |
| 2017 | uint32_t k_len | |
| 2018 | ) { | |
| 2019 | assert_bounds(k); | |
| 2020 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2021 | ||
| 2022 | crypto_secretstream_xchacha20poly1305_keygen(&k[k_offset]); | |
| 2023 | } | |
| 2024 | ||
| 2025 | static inline int | |
| 2026 | sn_crypto_secretstream_xchacha20poly1305_init_push( | |
| 2027 | js_env_t *env, | |
| 2028 | js_receiver_t, | |
| 2029 | ||
| 2030 | js_arraybuffer_span_t state, | |
| 2031 | uint32_t state_offset, | |
| 2032 | uint32_t state_len, | |
| 2033 | ||
| 2034 | js_arraybuffer_span_t header, | |
| 2035 | uint32_t header_offset, | |
| 2036 | uint32_t header_len, | |
| 2037 | ||
| 2038 | js_arraybuffer_span_t k, | |
| 2039 | uint32_t k_offset, | |
| 2040 | uint32_t k_len | |
| 2041 | ) { | |
| 2042 | assert_bounds(state); | |
| 2043 | assert_bounds(header); | |
| 2044 | assert_bounds(k); | |
| 2045 | ||
| 2046 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2047 | assert(header_len == crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 2048 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2049 | ||
| 2050 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2051 | ||
| 2052 | return crypto_secretstream_xchacha20poly1305_init_push(state_data, &header[header_offset], &k[k_offset]); | |
| 2053 | } | |
| 2054 | ||
| 2055 | static inline uint64_t | |
| 2056 | sn_crypto_secretstream_xchacha20poly1305_push( | |
| 2057 | js_env_t *env, | |
| 2058 | js_receiver_t, | |
| 2059 | ||
| 2060 | js_arraybuffer_span_t state, | |
| 2061 | uint32_t state_offset, | |
| 2062 | uint32_t state_len, | |
| 2063 | ||
| 2064 | js_arraybuffer_span_t c, | |
| 2065 | uint32_t c_offset, | |
| 2066 | uint32_t c_len, | |
| 2067 | ||
| 2068 | js_arraybuffer_span_t m, | |
| 2069 | uint32_t m_offset, | |
| 2070 | uint32_t m_len, | |
| 2071 | ||
| 2072 | js_arraybuffer_span_t ad, | |
| 2073 | uint32_t ad_offset, | |
| 2074 | uint32_t ad_len, | |
| 2075 | ||
| 2076 | uint32_t tag | |
| 2077 | ) { | |
| 2078 | assert_bounds(state); | |
| 2079 | assert_bounds(c); | |
| 2080 | assert_bounds(m); | |
| 2081 | ||
| 2082 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2083 | assert(c_len == m_len + crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2084 | assert(c_len <= 0xffffffff && "32bit integer"); | |
| 2085 | ||
| 2086 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2087 | ||
| 2088 | uint8_t *ad_data = NULL; | |
| 2089 | if (ad_len) { | |
| 2090 | assert_bounds(ad); | |
| 2091 | ad_data = &ad[ad_offset]; | |
| 2092 | } | |
| 2093 | ||
| 2094 | unsigned long long clen = 0; | |
| 2095 | int status = crypto_secretstream_xchacha20poly1305_push( | |
| 2096 | state_data, | |
| 2097 | &c[c_offset], | |
| 2098 | &clen, | |
| 2099 | &m[m_offset], | |
| 2100 | m_len, | |
| 2101 | ad_data, | |
| 2102 | ad_len, | |
| 2103 | tag | |
| 2104 | ); | |
| 2105 | ||
| 2106 | if (status < 0) return status; | |
| 2107 | ||
| 2108 | return clen; | |
| 2109 | } | |
| 2110 | ||
| 2111 | static inline int | |
| 2112 | sn_crypto_secretstream_xchacha20poly1305_init_pull( | |
| 2113 | js_env_t *, | |
| 2114 | js_receiver_t, | |
| 2115 | ||
| 2116 | js_arraybuffer_span_t state, | |
| 2117 | uint32_t state_offset, | |
| 2118 | uint32_t state_len, | |
| 2119 | ||
| 2120 | js_arraybuffer_span_t header, | |
| 2121 | uint32_t header_offset, | |
| 2122 | uint32_t header_len, | |
| 2123 | ||
| 2124 | js_arraybuffer_span_t k, | |
| 2125 | uint32_t k_offset, | |
| 2126 | uint32_t k_len | |
| 2127 | ) { | |
| 2128 | assert_bounds(state); | |
| 2129 | assert_bounds(header); | |
| 2130 | assert_bounds(k); | |
| 2131 | ||
| 2132 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2133 | assert(header_len == crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 2134 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2135 | ||
| 2136 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2137 | ||
| 2138 | return crypto_secretstream_xchacha20poly1305_init_pull(state_data, &header[header_offset], &k[k_offset]); | |
| 2139 | } | |
| 2140 | ||
| 2141 | static inline uint64_t | |
| 2142 | sn_crypto_secretstream_xchacha20poly1305_pull( | |
| 2143 | js_env_t *env, | |
| 2144 | js_receiver_t, | |
| 2145 | ||
| 2146 | js_arraybuffer_span_t state, | |
| 2147 | uint32_t state_offset, | |
| 2148 | uint32_t state_len, | |
| 2149 | ||
| 2150 | js_arraybuffer_span_t m, | |
| 2151 | uint32_t m_offset, | |
| 2152 | uint32_t m_len, | |
| 2153 | ||
| 2154 | js_arraybuffer_span_t tag, | |
| 2155 | uint32_t tag_offset, | |
| 2156 | uint32_t tag_len, | |
| 2157 | ||
| 2158 | js_arraybuffer_span_t c, | |
| 2159 | uint32_t c_offset, | |
| 2160 | uint32_t c_len, | |
| 2161 | ||
| 2162 | js_arraybuffer_span_t ad, | |
| 2163 | uint32_t ad_offset, | |
| 2164 | uint32_t ad_len | |
| 2165 | ) { | |
| 2166 | assert_bounds(state); | |
| 2167 | assert_bounds(m); | |
| 2168 | assert_bounds(tag); | |
| 2169 | assert_bounds(c); | |
| 2170 | ||
| 2171 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2172 | assert(c_len >= crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2173 | assert(tag_len == 1); | |
| 2174 | assert(m_len == c_len - crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2175 | assert(m_len <= 0xffffffff); | |
| 2176 | ||
| 2177 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2178 | ||
| 2179 | uint8_t *ad_data = NULL; | |
| 2180 | if (ad_len) { | |
| 2181 | assert_bounds(ad); | |
| 2182 | ad_data = &ad[ad_offset]; | |
| 2183 | } | |
| 2184 | ||
| 2185 | unsigned long long mlen = 0; | |
| 2186 | int status = crypto_secretstream_xchacha20poly1305_pull( | |
| 2187 | state_data, | |
| 2188 | &m[m_offset], | |
| 2189 | &mlen, | |
| 2190 | &tag[tag_offset], | |
| 2191 | &c[c_offset], | |
| 2192 | c_len, | |
| 2193 | ad_data, | |
| 2194 | ad_len | |
| 2195 | ); | |
| 2196 | ||
| 2197 | if (status < 0) return status; | |
| 2198 | ||
| 2199 | return mlen; | |
| 2200 | } | |
| 2201 | ||
| 2202 | static inline void | |
| 2203 | sn_crypto_secretstream_xchacha20poly1305_rekey( | |
| 2204 | js_env_t *, | |
| 2205 | js_receiver_t, | |
| 2206 | ||
| 2207 | js_arraybuffer_span_t state, | |
| 2208 | uint32_t state_offset, | |
| 2209 | uint32_t state_len | |
| 2210 | ) { | |
| 2211 | assert_bounds(state); | |
| 2212 | ||
| 2213 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2214 | ||
| 2215 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2216 | ||
| 2217 | crypto_secretstream_xchacha20poly1305_rekey(state_data); | |
| 2218 | } | |
| 2219 | ||
| 2220 | struct sn_async_task { | |
| 2221 | uv_work_t task; | |
| 2222 | js_env_t *env; | |
| 2223 | js_persistent_t<js_function_t<void, int>> cb; | |
| 2224 | js_deferred_teardown_t *teardown; | |
| 2225 | int code = 0; | |
| 2226 | bool exiting = false; | |
| 2227 | ||
| 2228 | sn_async_task(js_env_t *env) : env(env) { | |
| 2229 | int err; | |
| 2230 | err = js_add_teardown_callback<on_teardown, sn_async_task>(env, this, teardown); | |
| 2231 | assert(err == 0); | |
| 2232 | } | |
| 2233 | ||
| 2234 | ~sn_async_task() { | |
| 2235 | int err; | |
| 2236 | err = js_finish_teardown_callback(teardown); | |
| 2237 | assert(err == 0); | |
| 2238 | } | |
| 2239 | ||
| 2240 | private: | |
| 2241 | static void | |
| 2242 | on_teardown(js_deferred_teardown_t *teardown, sn_async_task *req) { | |
| 2243 | req->exiting = true; | |
| 2244 | } | |
| 2245 | }; | |
| 2246 | ||
| 2247 | struct sn_async_pwhash_request : sn_async_task { | |
| 2248 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2249 | std::span<uint8_t> out; | |
| 2250 | ||
| 2251 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2252 | std::span<char> pwd; | |
| 2253 | ||
| 2254 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 2255 | std::span<uint8_t> salt; | |
| 2256 | ||
| 2257 | uint64_t opslimit; | |
| 2258 | size_t memlimit; | |
| 2259 | int alg; | |
| 2260 | ||
| 2261 | sn_async_pwhash_request(js_env_t *env) : sn_async_task(env) {} | |
| 2262 | }; | |
| 2263 | ||
| 2264 | static void | |
| 2265 | async_pwhash_execute(uv_work_t *uv_req) { | |
| 2266 | auto req = reinterpret_cast<sn_async_pwhash_request *>(uv_req); | |
| 2267 | ||
| 2268 | req->code = crypto_pwhash( | |
| 2269 | req->out.data(), | |
| 2270 | req->out.size(), | |
| 2271 | req->pwd.data(), | |
| 2272 | req->pwd.size(), | |
| 2273 | req->salt.data(), | |
| 2274 | req->opslimit, | |
| 2275 | req->memlimit, | |
| 2276 | req->alg | |
| 2277 | ); | |
| 2278 | } | |
| 2279 | ||
| 2280 | static void | |
| 2281 | async_pwhash_complete(uv_work_t *uv_req, int status) { | |
| 2282 | int err; | |
| 2283 | ||
| 2284 | auto req = reinterpret_cast<sn_async_pwhash_request *>(uv_req); | |
| 2285 | ||
| 2286 | if (req->exiting) return delete req; | |
| 2287 | ||
| 2288 | js_handle_scope_t *scope; | |
| 2289 | err = js_open_handle_scope(req->env, &scope); | |
| 2290 | assert(err == 0); | |
| 2291 | ||
| 2292 | js_function_t<void, int> callback; | |
| 2293 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2294 | assert(err == 0); | |
| 2295 | ||
| 2296 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2297 | assert(err != js_pending_exception); | |
| 2298 | ||
| 2299 | err = js_close_handle_scope(req->env, scope); | |
| 2300 | assert(err == 0); | |
| 2301 | ||
| 2302 | delete req; | |
| 2303 | } | |
| 2304 | ||
| 2305 | static inline void | |
| 2306 | sn_crypto_pwhash_async( | |
| 2307 | js_env_t *env, | |
| 2308 | js_receiver_t, | |
| 2309 | ||
| 2310 | js_arraybuffer_t out, | |
| 2311 | uint32_t out_offset, | |
| 2312 | uint32_t out_len, | |
| 2313 | ||
| 2314 | js_arraybuffer_t pwd, | |
| 2315 | uint32_t pwd_offset, | |
| 2316 | uint32_t pwd_len, | |
| 2317 | ||
| 2318 | js_arraybuffer_t salt, | |
| 2319 | uint32_t salt_offset, | |
| 2320 | uint32_t salt_len, | |
| 2321 | ||
| 2322 | uint64_t opslimit, | |
| 2323 | uint64_t memlimit, | |
| 2324 | uint32_t alg, | |
| 2325 | ||
| 2326 | js_function_t<void, int> callback | |
| 2327 | ) { | |
| 2328 | int err; | |
| 2329 | ||
| 2330 | auto *req = new sn_async_pwhash_request(env); | |
| 2331 | ||
| 2332 | std::span<uint8_t> out_view; | |
| 2333 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2334 | assert(err == 0); | |
| 2335 | assert(out_offset + out_len <= out_view.size()); | |
| 2336 | ||
| 2337 | req->out = {&out_view[out_offset], out_len}; | |
| 2338 | ||
| 2339 | std::span<char> pwd_view; | |
| 2340 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2341 | assert(err == 0); | |
| 2342 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2343 | ||
| 2344 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2345 | ||
| 2346 | std::span<uint8_t> salt_view; | |
| 2347 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 2348 | assert(err == 0); | |
| 2349 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 2350 | ||
| 2351 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 2352 | ||
| 2353 | req->opslimit = opslimit; | |
| 2354 | req->memlimit = memlimit; | |
| 2355 | req->alg = alg; | |
| 2356 | ||
| 2357 | err = js_create_reference(env, out, req->out_ref); | |
| 2358 | assert(err == 0); | |
| 2359 | ||
| 2360 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2361 | assert(err == 0); | |
| 2362 | ||
| 2363 | err = js_create_reference(env, salt, req->salt_ref); | |
| 2364 | assert(err == 0); | |
| 2365 | ||
| 2366 | err = js_create_reference(env, callback, req->cb); | |
| 2367 | assert(err == 0); | |
| 2368 | ||
| 2369 | uv_loop_t *loop; | |
| 2370 | err = js_get_env_loop(env, &loop); | |
| 2371 | assert(err == 0); | |
| 2372 | ||
| 2373 | err = uv_queue_work(loop, &req->task, async_pwhash_execute, async_pwhash_complete); | |
| 2374 | assert(err == 0); | |
| 2375 | } | |
| 2376 | ||
| 2377 | struct sn_async_pwhash_str_request : sn_async_task { | |
| 2378 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2379 | std::span<char> out; | |
| 2380 | ||
| 2381 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2382 | std::span<char> pwd; | |
| 2383 | ||
| 2384 | uint64_t opslimit; | |
| 2385 | size_t memlimit; | |
| 2386 | ||
| 2387 | sn_async_pwhash_str_request(js_env_t *env) : sn_async_task(env) {} | |
| 2388 | }; | |
| 2389 | ||
| 2390 | static void | |
| 2391 | async_pwhash_str_execute(uv_work_t *uv_req) { | |
| 2392 | auto *req = reinterpret_cast<sn_async_pwhash_str_request *>(uv_req); | |
| 2393 | ||
| 2394 | req->code = crypto_pwhash_str( | |
| 2395 | req->out.data(), | |
| 2396 | req->pwd.data(), | |
| 2397 | req->pwd.size(), | |
| 2398 | req->opslimit, | |
| 2399 | req->memlimit | |
| 2400 | ); | |
| 2401 | } | |
| 2402 | ||
| 2403 | static void | |
| 2404 | async_pwhash_str_complete(uv_work_t *uv_req, int status) { | |
| 2405 | int err; | |
| 2406 | ||
| 2407 | auto *req = reinterpret_cast<sn_async_pwhash_str_request *>(uv_req); | |
| 2408 | ||
| 2409 | if (req->exiting) return delete req; | |
| 2410 | ||
| 2411 | js_handle_scope_t *scope; | |
| 2412 | err = js_open_handle_scope(req->env, &scope); | |
| 2413 | assert(err == 0); | |
| 2414 | ||
| 2415 | js_function_t<void, int> callback; | |
| 2416 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2417 | assert(err == 0); | |
| 2418 | ||
| 2419 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2420 | assert(err != js_pending_exception); | |
| 2421 | ||
| 2422 | err = js_close_handle_scope(req->env, scope); | |
| 2423 | assert(err == 0); | |
| 2424 | ||
| 2425 | delete req; | |
| 2426 | } | |
| 2427 | ||
| 2428 | static inline void | |
| 2429 | sn_crypto_pwhash_str_async( | |
| 2430 | js_env_t *env, | |
| 2431 | js_receiver_t, | |
| 2432 | ||
| 2433 | js_arraybuffer_t out, | |
| 2434 | uint32_t out_offset, | |
| 2435 | uint32_t out_len, | |
| 2436 | ||
| 2437 | js_arraybuffer_t pwd, | |
| 2438 | uint32_t pwd_offset, | |
| 2439 | uint32_t pwd_len, | |
| 2440 | ||
| 2441 | uint64_t opslimit, | |
| 2442 | uint64_t memlimit, | |
| 2443 | js_function_t<void, int> callback | |
| 2444 | ) { | |
| 2445 | int err; | |
| 2446 | ||
| 2447 | auto *req = new sn_async_pwhash_str_request(env); | |
| 2448 | ||
| 2449 | std::span<char> out_view; | |
| 2450 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2451 | assert(err == 0); | |
| 2452 | assert(out_offset + out_len <= out_view.size()); | |
| 2453 | ||
| 2454 | req->out = {&out_view[out_offset], out_len}; | |
| 2455 | ||
| 2456 | std::span<char> pwd_view; | |
| 2457 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2458 | assert(err == 0); | |
| 2459 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2460 | ||
| 2461 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2462 | ||
| 2463 | req->opslimit = opslimit; | |
| 2464 | req->memlimit = memlimit; | |
| 2465 | ||
| 2466 | err = js_create_reference(env, out, req->out_ref); | |
| 2467 | assert(err == 0); | |
| 2468 | ||
| 2469 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2470 | assert(err == 0); | |
| 2471 | ||
| 2472 | err = js_create_reference(env, callback, req->cb); | |
| 2473 | assert(err == 0); | |
| 2474 | ||
| 2475 | uv_loop_t *loop; | |
| 2476 | err = js_get_env_loop(env, &loop); | |
| 2477 | assert(err == 0); | |
| 2478 | ||
| 2479 | err = uv_queue_work(loop, &req->task, async_pwhash_str_execute, async_pwhash_str_complete); | |
| 2480 | assert(err == 0); | |
| 2481 | } | |
| 2482 | ||
| 2483 | struct sn_async_pwhash_str_verify_request : sn_async_task { | |
| 2484 | js_persistent_t<js_arraybuffer_t> str_ref; | |
| 2485 | std::span<char> str; | |
| 2486 | ||
| 2487 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2488 | std::span<char> pwd; | |
| 2489 | ||
| 2490 | sn_async_pwhash_str_verify_request(js_env_t *env) : sn_async_task(env) {} | |
| 2491 | }; | |
| 2492 | ||
| 2493 | static void | |
| 2494 | async_pwhash_str_verify_execute(uv_work_t *uv_req) { | |
| 2495 | auto *req = reinterpret_cast<sn_async_pwhash_str_verify_request *>(uv_req); | |
| 2496 | ||
| 2497 | req->code = crypto_pwhash_str_verify(req->str.data(), req->pwd.data(), req->pwd.size()); | |
| 2498 | } | |
| 2499 | ||
| 2500 | static void | |
| 2501 | async_pwhash_str_verify_complete(uv_work_t *uv_req, int status) { | |
| 2502 | int err; | |
| 2503 | ||
| 2504 | auto *req = reinterpret_cast<sn_async_pwhash_str_verify_request *>(uv_req); | |
| 2505 | ||
| 2506 | if (req->exiting) return delete req; | |
| 2507 | ||
| 2508 | js_handle_scope_t *scope; | |
| 2509 | err = js_open_handle_scope(req->env, &scope); | |
| 2510 | assert(err == 0); | |
| 2511 | ||
| 2512 | js_function_t<void, int> callback; | |
| 2513 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2514 | assert(err == 0); | |
| 2515 | ||
| 2516 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2517 | assert(err != js_pending_exception); | |
| 2518 | ||
| 2519 | err = js_close_handle_scope(req->env, scope); | |
| 2520 | assert(err == 0); | |
| 2521 | ||
| 2522 | delete req; | |
| 2523 | } | |
| 2524 | ||
| 2525 | static inline void | |
| 2526 | sn_crypto_pwhash_str_verify_async( | |
| 2527 | js_env_t *env, | |
| 2528 | js_receiver_t, | |
| 2529 | ||
| 2530 | js_arraybuffer_t str, | |
| 2531 | uint32_t str_offset, | |
| 2532 | uint32_t str_len, | |
| 2533 | ||
| 2534 | js_arraybuffer_t pwd, | |
| 2535 | uint32_t pwd_offset, | |
| 2536 | uint32_t pwd_len, | |
| 2537 | ||
| 2538 | js_function_t<void, int> callback | |
| 2539 | ) { | |
| 2540 | int err; | |
| 2541 | ||
| 2542 | auto *req = new sn_async_pwhash_str_verify_request(env); | |
| 2543 | ||
| 2544 | std::span<char> str_view; | |
| 2545 | err = js_get_arraybuffer_info(env, str, str_view); | |
| 2546 | assert(err == 0); | |
| 2547 | assert(str_offset + str_len <= str_view.size()); | |
| 2548 | ||
| 2549 | req->str = {&str_view[str_offset], str_len}; | |
| 2550 | ||
| 2551 | std::span<char> pwd_view; | |
| 2552 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2553 | assert(err == 0); | |
| 2554 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2555 | ||
| 2556 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2557 | ||
| 2558 | err = js_create_reference(env, str, req->str_ref); | |
| 2559 | assert(err == 0); | |
| 2560 | ||
| 2561 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2562 | assert(err == 0); | |
| 2563 | ||
| 2564 | err = js_create_reference(env, callback, req->cb); | |
| 2565 | assert(err == 0); | |
| 2566 | ||
| 2567 | uv_loop_t *loop; | |
| 2568 | err = js_get_env_loop(env, &loop); | |
| 2569 | assert(err == 0); | |
| 2570 | ||
| 2571 | err = uv_queue_work(loop, &req->task, async_pwhash_str_verify_execute, async_pwhash_str_verify_complete); | |
| 2572 | assert(err == 0); | |
| 2573 | } | |
| 2574 | ||
| 2575 | struct sn_async_pwhash_scryptsalsa208sha256_request : sn_async_task { | |
| 2576 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2577 | std::span<uint8_t> out; | |
| 2578 | ||
| 2579 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2580 | std::span<char> pwd; | |
| 2581 | ||
| 2582 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 2583 | std::span<uint8_t> salt; | |
| 2584 | ||
| 2585 | uint64_t opslimit; | |
| 2586 | size_t memlimit; | |
| 2587 | ||
| 2588 | sn_async_pwhash_scryptsalsa208sha256_request(js_env_t *env) : sn_async_task(env) {} | |
| 2589 | }; | |
| 2590 | ||
| 2591 | static void | |
| 2592 | async_pwhash_scryptsalsa208sha256_execute(uv_work_t *uv_req) { | |
| 2593 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_request *>(uv_req); | |
| 2594 | ||
| 2595 | req->code = crypto_pwhash_scryptsalsa208sha256( | |
| 2596 | req->out.data(), | |
| 2597 | req->out.size(), | |
| 2598 | req->pwd.data(), | |
| 2599 | req->pwd.size(), | |
| 2600 | req->salt.data(), | |
| 2601 | req->opslimit, | |
| 2602 | req->memlimit | |
| 2603 | ); | |
| 2604 | } | |
| 2605 | ||
| 2606 | static void | |
| 2607 | async_pwhash_scryptsalsa208sha256_complete(uv_work_t *uv_req, int status) { | |
| 2608 | int err; | |
| 2609 | ||
| 2610 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_request *>(uv_req); | |
| 2611 | ||
| 2612 | if (req->exiting) return delete req; | |
| 2613 | ||
| 2614 | js_handle_scope_t *scope; | |
| 2615 | err = js_open_handle_scope(req->env, &scope); | |
| 2616 | assert(err == 0); | |
| 2617 | ||
| 2618 | js_function_t<void, int> callback; | |
| 2619 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2620 | assert(err == 0); | |
| 2621 | ||
| 2622 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2623 | assert(err != js_pending_exception); | |
| 2624 | ||
| 2625 | err = js_close_handle_scope(req->env, scope); | |
| 2626 | assert(err == 0); | |
| 2627 | ||
| 2628 | delete req; | |
| 2629 | } | |
| 2630 | ||
| 2631 | static inline void | |
| 2632 | sn_crypto_pwhash_scryptsalsa208sha256_async( | |
| 2633 | js_env_t *env, | |
| 2634 | js_receiver_t, | |
| 2635 | ||
| 2636 | js_arraybuffer_t out, | |
| 2637 | uint32_t out_offset, | |
| 2638 | uint32_t out_len, | |
| 2639 | ||
| 2640 | js_arraybuffer_t pwd, | |
| 2641 | uint32_t pwd_offset, | |
| 2642 | uint32_t pwd_len, | |
| 2643 | ||
| 2644 | js_arraybuffer_t salt, | |
| 2645 | uint32_t salt_offset, | |
| 2646 | uint32_t salt_len, | |
| 2647 | ||
| 2648 | uint64_t opslimit, | |
| 2649 | uint64_t memlimit, | |
| 2650 | ||
| 2651 | js_function_t<void, int> callback | |
| 2652 | ) { | |
| 2653 | int err; | |
| 2654 | ||
| 2655 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_request(env); | |
| 2656 | ||
| 2657 | std::span<uint8_t> out_view; | |
| 2658 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2659 | assert(err == 0); | |
| 2660 | assert(out_offset + out_len <= out_view.size()); | |
| 2661 | ||
| 2662 | req->out = {&out_view[out_offset], out_len}; | |
| 2663 | ||
| 2664 | std::span<char> pwd_view; | |
| 2665 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2666 | assert(err == 0); | |
| 2667 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2668 | ||
| 2669 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2670 | ||
| 2671 | std::span<uint8_t> salt_view; | |
| 2672 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 2673 | assert(err == 0); | |
| 2674 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 2675 | assert(salt_len == crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 2676 | ||
| 2677 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 2678 | ||
| 2679 | req->opslimit = opslimit; | |
| 2680 | req->memlimit = memlimit; | |
| 2681 | ||
| 2682 | err = js_create_reference(env, out, req->out_ref); | |
| 2683 | assert(err == 0); | |
| 2684 | ||
| 2685 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2686 | assert(err == 0); | |
| 2687 | ||
| 2688 | err = js_create_reference(env, salt, req->salt_ref); | |
| 2689 | assert(err == 0); | |
| 2690 | ||
| 2691 | err = js_create_reference(env, callback, req->cb); | |
| 2692 | assert(err == 0); | |
| 2693 | ||
| 2694 | uv_loop_t *loop; | |
| 2695 | err = js_get_env_loop(env, &loop); | |
| 2696 | assert(err == 0); | |
| 2697 | ||
| 2698 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_execute, async_pwhash_scryptsalsa208sha256_complete); | |
| 2699 | assert(err == 0); | |
| 2700 | } | |
| 2701 | ||
| 2702 | struct sn_async_pwhash_scryptsalsa208sha256_str_request : sn_async_task { | |
| 2703 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2704 | std::span<char> out; | |
| 2705 | ||
| 2706 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2707 | std::span<char> pwd; | |
| 2708 | ||
| 2709 | uint64_t opslimit; | |
| 2710 | size_t memlimit; | |
| 2711 | ||
| 2712 | sn_async_pwhash_scryptsalsa208sha256_str_request(js_env_t *env) : sn_async_task(env) {} | |
| 2713 | }; | |
| 2714 | ||
| 2715 | static void | |
| 2716 | async_pwhash_scryptsalsa208sha256_str_execute(uv_work_t *uv_req) { | |
| 2717 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_request *>(uv_req); | |
| 2718 | ||
| 2719 | req->code = crypto_pwhash_scryptsalsa208sha256_str( | |
| 2720 | req->out.data(), | |
| 2721 | req->pwd.data(), | |
| 2722 | req->pwd.size(), | |
| 2723 | req->opslimit, | |
| 2724 | req->memlimit | |
| 2725 | ); | |
| 2726 | } | |
| 2727 | ||
| 2728 | static void | |
| 2729 | async_pwhash_scryptsalsa208sha256_str_complete(uv_work_t *uv_req, int status) { | |
| 2730 | int err; | |
| 2731 | ||
| 2732 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_request *>(uv_req); | |
| 2733 | ||
| 2734 | if (req->exiting) return delete req; | |
| 2735 | ||
| 2736 | js_handle_scope_t *scope; | |
| 2737 | err = js_open_handle_scope(req->env, &scope); | |
| 2738 | assert(err == 0); | |
| 2739 | ||
| 2740 | js_function_t<void, int> callback; | |
| 2741 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2742 | assert(err == 0); | |
| 2743 | ||
| 2744 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2745 | assert(err != js_pending_exception); | |
| 2746 | ||
| 2747 | err = js_close_handle_scope(req->env, scope); | |
| 2748 | assert(err == 0); | |
| 2749 | ||
| 2750 | delete req; | |
| 2751 | } | |
| 2752 | ||
| 2753 | static inline void | |
| 2754 | sn_crypto_pwhash_scryptsalsa208sha256_str_async( | |
| 2755 | js_env_t *env, | |
| 2756 | js_receiver_t, | |
| 2757 | ||
| 2758 | js_arraybuffer_t out, | |
| 2759 | uint32_t out_offset, | |
| 2760 | uint32_t out_len, | |
| 2761 | ||
| 2762 | js_arraybuffer_t pwd, | |
| 2763 | uint32_t pwd_offset, | |
| 2764 | uint32_t pwd_len, | |
| 2765 | ||
| 2766 | uint64_t opslimit, | |
| 2767 | uint64_t memlimit, | |
| 2768 | ||
| 2769 | js_function_t<void, int> callback | |
| 2770 | ) { | |
| 2771 | int err; | |
| 2772 | ||
| 2773 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_str_request(env); | |
| 2774 | ||
| 2775 | std::span<char> out_view; | |
| 2776 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2777 | assert(err == 0); | |
| 2778 | assert(out_offset + out_len <= out_view.size()); | |
| 2779 | ||
| 2780 | req->out = {&out_view[out_offset], out_len}; | |
| 2781 | ||
| 2782 | std::span<char> pwd_view; | |
| 2783 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2784 | assert(err == 0); | |
| 2785 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2786 | ||
| 2787 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2788 | ||
| 2789 | req->opslimit = opslimit; | |
| 2790 | req->memlimit = memlimit; | |
| 2791 | ||
| 2792 | err = js_create_reference(env, out, req->out_ref); | |
| 2793 | assert(err == 0); | |
| 2794 | ||
| 2795 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2796 | assert(err == 0); | |
| 2797 | ||
| 2798 | err = js_create_reference(env, callback, req->cb); | |
| 2799 | assert(err == 0); | |
| 2800 | ||
| 2801 | uv_loop_t *loop; | |
| 2802 | err = js_get_env_loop(env, &loop); | |
| 2803 | assert(err == 0); | |
| 2804 | ||
| 2805 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_str_execute, async_pwhash_scryptsalsa208sha256_str_complete); | |
| 2806 | assert(err == 0); | |
| 2807 | } | |
| 2808 | ||
| 2809 | struct sn_async_pwhash_scryptsalsa208sha256_str_verify_request : sn_async_task { | |
| 2810 | js_persistent_t<js_arraybuffer_t> str_ref; | |
| 2811 | std::span<char> str; | |
| 2812 | ||
| 2813 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2814 | std::span<char> pwd; | |
| 2815 | ||
| 2816 | sn_async_pwhash_scryptsalsa208sha256_str_verify_request(js_env_t *env) : sn_async_task(env) {} | |
| 2817 | }; | |
| 2818 | ||
| 2819 | static void | |
| 2820 | async_pwhash_scryptsalsa208sha256_str_verify_execute(uv_work_t *uv_req) { | |
| 2821 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_verify_request *>(uv_req); | |
| 2822 | ||
| 2823 | req->code = crypto_pwhash_scryptsalsa208sha256_str_verify(req->str.data(), req->pwd.data(), req->pwd.size()); | |
| 2824 | } | |
| 2825 | ||
| 2826 | static void | |
| 2827 | async_pwhash_scryptsalsa208sha256_str_verify_complete(uv_work_t *uv_req, int status) { | |
| 2828 | int err; | |
| 2829 | ||
| 2830 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_verify_request *>(uv_req); | |
| 2831 | ||
| 2832 | if (req->exiting) return delete req; | |
| 2833 | ||
| 2834 | js_handle_scope_t *scope; | |
| 2835 | err = js_open_handle_scope(req->env, &scope); | |
| 2836 | assert(err == 0); | |
| 2837 | ||
| 2838 | js_function_t<void, int> callback; | |
| 2839 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2840 | assert(err == 0); | |
| 2841 | ||
| 2842 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2843 | assert(err != js_pending_exception); | |
| 2844 | ||
| 2845 | err = js_close_handle_scope(req->env, scope); | |
| 2846 | assert(err == 0); | |
| 2847 | ||
| 2848 | delete req; | |
| 2849 | } | |
| 2850 | ||
| 2851 | static inline void | |
| 2852 | sn_crypto_pwhash_scryptsalsa208sha256_str_verify_async( | |
| 2853 | js_env_t *env, | |
| 2854 | js_receiver_t, | |
| 2855 | ||
| 2856 | js_arraybuffer_t str, | |
| 2857 | uint32_t str_offset, | |
| 2858 | uint32_t str_len, | |
| 2859 | ||
| 2860 | js_arraybuffer_t pwd, | |
| 2861 | uint32_t pwd_offset, | |
| 2862 | uint32_t pwd_len, | |
| 2863 | ||
| 2864 | js_function_t<void, int> callback | |
| 2865 | ) { | |
| 2866 | int err; | |
| 2867 | ||
| 2868 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_str_verify_request(env); | |
| 2869 | ||
| 2870 | std::span<char> str_view; | |
| 2871 | err = js_get_arraybuffer_info(env, str, str_view); | |
| 2872 | assert(err == 0); | |
| 2873 | assert(str_offset + str_len <= str_view.size()); | |
| 2874 | ||
| 2875 | req->str = {&str_view[str_offset], str_len}; | |
| 2876 | ||
| 2877 | std::span<char> pwd_view; | |
| 2878 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2879 | assert(err == 0); | |
| 2880 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2881 | ||
| 2882 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2883 | ||
| 2884 | err = js_create_reference(env, str, req->str_ref); | |
| 2885 | assert(err == 0); | |
| 2886 | ||
| 2887 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2888 | assert(err == 0); | |
| 2889 | ||
| 2890 | err = js_create_reference(env, callback, req->cb); | |
| 2891 | assert(err == 0); | |
| 2892 | ||
| 2893 | uv_loop_t *loop; | |
| 2894 | err = js_get_env_loop(env, &loop); | |
| 2895 | assert(err == 0); | |
| 2896 | ||
| 2897 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_str_verify_execute, async_pwhash_scryptsalsa208sha256_str_verify_complete); | |
| 2898 | assert(err == 0); | |
| 2899 | } | |
| 2900 | ||
| 2901 | struct sn_crypto_stream_xor_state { | |
| 2902 | unsigned char n[crypto_stream_NONCEBYTES]; | |
| 2903 | unsigned char k[crypto_stream_KEYBYTES]; | |
| 2904 | unsigned char next_block[64]; | |
| 2905 | int remainder; | |
| 2906 | uint64_t block_counter; | |
| 2907 | }; | |
| 2908 | ||
| 2909 | static inline void | |
| 2910 | sn_crypto_stream_xor_wrap_init( | |
| 2911 | js_env_t *, | |
| 2912 | js_receiver_t, | |
| 2913 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state, | |
| 2914 | js_typedarray_span_t<> n, | |
| 2915 | js_typedarray_span_t<> k | |
| 2916 | ) { | |
| 2917 | assert(n.size_bytes() == crypto_stream_NONCEBYTES); | |
| 2918 | assert(k.size_bytes() == crypto_stream_KEYBYTES); | |
| 2919 | ||
| 2920 | state->remainder = 0; | |
| 2921 | state->block_counter = 0; | |
| 2922 | memcpy(state->n, n.data(), crypto_stream_NONCEBYTES); | |
| 2923 | memcpy(state->k, k.data(), crypto_stream_KEYBYTES); | |
| 2924 | } | |
| 2925 | ||
| 2926 | static inline void | |
| 2927 | sn_crypto_stream_xor_wrap_update( | |
| 2928 | js_env_t *, | |
| 2929 | js_receiver_t, | |
| 2930 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state, | |
| 2931 | js_typedarray_span_t<> c, | |
| 2932 | js_typedarray_span_t<> m | |
| 2933 | ) { | |
| 2934 | assert(c.size_bytes() == m.size_bytes()); | |
| 2935 | ||
| 2936 | auto next_block = state->next_block; | |
| 2937 | ||
| 2938 | size_t m_size = m.size_bytes(); | |
| 2939 | auto *c_ptr = c.data(); | |
| 2940 | auto *m_ptr = m.data(); | |
| 2941 | ||
| 2942 | if (state->remainder) { | |
| 2943 | uint64_t offset = 0; | |
| 2944 | int rem = state->remainder; | |
| 2945 | ||
| 2946 | while (rem < 64 && offset < m_size) { | |
| 2947 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 2948 | ++offset; | |
| 2949 | ++rem; | |
| 2950 | } | |
| 2951 | ||
| 2952 | c_ptr += offset; | |
| 2953 | m_ptr += offset; | |
| 2954 | m_size -= offset; | |
| 2955 | state->remainder = (rem == 64) ? 0 : rem; | |
| 2956 | ||
| 2957 | if (m_size == 0) return; | |
| 2958 | } | |
| 2959 | ||
| 2960 | state->remainder = m_size & 63; | |
| 2961 | size_t main_len = m_size - state->remainder; | |
| 2962 | ||
| 2963 | crypto_stream_xsalsa20_xor_ic(c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k); | |
| 2964 | state->block_counter += main_len / 64; | |
| 2965 | ||
| 2966 | if (state->remainder) { | |
| 2967 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 2968 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 2969 | ||
| 2970 | crypto_stream_xsalsa20_xor_ic( | |
| 2971 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 2972 | ); | |
| 2973 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 2974 | ||
| 2975 | state->block_counter++; | |
| 2976 | } | |
| 2977 | } | |
| 2978 | ||
| 2979 | static inline void | |
| 2980 | sn_crypto_stream_xor_wrap_final( | |
| 2981 | js_env_t *, | |
| 2982 | js_receiver_t, | |
| 2983 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state | |
| 2984 | ) { | |
| 2985 | sodium_memzero(state->n, sizeof(state->n)); | |
| 2986 | sodium_memzero(state->k, sizeof(state->k)); | |
| 2987 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 2988 | state->remainder = 0; | |
| 2989 | } | |
| 2990 | ||
| 2991 | struct sn_crypto_stream_chacha20_xor_state { | |
| 2992 | unsigned char n[crypto_stream_chacha20_NONCEBYTES]; | |
| 2993 | unsigned char k[crypto_stream_chacha20_KEYBYTES]; | |
| 2994 | unsigned char next_block[64]; | |
| 2995 | int remainder; | |
| 2996 | uint64_t block_counter; | |
| 2997 | }; | |
| 2998 | ||
| 2999 | static inline void | |
| 3000 | sn_crypto_stream_chacha20_xor_wrap_init( | |
| 3001 | js_env_t *, | |
| 3002 | js_receiver_t, | |
| 3003 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state, | |
| 3004 | js_typedarray_span_t<> n, | |
| 3005 | js_typedarray_span_t<> k | |
| 3006 | ) { | |
| 3007 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 3008 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 3009 | ||
| 3010 | state->remainder = 0; | |
| 3011 | state->block_counter = 0; | |
| 3012 | memcpy(state->n, n.data(), crypto_stream_chacha20_NONCEBYTES); | |
| 3013 | memcpy(state->k, k.data(), crypto_stream_chacha20_KEYBYTES); | |
| 3014 | } | |
| 3015 | ||
| 3016 | static inline void | |
| 3017 | sn_crypto_stream_chacha20_xor_wrap_update( | |
| 3018 | js_env_t *, | |
| 3019 | js_receiver_t, | |
| 3020 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state, | |
| 3021 | js_typedarray_span_t<> c, | |
| 3022 | js_typedarray_span_t<> m | |
| 3023 | ) { | |
| 3024 | assert(c.size_bytes() == m.size_bytes()); | |
| 3025 | ||
| 3026 | auto *next_block = state->next_block; | |
| 3027 | ||
| 3028 | size_t m_size = m.size_bytes(); | |
| 3029 | auto *c_ptr = c.data(); | |
| 3030 | auto *m_ptr = m.data(); | |
| 3031 | ||
| 3032 | if (state->remainder) { | |
| 3033 | uint64_t offset = 0; | |
| 3034 | int rem = state->remainder; | |
| 3035 | ||
| 3036 | while (rem < 64 && offset < m_size) { | |
| 3037 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3038 | offset++; | |
| 3039 | rem++; | |
| 3040 | } | |
| 3041 | ||
| 3042 | c_ptr += offset; | |
| 3043 | m_ptr += offset; | |
| 3044 | m_size -= offset; | |
| 3045 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3046 | ||
| 3047 | if (m_size == 0) return; | |
| 3048 | } | |
| 3049 | ||
| 3050 | state->remainder = m_size & 63; | |
| 3051 | size_t main_len = m_size - state->remainder; | |
| 3052 | ||
| 3053 | crypto_stream_chacha20_xor_ic( | |
| 3054 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3055 | ); | |
| 3056 | ||
| 3057 | state->block_counter += main_len / 64; | |
| 3058 | ||
| 3059 | if (state->remainder) { | |
| 3060 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3061 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3062 | ||
| 3063 | crypto_stream_chacha20_xor_ic( | |
| 3064 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3065 | ); | |
| 3066 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3067 | ||
| 3068 | state->block_counter++; | |
| 3069 | } | |
| 3070 | } | |
| 3071 | ||
| 3072 | static inline void | |
| 3073 | sn_crypto_stream_chacha20_xor_wrap_final( | |
| 3074 | js_env_t *, | |
| 3075 | js_receiver_t, | |
| 3076 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state | |
| 3077 | ) { | |
| 3078 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3079 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3080 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3081 | state->remainder = 0; | |
| 3082 | } | |
| 3083 | ||
| 3084 | struct sn_crypto_stream_chacha20_ietf_xor_state { | |
| 3085 | unsigned char n[crypto_stream_chacha20_ietf_NONCEBYTES]; | |
| 3086 | unsigned char k[crypto_stream_chacha20_ietf_KEYBYTES]; | |
| 3087 | unsigned char next_block[64]; | |
| 3088 | int remainder; | |
| 3089 | uint64_t block_counter; | |
| 3090 | }; | |
| 3091 | ||
| 3092 | static inline void | |
| 3093 | sn_crypto_stream_chacha20_ietf_xor_wrap_init( | |
| 3094 | js_env_t *, | |
| 3095 | js_receiver_t, | |
| 3096 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state, | |
| 3097 | js_typedarray_span_t<> n, | |
| 3098 | js_typedarray_span_t<> k | |
| 3099 | ) { | |
| 3100 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 3101 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 3102 | ||
| 3103 | state->remainder = 0; | |
| 3104 | state->block_counter = 0; | |
| 3105 | memcpy(state->n, n.data(), crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 3106 | memcpy(state->k, k.data(), crypto_stream_chacha20_ietf_KEYBYTES); | |
| 3107 | } | |
| 3108 | ||
| 3109 | static inline void | |
| 3110 | sn_crypto_stream_chacha20_ietf_xor_wrap_update( | |
| 3111 | js_env_t *, | |
| 3112 | js_receiver_t, | |
| 3113 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state, | |
| 3114 | js_typedarray_span_t<> c, | |
| 3115 | js_typedarray_span_t<> m | |
| 3116 | ) { | |
| 3117 | assert(c.size_bytes() == m.size_bytes()); | |
| 3118 | ||
| 3119 | auto *next_block = state->next_block; | |
| 3120 | ||
| 3121 | size_t m_size = m.size_bytes(); | |
| 3122 | auto *c_ptr = c.data(); | |
| 3123 | auto *m_ptr = m.data(); | |
| 3124 | ||
| 3125 | if (state->remainder) { | |
| 3126 | uint64_t offset = 0; | |
| 3127 | int rem = state->remainder; | |
| 3128 | ||
| 3129 | while (rem < 64 && offset < m_size) { | |
| 3130 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3131 | offset++; | |
| 3132 | rem++; | |
| 3133 | } | |
| 3134 | ||
| 3135 | c_ptr += offset; | |
| 3136 | m_ptr += offset; | |
| 3137 | m_size -= offset; | |
| 3138 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3139 | ||
| 3140 | if (m_size == 0) return; | |
| 3141 | } | |
| 3142 | ||
| 3143 | state->remainder = m_size & 63; | |
| 3144 | size_t main_len = m_size - state->remainder; | |
| 3145 | ||
| 3146 | crypto_stream_chacha20_ietf_xor_ic( | |
| 3147 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3148 | ); | |
| 3149 | ||
| 3150 | state->block_counter += main_len / 64; | |
| 3151 | ||
| 3152 | if (state->remainder) { | |
| 3153 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3154 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3155 | ||
| 3156 | crypto_stream_chacha20_ietf_xor_ic( | |
| 3157 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3158 | ); | |
| 3159 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3160 | ||
| 3161 | state->block_counter++; | |
| 3162 | } | |
| 3163 | } | |
| 3164 | ||
| 3165 | static inline void | |
| 3166 | sn_crypto_stream_chacha20_ietf_xor_wrap_final( | |
| 3167 | js_env_t *, | |
| 3168 | js_receiver_t, | |
| 3169 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state | |
| 3170 | ) { | |
| 3171 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3172 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3173 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3174 | state->remainder = 0; | |
| 3175 | } | |
| 3176 | ||
| 3177 | struct sn_crypto_stream_xchacha20_xor_state { | |
| 3178 | unsigned char n[crypto_stream_xchacha20_NONCEBYTES]; | |
| 3179 | unsigned char k[crypto_stream_xchacha20_KEYBYTES]; | |
| 3180 | unsigned char next_block[64]; | |
| 3181 | int remainder; | |
| 3182 | uint64_t block_counter; | |
| 3183 | }; | |
| 3184 | ||
| 3185 | static inline void | |
| 3186 | sn_crypto_stream_xchacha20_xor_wrap_init( | |
| 3187 | js_env_t *, | |
| 3188 | js_receiver_t, | |
| 3189 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state, | |
| 3190 | js_typedarray_span_t<> n, | |
| 3191 | js_typedarray_span_t<> k | |
| 3192 | ) { | |
| 3193 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 3194 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 3195 | ||
| 3196 | state->remainder = 0; | |
| 3197 | state->block_counter = 0; | |
| 3198 | memcpy(state->n, n.data(), crypto_stream_xchacha20_NONCEBYTES); | |
| 3199 | memcpy(state->k, k.data(), crypto_stream_xchacha20_KEYBYTES); | |
| 3200 | } | |
| 3201 | ||
| 3202 | static inline void | |
| 3203 | sn_crypto_stream_xchacha20_xor_wrap_update( | |
| 3204 | js_env_t *, | |
| 3205 | js_receiver_t, | |
| 3206 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state, | |
| 3207 | js_typedarray_span_t<> c, | |
| 3208 | js_typedarray_span_t<> m | |
| 3209 | ) { | |
| 3210 | assert(c.size_bytes() == m.size_bytes()); | |
| 3211 | ||
| 3212 | auto *next_block = state->next_block; | |
| 3213 | ||
| 3214 | size_t m_size = m.size_bytes(); | |
| 3215 | auto *c_ptr = c.data(); | |
| 3216 | auto *m_ptr = m.data(); | |
| 3217 | ||
| 3218 | if (state->remainder) { | |
| 3219 | uint64_t offset = 0; | |
| 3220 | int rem = state->remainder; | |
| 3221 | ||
| 3222 | while (rem < 64 && offset < m_size) { | |
| 3223 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3224 | ++offset; | |
| 3225 | ++rem; | |
| 3226 | } | |
| 3227 | ||
| 3228 | c_ptr += offset; | |
| 3229 | m_ptr += offset; | |
| 3230 | m_size -= offset; | |
| 3231 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3232 | ||
| 3233 | if (m_size == 0) return; | |
| 3234 | } | |
| 3235 | ||
| 3236 | state->remainder = m_size & 63; | |
| 3237 | size_t main_len = m_size - state->remainder; | |
| 3238 | ||
| 3239 | crypto_stream_xchacha20_xor_ic( | |
| 3240 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3241 | ); | |
| 3242 | ||
| 3243 | state->block_counter += main_len / 64; | |
| 3244 | ||
| 3245 | if (state->remainder) { | |
| 3246 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3247 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3248 | ||
| 3249 | crypto_stream_xchacha20_xor_ic( | |
| 3250 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3251 | ); | |
| 3252 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3253 | ||
| 3254 | state->block_counter++; | |
| 3255 | } | |
| 3256 | } | |
| 3257 | ||
| 3258 | static inline void | |
| 3259 | sn_crypto_stream_xchacha20_xor_wrap_final( | |
| 3260 | js_env_t *, | |
| 3261 | js_receiver_t, | |
| 3262 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state | |
| 3263 | ) { | |
| 3264 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3265 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3266 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3267 | state->remainder = 0; | |
| 3268 | } | |
| 3269 | ||
| 3270 | struct sn_crypto_stream_salsa20_xor_state { | |
| 3271 | unsigned char n[crypto_stream_salsa20_NONCEBYTES]; | |
| 3272 | unsigned char k[crypto_stream_salsa20_KEYBYTES]; | |
| 3273 | unsigned char next_block[64]; | |
| 3274 | int remainder; | |
| 3275 | uint64_t block_counter; | |
| 3276 | }; | |
| 3277 | ||
| 3278 | static inline void | |
| 3279 | sn_crypto_stream_salsa20_xor_wrap_init( | |
| 3280 | js_env_t *, | |
| 3281 | js_receiver_t, | |
| 3282 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state, | |
| 3283 | js_typedarray_span_t<> n, | |
| 3284 | js_typedarray_span_t<> k | |
| 3285 | ) { | |
| 3286 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 3287 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 3288 | ||
| 3289 | state->remainder = 0; | |
| 3290 | state->block_counter = 0; | |
| 3291 | memcpy(state->n, n.data(), crypto_stream_salsa20_NONCEBYTES); | |
| 3292 | memcpy(state->k, k.data(), crypto_stream_salsa20_KEYBYTES); | |
| 3293 | } | |
| 3294 | ||
| 3295 | static inline void | |
| 3296 | sn_crypto_stream_salsa20_xor_wrap_update( | |
| 3297 | js_env_t *, | |
| 3298 | js_receiver_t, | |
| 3299 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state, | |
| 3300 | js_typedarray_span_t<> c, | |
| 3301 | js_typedarray_span_t<> m | |
| 3302 | ) { | |
| 3303 | assert(c.size_bytes() == m.size_bytes()); | |
| 3304 | ||
| 3305 | auto *next_block = state->next_block; | |
| 3306 | ||
| 3307 | size_t m_size = m.size_bytes(); | |
| 3308 | auto *c_ptr = c.data(); | |
| 3309 | auto *m_ptr = m.data(); | |
| 3310 | ||
| 3311 | if (state->remainder) { | |
| 3312 | uint64_t offset = 0; | |
| 3313 | int rem = state->remainder; | |
| 3314 | ||
| 3315 | while (rem < 64 && offset < m_size) { | |
| 3316 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3317 | ++offset; | |
| 3318 | ++rem; | |
| 3319 | } | |
| 3320 | ||
| 3321 | c_ptr += offset; | |
| 3322 | m_ptr += offset; | |
| 3323 | m_size -= offset; | |
| 3324 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3325 | ||
| 3326 | if (m_size == 0) return; | |
| 3327 | } | |
| 3328 | ||
| 3329 | state->remainder = m_size & 63; | |
| 3330 | size_t main_len = m_size - state->remainder; | |
| 3331 | ||
| 3332 | crypto_stream_salsa20_xor_ic( | |
| 3333 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3334 | ); | |
| 3335 | ||
| 3336 | state->block_counter += main_len / 64; | |
| 3337 | ||
| 3338 | if (state->remainder) { | |
| 3339 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3340 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3341 | ||
| 3342 | crypto_stream_salsa20_xor_ic( | |
| 3343 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3344 | ); | |
| 3345 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3346 | ||
| 3347 | state->block_counter++; | |
| 3348 | } | |
| 3349 | } | |
| 3350 | ||
| 3351 | static inline void | |
| 3352 | sn_crypto_stream_salsa20_xor_wrap_final( | |
| 3353 | js_env_t *, | |
| 3354 | js_receiver_t, | |
| 3355 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state | |
| 3356 | ) { | |
| 3357 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3358 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3359 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3360 | state->remainder = 0; | |
| 3361 | } | |
| 3362 | ||
| 3363 | // Experimental API | |
| 3364 | ||
| 3365 | static inline void | |
| 3366 | sn_extension_tweak_ed25519_base( | |
| 3367 | js_env_t *, | |
| 3368 | js_receiver_t, | |
| 3369 | js_typedarray_span_t<> n, | |
| 3370 | js_typedarray_span_t<> p, | |
| 3371 | js_typedarray_span_t<> ns | |
| 3372 | ) { | |
| 3373 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3374 | assert(p.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3375 | ||
| 3376 | sn__extension_tweak_ed25519_base(p.data(), n.data(), ns.data(), ns.size_bytes()); | |
| 3377 | } | |
| 3378 | ||
| 3379 | static inline int | |
| 3380 | sn_extension_tweak_ed25519_sign_detached( | |
| 3381 | js_env_t *, | |
| 3382 | js_receiver_t, | |
| 3383 | js_typedarray_span_t<> sig, | |
| 3384 | js_typedarray_span_t<> m, | |
| 3385 | js_typedarray_span_t<> scalar, | |
| 3386 | std::optional<js_typedarray_span_t<>> pk | |
| 3387 | ) { | |
| 3388 | assert(sig.size_bytes() == crypto_sign_BYTES); | |
| 3389 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3390 | ||
| 3391 | uint8_t *pk_data = nullptr; | |
| 3392 | if (pk) { | |
| 3393 | assert(pk->size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3394 | pk_data = pk->data(); | |
| 3395 | } | |
| 3396 | ||
| 3397 | return sn__extension_tweak_ed25519_sign_detached( | |
| 3398 | sig.data(), | |
| 3399 | nullptr, | |
| 3400 | m.data(), | |
| 3401 | m.size_bytes(), | |
| 3402 | scalar.data(), | |
| 3403 | pk_data | |
| 3404 | ); | |
| 3405 | } | |
| 3406 | ||
| 3407 | static inline void | |
| 3408 | sn_extension_tweak_ed25519_sk_to_scalar( | |
| 3409 | js_env_t *, | |
| 3410 | js_receiver_t, | |
| 3411 | js_typedarray_span_t<> n, | |
| 3412 | js_typedarray_span_t<> sk | |
| 3413 | ) { | |
| 3414 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3415 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 3416 | ||
| 3417 | sn__extension_tweak_ed25519_sk_to_scalar(n.data(), sk.data()); | |
| 3418 | } | |
| 3419 | ||
| 3420 | static inline void | |
| 3421 | sn_extension_tweak_ed25519_scalar( | |
| 3422 | js_env_t *, | |
| 3423 | js_receiver_t, | |
| 3424 | js_typedarray_span_t<> scalar_out, | |
| 3425 | js_typedarray_span_t<> scalar, | |
| 3426 | js_typedarray_span_t<> ns | |
| 3427 | ) { | |
| 3428 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3429 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3430 | ||
| 3431 | sn__extension_tweak_ed25519_scalar(scalar_out.data(), scalar.data(), ns.data(), ns.size_bytes()); | |
| 3432 | } | |
| 3433 | ||
| 3434 | static inline int | |
| 3435 | sn_extension_tweak_ed25519_pk( | |
| 3436 | js_env_t *, | |
| 3437 | js_receiver_t, | |
| 3438 | js_typedarray_span_t<> tpk, | |
| 3439 | js_typedarray_span_t<> pk, | |
| 3440 | js_typedarray_span_t<> ns | |
| 3441 | ) { | |
| 3442 | assert(tpk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3443 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3444 | ||
| 3445 | return sn__extension_tweak_ed25519_pk(tpk.data(), pk.data(), ns.data(), ns.size_bytes()); | |
| 3446 | } | |
| 3447 | ||
| 3448 | static inline void | |
| 3449 | sn_extension_tweak_ed25519_keypair( | |
| 3450 | js_env_t *, | |
| 3451 | js_receiver_t, | |
| 3452 | js_typedarray_span_t<> pk, | |
| 3453 | js_typedarray_span_t<> scalar_out, | |
| 3454 | js_typedarray_span_t<> scalar_in, | |
| 3455 | js_typedarray_span_t<> ns | |
| 3456 | ) { | |
| 3457 | assert(pk.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3458 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3459 | assert(scalar_in.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3460 | ||
| 3461 | sn__extension_tweak_ed25519_keypair( | |
| 3462 | pk.data(), | |
| 3463 | scalar_out.data(), | |
| 3464 | scalar_in.data(), | |
| 3465 | ns.data(), | |
| 3466 | ns.size_bytes() | |
| 3467 | ); | |
| 3468 | } | |
| 3469 | ||
| 3470 | static inline void | |
| 3471 | sn_extension_tweak_ed25519_scalar_add( | |
| 3472 | js_env_t *, | |
| 3473 | js_receiver_t, | |
| 3474 | js_typedarray_span_t<> scalar_out, | |
| 3475 | js_typedarray_span_t<> scalar, | |
| 3476 | js_typedarray_span_t<> n | |
| 3477 | ) { | |
| 3478 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3479 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3480 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3481 | ||
| 3482 | sn__extension_tweak_ed25519_scalar_add(scalar_out.data(), scalar.data(), n.data()); | |
| 3483 | } | |
| 3484 | ||
| 3485 | static inline int | |
| 3486 | sn_extension_tweak_ed25519_pk_add( | |
| 3487 | js_env_t *, | |
| 3488 | js_receiver_t, | |
| 3489 | js_typedarray_span_t<> tpk, | |
| 3490 | js_typedarray_span_t<> pk, | |
| 3491 | js_typedarray_span_t<> p | |
| 3492 | ) { | |
| 3493 | assert(tpk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3494 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3495 | assert(p.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3496 | ||
| 3497 | return sn__extension_tweak_ed25519_pk_add(tpk.data(), pk.data(), p.data()); | |
| 3498 | } | |
| 3499 | ||
| 3500 | static inline int | |
| 3501 | sn_extension_tweak_ed25519_keypair_add( | |
| 3502 | js_env_t *, | |
| 3503 | js_receiver_t, | |
| 3504 | js_typedarray_span_t<> pk, | |
| 3505 | js_typedarray_span_t<> scalar_out, | |
| 3506 | js_typedarray_span_t<> scalar_in, | |
| 3507 | js_typedarray_span_t<> tweak | |
| 3508 | ) { | |
| 3509 | assert(pk.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3510 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3511 | assert(scalar_in.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3512 | assert(tweak.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3513 | ||
| 3514 | return sn__extension_tweak_ed25519_keypair_add( | |
| 3515 | pk.data(), | |
| 3516 | scalar_out.data(), | |
| 3517 | scalar_in.data(), | |
| 3518 | tweak.data() | |
| 3519 | ); | |
| 3520 | } | |
| 3521 | ||
| 3522 | static inline int | |
| 3523 | sn_extension_pbkdf2_sha512( | |
| 3524 | js_env_t *, | |
| 3525 | js_receiver_t, | |
| 3526 | js_typedarray_span_t<> out, | |
| 3527 | js_typedarray_span_t<> passwd, | |
| 3528 | js_typedarray_span_t<> salt, | |
| 3529 | uint64_t iter, | |
| 3530 | uint64_t outlen | |
| 3531 | ) { | |
| 3532 | ||
| 3533 | assert(iter >= sn__extension_pbkdf2_sha512_ITERATIONS_MIN); | |
| 3534 | assert(outlen <= sn__extension_pbkdf2_sha512_BYTES_MAX); | |
| 3535 | assert(out.size_bytes() >= static_cast<size_t>(outlen)); | |
| 3536 | ||
| 3537 | return sn__extension_pbkdf2_sha512( | |
| 3538 | passwd.data(), | |
| 3539 | passwd.size_bytes(), | |
| 3540 | salt.data(), | |
| 3541 | salt.size_bytes(), | |
| 3542 | iter, | |
| 3543 | out.data(), | |
| 3544 | outlen | |
| 3545 | ); | |
| 3546 | } | |
| 3547 | ||
| 3548 | struct sn_async_pbkdf2_sha512_request : sn_async_task { | |
| 3549 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 3550 | std::span<uint8_t> out; | |
| 3551 | ||
| 3552 | size_t outlen; | |
| 3553 | ||
| 3554 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 3555 | std::span<uint8_t> pwd; | |
| 3556 | ||
| 3557 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 3558 | std::span<uint8_t> salt; | |
| 3559 | ||
| 3560 | uint64_t iter; | |
| 3561 | ||
| 3562 | sn_async_pbkdf2_sha512_request(js_env_t *env) : sn_async_task(env) {} | |
| 3563 | }; | |
| 3564 | ||
| 3565 | static void | |
| 3566 | async_pbkdf2_sha512_execute(uv_work_t *uv_req) { | |
| 3567 | auto *req = reinterpret_cast<sn_async_pbkdf2_sha512_request *>(uv_req); | |
| 3568 | ||
| 3569 | req->code = sn__extension_pbkdf2_sha512( | |
| 3570 | req->pwd.data(), | |
| 3571 | req->pwd.size(), | |
| 3572 | req->salt.data(), | |
| 3573 | req->salt.size(), | |
| 3574 | req->iter, | |
| 3575 | req->out.data(), | |
| 3576 | req->outlen | |
| 3577 | ); | |
| 3578 | } | |
| 3579 | ||
| 3580 | static void | |
| 3581 | async_pbkdf2_sha512_complete(uv_work_t *uv_req, int status) { | |
| 3582 | int err; | |
| 3583 | ||
| 3584 | auto *req = reinterpret_cast<sn_async_pbkdf2_sha512_request *>(uv_req); | |
| 3585 | ||
| 3586 | if (req->exiting) return delete req; | |
| 3587 | ||
| 3588 | js_handle_scope_t *scope; | |
| 3589 | err = js_open_handle_scope(req->env, &scope); | |
| 3590 | assert(err == 0); | |
| 3591 | ||
| 3592 | js_function_t<void, int> callback; | |
| 3593 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 3594 | assert(err == 0); | |
| 3595 | ||
| 3596 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 3597 | assert(err != js_pending_exception); | |
| 3598 | ||
| 3599 | err = js_close_handle_scope(req->env, scope); | |
| 3600 | assert(err == 0); | |
| 3601 | ||
| 3602 | delete req; | |
| 3603 | } | |
| 3604 | ||
| 3605 | static inline void | |
| 3606 | sn_extension_pbkdf2_sha512_async( | |
| 3607 | js_env_t *env, | |
| 3608 | js_receiver_t, | |
| 3609 | ||
| 3610 | js_arraybuffer_t out, | |
| 3611 | uint32_t out_offset, | |
| 3612 | uint32_t out_len, | |
| 3613 | ||
| 3614 | js_arraybuffer_t pwd, | |
| 3615 | uint32_t pwd_offset, | |
| 3616 | uint32_t pwd_len, | |
| 3617 | ||
| 3618 | js_arraybuffer_t salt, | |
| 3619 | uint32_t salt_offset, | |
| 3620 | uint32_t salt_len, | |
| 3621 | ||
| 3622 | uint64_t iter, | |
| 3623 | uint64_t outlen, | |
| 3624 | ||
| 3625 | js_function_t<void, int> callback | |
| 3626 | ) { | |
| 3627 | int err; | |
| 3628 | ||
| 3629 | auto *req = new sn_async_pbkdf2_sha512_request(env); | |
| 3630 | ||
| 3631 | std::span<uint8_t> out_view; | |
| 3632 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 3633 | assert(err == 0); | |
| 3634 | assert(out_offset + out_len <= out_view.size()); | |
| 3635 | ||
| 3636 | req->out = {&out_view[out_offset], out_len}; | |
| 3637 | ||
| 3638 | std::span<uint8_t> pwd_view; | |
| 3639 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 3640 | assert(err == 0); | |
| 3641 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 3642 | ||
| 3643 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 3644 | ||
| 3645 | std::span<uint8_t> salt_view; | |
| 3646 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 3647 | assert(err == 0); | |
| 3648 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 3649 | ||
| 3650 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 3651 | ||
| 3652 | req->iter = iter; | |
| 3653 | req->outlen = outlen; | |
| 3654 | ||
| 3655 | err = js_create_reference(env, out, req->out_ref); | |
| 3656 | assert(err == 0); | |
| 3657 | ||
| 3658 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 3659 | assert(err == 0); | |
| 3660 | ||
| 3661 | err = js_create_reference(env, salt, req->salt_ref); | |
| 3662 | assert(err == 0); | |
| 3663 | ||
| 3664 | err = js_create_reference(env, callback, req->cb); | |
| 3665 | assert(err == 0); | |
| 3666 | ||
| 3667 | uv_loop_t *loop; | |
| 3668 | err = js_get_env_loop(env, &loop); | |
| 3669 | assert(err == 0); | |
| 3670 | ||
| 3671 | err = uv_queue_work(loop, &req->task, async_pbkdf2_sha512_execute, async_pbkdf2_sha512_complete); | |
| 3672 | assert(err == 0); | |
| 3673 | } | |
| 3674 | ||
| 3675 | static js_value_t * | |
| 3676 | sodium_native_exports(js_env_t *env, js_value_t *exports) { | |
| 3677 | int err; | |
| 3678 | ||
| 3679 | err = sodium_init(); | |
| 3680 | assert(err >= 0); | |
| 3681 | ||
| 3682 | #define V_FUNCTION(name, fn) \ | |
| 3683 | err = js_set_property<fn>(env, exports, name); \ | |
| 3684 | assert(err == 0); | |
| 3685 | ||
| 3686 | #define V_FUNCTION_NOSCOPE(name, fn) \ | |
| 3687 | err = js_set_property<fn, js_function_options_t{.scoped = false}>(env, exports, name); \ | |
| 3688 | assert(err == 0); | |
| 3689 | ||
| 3690 | #define V_UINT32(name, constant) \ | |
| 3691 | err = js_set_property(env, exports, name, static_cast<uint32_t>(constant)); \ | |
| 3692 | assert(err == 0); | |
| 3693 | ||
| 3694 | #define V_UINT64(name, constant) \ | |
| 3695 | err = js_set_property(env, exports, name, static_cast<int64_t>(std::min<uint64_t>(constant, js_max_safe_integer))); \ | |
| 3696 | assert(err == 0); | |
| 3697 | ||
| 3698 | #define V_STRING(name, str) \ | |
| 3699 | err = js_set_property(env, exports, name, str); \ | |
| 3700 | assert(err == 0); | |
| 3701 | ||
| 3702 | // memory | |
| 3703 | ||
| 3704 | V_FUNCTION("sodium_memzero", sn_sodium_memzero); | |
| 3705 | V_FUNCTION("sodium_mlock", sn_sodium_mlock); | |
| 3706 | V_FUNCTION("sodium_munlock", sn_sodium_munlock); | |
| 3707 | V_FUNCTION("sodium_malloc", sn_sodium_malloc); | |
| 3708 | V_FUNCTION("sodium_free", sn_sodium_free); | |
| 3709 | V_FUNCTION("sodium_mprotect_noaccess", sn_sodium_mprotect_noaccess); | |
| 3710 | V_FUNCTION("sodium_mprotect_readonly", sn_sodium_mprotect_readonly); | |
| 3711 | V_FUNCTION("sodium_mprotect_readwrite", sn_sodium_mprotect_readwrite); | |
| 3712 | ||
| 3713 | // randombytes | |
| 3714 | ||
| 3715 | V_FUNCTION_NOSCOPE("randombytes_buf", sn_randombytes_buf); | |
| 3716 | V_FUNCTION_NOSCOPE("randombytes_buf_deterministic", sn_randombytes_buf_deterministic); | |
| 3717 | V_FUNCTION_NOSCOPE("randombytes_random", sn_randombytes_random); | |
| 3718 | V_FUNCTION_NOSCOPE("randombytes_uniform", sn_randombytes_uniform); | |
| 3719 | ||
| 3720 | V_UINT32("randombytes_SEEDBYTES", randombytes_SEEDBYTES); | |
| 3721 | ||
| 3722 | // sodium helpers | |
| 3723 | ||
| 3724 | V_FUNCTION("sodium_memcmp", sn_sodium_memcmp); | |
| 3725 | V_FUNCTION("sodium_increment", sn_sodium_increment); | |
| 3726 | V_FUNCTION("sodium_add", sn_sodium_add); | |
| 3727 | V_FUNCTION("sodium_sub", sn_sodium_sub); | |
| 3728 | V_FUNCTION("sodium_compare", sn_sodium_compare); | |
| 3729 | V_FUNCTION("sodium_is_zero", sn_sodium_is_zero); | |
| 3730 | V_FUNCTION("sodium_pad", sn_sodium_pad); | |
| 3731 | V_FUNCTION("sodium_unpad", sn_sodium_unpad); | |
| 3732 | ||
| 3733 | // crypto_aead | |
| 3734 | ||
| 3735 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_keygen", sn_crypto_aead_xchacha20poly1305_ietf_keygen); | |
| 3736 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_encrypt", sn_crypto_aead_xchacha20poly1305_ietf_encrypt); | |
| 3737 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_decrypt", sn_crypto_aead_xchacha20poly1305_ietf_decrypt); | |
| 3738 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_encrypt_detached", sn_crypto_aead_xchacha20poly1305_ietf_encrypt_detached); | |
| 3739 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_decrypt_detached", sn_crypto_aead_xchacha20poly1305_ietf_decrypt_detached); | |
| 3740 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_ABYTES", crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 3741 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_KEYBYTES", crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 3742 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_NPUBBYTES", crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 3743 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_NSECBYTES", crypto_aead_xchacha20poly1305_ietf_NSECBYTES); | |
| 3744 | V_UINT64("crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX", crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX); | |
| 3745 | ||
| 3746 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_keygen", sn_crypto_aead_chacha20poly1305_ietf_keygen); | |
| 3747 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_encrypt", sn_crypto_aead_chacha20poly1305_ietf_encrypt); | |
| 3748 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_decrypt", sn_crypto_aead_chacha20poly1305_ietf_decrypt); | |
| 3749 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_encrypt_detached", sn_crypto_aead_chacha20poly1305_ietf_encrypt_detached); | |
| 3750 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_decrypt_detached", sn_crypto_aead_chacha20poly1305_ietf_decrypt_detached); | |
| 3751 | V_UINT32("crypto_aead_chacha20poly1305_ietf_ABYTES", crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 3752 | V_UINT32("crypto_aead_chacha20poly1305_ietf_KEYBYTES", crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 3753 | V_UINT32("crypto_aead_chacha20poly1305_ietf_NPUBBYTES", crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 3754 | V_UINT32("crypto_aead_chacha20poly1305_ietf_NSECBYTES", crypto_aead_chacha20poly1305_ietf_NSECBYTES); | |
| 3755 | V_UINT64("crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX", crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX); | |
| 3756 | ||
| 3757 | // crypto_auth | |
| 3758 | ||
| 3759 | V_FUNCTION("crypto_auth", sn_crypto_auth); | |
| 3760 | V_FUNCTION("crypto_auth_verify", sn_crypto_auth_verify); | |
| 3761 | V_UINT32("crypto_auth_BYTES", crypto_auth_BYTES); | |
| 3762 | V_UINT32("crypto_auth_KEYBYTES", crypto_auth_KEYBYTES); | |
| 3763 | V_STRING("crypto_auth_PRIMITIVE", crypto_auth_PRIMITIVE); | |
| 3764 | ||
| 3765 | // crypto_box | |
| 3766 | ||
| 3767 | V_FUNCTION("crypto_box_keypair", sn_crypto_box_keypair); | |
| 3768 | V_FUNCTION("crypto_box_seed_keypair", sn_crypto_box_seed_keypair); | |
| 3769 | V_FUNCTION("crypto_box_easy", sn_crypto_box_easy); | |
| 3770 | V_FUNCTION("crypto_box_open_easy", sn_crypto_box_open_easy); | |
| 3771 | V_FUNCTION("crypto_box_detached", sn_crypto_box_detached); | |
| 3772 | V_FUNCTION("crypto_box_open_detached", sn_crypto_box_open_detached); | |
| 3773 | V_FUNCTION("crypto_box_seal", sn_crypto_box_seal); | |
| 3774 | V_FUNCTION_NOSCOPE("crypto_box_seal_open", sn_crypto_box_seal_open); | |
| 3775 | ||
| 3776 | V_UINT32("crypto_box_SEEDBYTES", crypto_box_SEEDBYTES); | |
| 3777 | V_UINT32("crypto_box_PUBLICKEYBYTES", crypto_box_PUBLICKEYBYTES); | |
| 3778 | V_UINT32("crypto_box_SECRETKEYBYTES", crypto_box_SECRETKEYBYTES); | |
| 3779 | V_UINT32("crypto_box_NONCEBYTES", crypto_box_NONCEBYTES); | |
| 3780 | V_UINT32("crypto_box_MACBYTES", crypto_box_MACBYTES); | |
| 3781 | V_UINT32("crypto_box_SEALBYTES", crypto_box_SEALBYTES); | |
| 3782 | V_STRING("crypto_box_PRIMITIVE", crypto_box_PRIMITIVE); | |
| 3783 | ||
| 3784 | // crypto_core | |
| 3785 | ||
| 3786 | V_FUNCTION("crypto_core_ed25519_is_valid_point", sn_crypto_core_ed25519_is_valid_point); | |
| 3787 | V_FUNCTION("crypto_core_ed25519_from_uniform", sn_crypto_core_ed25519_from_uniform); | |
| 3788 | V_FUNCTION("crypto_core_ed25519_add", sn_crypto_core_ed25519_add); | |
| 3789 | V_FUNCTION("crypto_core_ed25519_sub", sn_crypto_core_ed25519_sub); | |
| 3790 | V_FUNCTION("crypto_core_ed25519_scalar_random", sn_crypto_core_ed25519_scalar_random); | |
| 3791 | V_FUNCTION("crypto_core_ed25519_scalar_reduce", sn_crypto_core_ed25519_scalar_reduce); | |
| 3792 | V_FUNCTION("crypto_core_ed25519_scalar_invert", sn_crypto_core_ed25519_scalar_invert); | |
| 3793 | V_FUNCTION("crypto_core_ed25519_scalar_negate", sn_crypto_core_ed25519_scalar_negate); | |
| 3794 | V_FUNCTION("crypto_core_ed25519_scalar_complement", sn_crypto_core_ed25519_scalar_complement); | |
| 3795 | V_FUNCTION("crypto_core_ed25519_scalar_add", sn_crypto_core_ed25519_scalar_add); | |
| 3796 | V_FUNCTION("crypto_core_ed25519_scalar_sub", sn_crypto_core_ed25519_scalar_sub); | |
| 3797 | V_UINT32("crypto_core_ed25519_BYTES", crypto_core_ed25519_BYTES); | |
| 3798 | V_UINT32("crypto_core_ed25519_UNIFORMBYTES", crypto_core_ed25519_UNIFORMBYTES); | |
| 3799 | V_UINT32("crypto_core_ed25519_SCALARBYTES", crypto_core_ed25519_SCALARBYTES); | |
| 3800 | V_UINT32("crypto_core_ed25519_NONREDUCEDSCALARBYTES", crypto_core_ed25519_NONREDUCEDSCALARBYTES); | |
| 3801 | ||
| 3802 | // crypto_kdf | |
| 3803 | ||
| 3804 | V_FUNCTION("crypto_kdf_keygen", sn_crypto_kdf_keygen); | |
| 3805 | V_FUNCTION("crypto_kdf_derive_from_key", sn_crypto_kdf_derive_from_key); | |
| 3806 | V_UINT32("crypto_kdf_BYTES_MIN", crypto_kdf_BYTES_MIN); | |
| 3807 | V_UINT32("crypto_kdf_BYTES_MAX", crypto_kdf_BYTES_MAX); | |
| 3808 | V_UINT32("crypto_kdf_CONTEXTBYTES", crypto_kdf_CONTEXTBYTES); | |
| 3809 | V_UINT32("crypto_kdf_KEYBYTES", crypto_kdf_KEYBYTES); | |
| 3810 | V_STRING("crypto_kdf_PRIMITIVE", crypto_kdf_PRIMITIVE); | |
| 3811 | ||
| 3812 | // crypto_kx | |
| 3813 | ||
| 3814 | V_FUNCTION("crypto_kx_keypair", sn_crypto_kx_keypair); | |
| 3815 | V_FUNCTION("crypto_kx_seed_keypair", sn_crypto_kx_seed_keypair); | |
| 3816 | V_FUNCTION("crypto_kx_client_session_keys", sn_crypto_kx_client_session_keys); | |
| 3817 | V_FUNCTION("crypto_kx_server_session_keys", sn_crypto_kx_server_session_keys); | |
| 3818 | V_UINT32("crypto_kx_PUBLICKEYBYTES", crypto_kx_PUBLICKEYBYTES); | |
| 3819 | V_UINT32("crypto_kx_SECRETKEYBYTES", crypto_kx_SECRETKEYBYTES); | |
| 3820 | V_UINT32("crypto_kx_SEEDBYTES", crypto_kx_SEEDBYTES); | |
| 3821 | V_UINT32("crypto_kx_SESSIONKEYBYTES", crypto_kx_SESSIONKEYBYTES); | |
| 3822 | V_STRING("crypto_kx_PRIMITIVE", crypto_kx_PRIMITIVE); | |
| 3823 | ||
| 3824 | // crypto_generichash | |
| 3825 | ||
| 3826 | V_FUNCTION_NOSCOPE("crypto_generichash", sn_crypto_generichash); | |
| 3827 | V_FUNCTION("crypto_generichash_batch", sn_crypto_generichash_batch); | |
| 3828 | V_FUNCTION_NOSCOPE("crypto_generichash_keygen", sn_crypto_generichash_keygen); | |
| 3829 | V_FUNCTION_NOSCOPE("crypto_generichash_init", sn_crypto_generichash_init); | |
| 3830 | V_FUNCTION_NOSCOPE("crypto_generichash_update", sn_crypto_generichash_update); | |
| 3831 | V_FUNCTION_NOSCOPE("crypto_generichash_final", sn_crypto_generichash_final); | |
| 3832 | ||
| 3833 | V_UINT32("crypto_generichash_STATEBYTES", sizeof(crypto_generichash_state)); | |
| 3834 | V_STRING("crypto_generichash_PRIMITIVE", crypto_generichash_PRIMITIVE); | |
| 3835 | V_UINT32("crypto_generichash_BYTES_MIN", crypto_generichash_BYTES_MIN); | |
| 3836 | V_UINT32("crypto_generichash_BYTES_MAX", crypto_generichash_BYTES_MAX); | |
| 3837 | V_UINT32("crypto_generichash_BYTES", crypto_generichash_BYTES); | |
| 3838 | V_UINT32("crypto_generichash_KEYBYTES_MIN", crypto_generichash_KEYBYTES_MIN); | |
| 3839 | V_UINT32("crypto_generichash_KEYBYTES_MAX", crypto_generichash_KEYBYTES_MAX); | |
| 3840 | V_UINT32("crypto_generichash_KEYBYTES", crypto_generichash_KEYBYTES); | |
| 3841 | ||
| 3842 | // crypto_hash | |
| 3843 | ||
| 3844 | V_FUNCTION("crypto_hash", sn_crypto_hash); | |
| 3845 | V_UINT32("crypto_hash_BYTES", crypto_hash_BYTES); | |
| 3846 | V_STRING("crypto_hash_PRIMITIVE", crypto_hash_PRIMITIVE); | |
| 3847 | ||
| 3848 | V_FUNCTION("crypto_hash_sha256", sn_crypto_hash_sha256); | |
| 3849 | V_FUNCTION("crypto_hash_sha256_init", sn_crypto_hash_sha256_init); | |
| 3850 | V_FUNCTION("crypto_hash_sha256_update", sn_crypto_hash_sha256_update); | |
| 3851 | V_FUNCTION("crypto_hash_sha256_final", sn_crypto_hash_sha256_final); | |
| 3852 | V_UINT32("crypto_hash_sha256_STATEBYTES", sizeof(crypto_hash_sha256_state)); | |
| 3853 | V_UINT32("crypto_hash_sha256_BYTES", crypto_hash_sha256_BYTES); | |
| 3854 | ||
| 3855 | V_FUNCTION("crypto_hash_sha512", sn_crypto_hash_sha512); | |
| 3856 | V_FUNCTION("crypto_hash_sha512_init", sn_crypto_hash_sha512_init); | |
| 3857 | V_FUNCTION("crypto_hash_sha512_update", sn_crypto_hash_sha512_update); | |
| 3858 | V_FUNCTION("crypto_hash_sha512_final", sn_crypto_hash_sha512_final); | |
| 3859 | V_UINT32("crypto_hash_sha512_STATEBYTES", sizeof(crypto_hash_sha512_state)); | |
| 3860 | V_UINT32("crypto_hash_sha512_BYTES", crypto_hash_sha512_BYTES); | |
| 3861 | ||
| 3862 | // crypto_onetimeauth | |
| 3863 | ||
| 3864 | V_FUNCTION("crypto_onetimeauth", sn_crypto_onetimeauth); | |
| 3865 | V_FUNCTION("crypto_onetimeauth_verify", sn_crypto_onetimeauth_verify); | |
| 3866 | V_FUNCTION("crypto_onetimeauth_init", sn_crypto_onetimeauth_init); | |
| 3867 | V_FUNCTION("crypto_onetimeauth_update", sn_crypto_onetimeauth_update); | |
| 3868 | V_FUNCTION("crypto_onetimeauth_final", sn_crypto_onetimeauth_final); | |
| 3869 | V_UINT32("crypto_onetimeauth_STATEBYTES", sizeof(crypto_onetimeauth_state)); | |
| 3870 | V_UINT32("crypto_onetimeauth_BYTES", crypto_onetimeauth_BYTES); | |
| 3871 | V_UINT32("crypto_onetimeauth_KEYBYTES", crypto_onetimeauth_KEYBYTES); | |
| 3872 | V_STRING("crypto_onetimeauth_PRIMITIVE", crypto_onetimeauth_PRIMITIVE); | |
| 3873 | ||
| 3874 | // crypto_pwhash | |
| 3875 | ||
| 3876 | V_FUNCTION("crypto_pwhash", sn_crypto_pwhash); | |
| 3877 | V_FUNCTION("crypto_pwhash_str", sn_crypto_pwhash_str); | |
| 3878 | V_FUNCTION("crypto_pwhash_str_verify", sn_crypto_pwhash_str_verify); | |
| 3879 | V_FUNCTION("crypto_pwhash_str_needs_rehash", sn_crypto_pwhash_str_needs_rehash); | |
| 3880 | V_FUNCTION("crypto_pwhash_async", sn_crypto_pwhash_async); | |
| 3881 | V_FUNCTION("crypto_pwhash_str_async", sn_crypto_pwhash_str_async); | |
| 3882 | V_FUNCTION("crypto_pwhash_str_verify_async", sn_crypto_pwhash_str_verify_async); | |
| 3883 | V_UINT32("crypto_pwhash_ALG_ARGON2I13", crypto_pwhash_ALG_ARGON2I13); | |
| 3884 | V_UINT32("crypto_pwhash_ALG_ARGON2ID13", crypto_pwhash_ALG_ARGON2ID13); | |
| 3885 | V_UINT32("crypto_pwhash_ALG_DEFAULT", crypto_pwhash_ALG_DEFAULT); | |
| 3886 | V_UINT32("crypto_pwhash_BYTES_MIN", crypto_pwhash_BYTES_MIN); | |
| 3887 | V_UINT32("crypto_pwhash_BYTES_MAX", crypto_pwhash_BYTES_MAX); | |
| 3888 | V_UINT32("crypto_pwhash_PASSWD_MIN", crypto_pwhash_PASSWD_MIN); | |
| 3889 | V_UINT32("crypto_pwhash_PASSWD_MAX", crypto_pwhash_PASSWD_MAX); | |
| 3890 | V_UINT32("crypto_pwhash_SALTBYTES", crypto_pwhash_SALTBYTES); | |
| 3891 | V_UINT32("crypto_pwhash_STRBYTES", crypto_pwhash_STRBYTES); | |
| 3892 | V_STRING("crypto_pwhash_STRPREFIX", crypto_pwhash_STRPREFIX); | |
| 3893 | V_UINT32("crypto_pwhash_OPSLIMIT_MIN", crypto_pwhash_OPSLIMIT_MIN); | |
| 3894 | V_UINT32("crypto_pwhash_OPSLIMIT_MAX", crypto_pwhash_OPSLIMIT_MAX); | |
| 3895 | V_UINT64("crypto_pwhash_MEMLIMIT_MIN", crypto_pwhash_MEMLIMIT_MIN); | |
| 3896 | V_UINT64("crypto_pwhash_MEMLIMIT_MAX", crypto_pwhash_MEMLIMIT_MAX); | |
| 3897 | V_UINT32("crypto_pwhash_OPSLIMIT_INTERACTIVE", crypto_pwhash_OPSLIMIT_INTERACTIVE); | |
| 3898 | V_UINT64("crypto_pwhash_MEMLIMIT_INTERACTIVE", crypto_pwhash_MEMLIMIT_INTERACTIVE); | |
| 3899 | V_UINT32("crypto_pwhash_OPSLIMIT_MODERATE", crypto_pwhash_OPSLIMIT_MODERATE); | |
| 3900 | V_UINT64("crypto_pwhash_MEMLIMIT_MODERATE", crypto_pwhash_MEMLIMIT_MODERATE); | |
| 3901 | V_UINT32("crypto_pwhash_OPSLIMIT_SENSITIVE", crypto_pwhash_OPSLIMIT_SENSITIVE); | |
| 3902 | V_UINT64("crypto_pwhash_MEMLIMIT_SENSITIVE", crypto_pwhash_MEMLIMIT_SENSITIVE); | |
| 3903 | V_STRING("crypto_pwhash_PRIMITIVE", crypto_pwhash_PRIMITIVE); | |
| 3904 | ||
| 3905 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256", sn_crypto_pwhash_scryptsalsa208sha256); | |
| 3906 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str", sn_crypto_pwhash_scryptsalsa208sha256_str); | |
| 3907 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_verify", sn_crypto_pwhash_scryptsalsa208sha256_str_verify); | |
| 3908 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_needs_rehash", sn_crypto_pwhash_scryptsalsa208sha256_str_needs_rehash); | |
| 3909 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_async", sn_crypto_pwhash_scryptsalsa208sha256_async); | |
| 3910 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_async", sn_crypto_pwhash_scryptsalsa208sha256_str_async) | |
| 3911 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_verify_async", sn_crypto_pwhash_scryptsalsa208sha256_str_verify_async); | |
| 3912 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_BYTES_MIN", crypto_pwhash_scryptsalsa208sha256_BYTES_MIN); | |
| 3913 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_BYTES_MAX", crypto_pwhash_scryptsalsa208sha256_BYTES_MAX); | |
| 3914 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN", crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN); | |
| 3915 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX", crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX); | |
| 3916 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_SALTBYTES", crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 3917 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_STRBYTES", crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 3918 | V_STRING("crypto_pwhash_scryptsalsa208sha256_STRPREFIX", crypto_pwhash_scryptsalsa208sha256_STRPREFIX); | |
| 3919 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 3920 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 3921 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 3922 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 3923 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); | |
| 3924 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); | |
| 3925 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE); | |
| 3926 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE); | |
| 3927 | ||
| 3928 | // crypto_scalarmult | |
| 3929 | ||
| 3930 | V_FUNCTION("crypto_scalarmult_base", sn_crypto_scalarmult_base); | |
| 3931 | V_FUNCTION("crypto_scalarmult", sn_crypto_scalarmult); | |
| 3932 | V_STRING("crypto_scalarmult_PRIMITIVE", crypto_scalarmult_PRIMITIVE); | |
| 3933 | V_UINT32("crypto_scalarmult_BYTES", crypto_scalarmult_BYTES); | |
| 3934 | V_UINT32("crypto_scalarmult_SCALARBYTES", crypto_scalarmult_SCALARBYTES); | |
| 3935 | ||
| 3936 | V_FUNCTION("crypto_scalarmult_ed25519_base", sn_crypto_scalarmult_ed25519_base); | |
| 3937 | V_FUNCTION("crypto_scalarmult_ed25519", sn_crypto_scalarmult_ed25519); | |
| 3938 | V_FUNCTION("crypto_scalarmult_ed25519_base_noclamp", sn_crypto_scalarmult_ed25519_base_noclamp); | |
| 3939 | V_FUNCTION("crypto_scalarmult_ed25519_noclamp", sn_crypto_scalarmult_ed25519_noclamp); | |
| 3940 | V_UINT32("crypto_scalarmult_ed25519_BYTES", crypto_scalarmult_ed25519_BYTES); | |
| 3941 | V_UINT32("crypto_scalarmult_ed25519_SCALARBYTES", crypto_scalarmult_ed25519_SCALARBYTES); | |
| 3942 | ||
| 3943 | // crypto_secretbox | |
| 3944 | ||
| 3945 | V_FUNCTION("crypto_secretbox_easy", sn_crypto_secretbox_easy); | |
| 3946 | V_FUNCTION("crypto_secretbox_open_easy", sn_crypto_secretbox_open_easy); | |
| 3947 | V_FUNCTION("crypto_secretbox_detached", sn_crypto_secretbox_detached); | |
| 3948 | V_FUNCTION("crypto_secretbox_open_detached", sn_crypto_secretbox_open_detached); | |
| 3949 | V_UINT32("crypto_secretbox_KEYBYTES", crypto_secretbox_KEYBYTES); | |
| 3950 | V_UINT32("crypto_secretbox_NONCEBYTES", crypto_secretbox_NONCEBYTES); | |
| 3951 | V_UINT32("crypto_secretbox_MACBYTES", crypto_secretbox_MACBYTES); | |
| 3952 | V_STRING("crypto_secretbox_PRIMITIVE", crypto_secretbox_PRIMITIVE); | |
| 3953 | ||
| 3954 | // crypto_secretstream | |
| 3955 | ||
| 3956 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_keygen", sn_crypto_secretstream_xchacha20poly1305_keygen); | |
| 3957 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_init_push", sn_crypto_secretstream_xchacha20poly1305_init_push); | |
| 3958 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_init_pull", sn_crypto_secretstream_xchacha20poly1305_init_pull); | |
| 3959 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_push", sn_crypto_secretstream_xchacha20poly1305_push); | |
| 3960 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_pull", sn_crypto_secretstream_xchacha20poly1305_pull); | |
| 3961 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_rekey", sn_crypto_secretstream_xchacha20poly1305_rekey); | |
| 3962 | ||
| 3963 | V_UINT32("crypto_secretstream_xchacha20poly1305_STATEBYTES", sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 3964 | V_UINT32("crypto_secretstream_xchacha20poly1305_ABYTES", crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 3965 | V_UINT32("crypto_secretstream_xchacha20poly1305_HEADERBYTES", crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 3966 | V_UINT32("crypto_secretstream_xchacha20poly1305_KEYBYTES", crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 3967 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAGBYTES", 1); | |
| 3968 | V_UINT64("crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX", crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX); | |
| 3969 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_MESSAGE", crypto_secretstream_xchacha20poly1305_TAG_MESSAGE); | |
| 3970 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_PUSH", crypto_secretstream_xchacha20poly1305_TAG_PUSH); | |
| 3971 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_REKEY", crypto_secretstream_xchacha20poly1305_TAG_REKEY); | |
| 3972 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_FINAL", crypto_secretstream_xchacha20poly1305_TAG_FINAL); | |
| 3973 | ||
| 3974 | // crypto_shorthash | |
| 3975 | ||
| 3976 | V_FUNCTION("crypto_shorthash", sn_crypto_shorthash); | |
| 3977 | V_UINT32("crypto_shorthash_BYTES", crypto_shorthash_BYTES); | |
| 3978 | V_UINT32("crypto_shorthash_KEYBYTES", crypto_shorthash_KEYBYTES); | |
| 3979 | V_STRING("crypto_shorthash_PRIMITIVE", crypto_shorthash_PRIMITIVE); | |
| 3980 | ||
| 3981 | // crypto_sign | |
| 3982 | ||
| 3983 | V_FUNCTION("crypto_sign_keypair", sn_crypto_sign_keypair); | |
| 3984 | V_FUNCTION("crypto_sign_seed_keypair", sn_crypto_sign_seed_keypair); | |
| 3985 | V_FUNCTION("crypto_sign", sn_crypto_sign); | |
| 3986 | V_FUNCTION("crypto_sign_open", sn_crypto_sign_open); | |
| 3987 | V_FUNCTION("crypto_sign_detached", sn_crypto_sign_detached); | |
| 3988 | V_FUNCTION_NOSCOPE("crypto_sign_verify_detached", sn_crypto_sign_verify_detached); | |
| 3989 | V_FUNCTION("crypto_sign_ed25519_sk_to_pk", sn_crypto_sign_ed25519_sk_to_pk); | |
| 3990 | V_FUNCTION("crypto_sign_ed25519_pk_to_curve25519", sn_crypto_sign_ed25519_pk_to_curve25519); | |
| 3991 | V_FUNCTION("crypto_sign_ed25519_sk_to_curve25519", sn_crypto_sign_ed25519_sk_to_curve25519); | |
| 3992 | ||
| 3993 | V_UINT32("crypto_sign_SEEDBYTES", crypto_sign_SEEDBYTES); | |
| 3994 | V_UINT32("crypto_sign_PUBLICKEYBYTES", crypto_sign_PUBLICKEYBYTES); | |
| 3995 | V_UINT32("crypto_sign_SECRETKEYBYTES", crypto_sign_SECRETKEYBYTES); | |
| 3996 | V_UINT32("crypto_sign_BYTES", crypto_sign_BYTES); | |
| 3997 | ||
| 3998 | // crypto_stream | |
| 3999 | ||
| 4000 | V_FUNCTION("crypto_stream", sn_crypto_stream); | |
| 4001 | V_UINT32("crypto_stream_KEYBYTES", crypto_stream_KEYBYTES); | |
| 4002 | V_UINT32("crypto_stream_NONCEBYTES", crypto_stream_NONCEBYTES); | |
| 4003 | V_STRING("crypto_stream_PRIMITIVE", crypto_stream_PRIMITIVE); | |
| 4004 | ||
| 4005 | V_FUNCTION_NOSCOPE("crypto_stream_xor", sn_crypto_stream_xor); | |
| 4006 | V_FUNCTION("crypto_stream_xor_init", sn_crypto_stream_xor_wrap_init); | |
| 4007 | V_FUNCTION("crypto_stream_xor_update", sn_crypto_stream_xor_wrap_update); | |
| 4008 | V_FUNCTION("crypto_stream_xor_final", sn_crypto_stream_xor_wrap_final); | |
| 4009 | V_UINT32("crypto_stream_xor_STATEBYTES", sizeof(sn_crypto_stream_xor_state)); | |
| 4010 | ||
| 4011 | V_FUNCTION("crypto_stream_chacha20", sn_crypto_stream_chacha20); | |
| 4012 | V_UINT32("crypto_stream_chacha20_KEYBYTES", crypto_stream_chacha20_KEYBYTES); | |
| 4013 | V_UINT32("crypto_stream_chacha20_NONCEBYTES", crypto_stream_chacha20_NONCEBYTES); | |
| 4014 | V_UINT64("crypto_stream_chacha20_MESSAGEBYTES_MAX", crypto_stream_chacha20_MESSAGEBYTES_MAX); | |
| 4015 | ||
| 4016 | V_FUNCTION("crypto_stream_chacha20_xor", sn_crypto_stream_chacha20_xor); | |
| 4017 | V_FUNCTION("crypto_stream_chacha20_xor_ic", sn_crypto_stream_chacha20_xor_ic); | |
| 4018 | V_FUNCTION("crypto_stream_chacha20_xor_init", sn_crypto_stream_chacha20_xor_wrap_init); | |
| 4019 | V_FUNCTION("crypto_stream_chacha20_xor_update", sn_crypto_stream_chacha20_xor_wrap_update); | |
| 4020 | V_FUNCTION("crypto_stream_chacha20_xor_final", sn_crypto_stream_chacha20_xor_wrap_final); | |
| 4021 | V_UINT32("crypto_stream_chacha20_xor_STATEBYTES", sizeof(sn_crypto_stream_chacha20_xor_state)); | |
| 4022 | ||
| 4023 | V_FUNCTION("crypto_stream_chacha20_ietf", sn_crypto_stream_chacha20_ietf); | |
| 4024 | V_UINT32("crypto_stream_chacha20_ietf_KEYBYTES", crypto_stream_chacha20_ietf_KEYBYTES); | |
| 4025 | V_UINT32("crypto_stream_chacha20_ietf_NONCEBYTES", crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 4026 | V_UINT64("crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX", crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX); | |
| 4027 | V_UINT32("crypto_stream_chacha20_ietf_xor_STATEBYTES", sizeof(sn_crypto_stream_chacha20_ietf_xor_state)); | |
| 4028 | ||
| 4029 | V_FUNCTION("crypto_stream_chacha20_ietf_xor", sn_crypto_stream_chacha20_ietf_xor); | |
| 4030 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_ic", sn_crypto_stream_chacha20_ietf_xor_ic); | |
| 4031 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_init", sn_crypto_stream_chacha20_ietf_xor_wrap_init); | |
| 4032 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_update", sn_crypto_stream_chacha20_ietf_xor_wrap_update); | |
| 4033 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_final", sn_crypto_stream_chacha20_ietf_xor_wrap_final); | |
| 4034 | ||
| 4035 | V_FUNCTION("crypto_stream_xchacha20", sn_crypto_stream_xchacha20); | |
| 4036 | V_UINT32("crypto_stream_xchacha20_KEYBYTES", crypto_stream_xchacha20_KEYBYTES); | |
| 4037 | V_UINT32("crypto_stream_xchacha20_NONCEBYTES", crypto_stream_xchacha20_NONCEBYTES); | |
| 4038 | V_UINT64("crypto_stream_xchacha20_MESSAGEBYTES_MAX", crypto_stream_xchacha20_MESSAGEBYTES_MAX); | |
| 4039 | ||
| 4040 | V_FUNCTION("crypto_stream_xchacha20_xor", sn_crypto_stream_xchacha20_xor); | |
| 4041 | V_FUNCTION("crypto_stream_xchacha20_xor_ic", sn_crypto_stream_xchacha20_xor_ic); | |
| 4042 | V_FUNCTION("crypto_stream_xchacha20_xor_init", sn_crypto_stream_xchacha20_xor_wrap_init); | |
| 4043 | V_FUNCTION("crypto_stream_xchacha20_xor_update", sn_crypto_stream_xchacha20_xor_wrap_update); | |
| 4044 | V_FUNCTION("crypto_stream_xchacha20_xor_final", sn_crypto_stream_xchacha20_xor_wrap_final); | |
| 4045 | V_FUNCTION("crypto_stream_xchacha20", sn_crypto_stream_xchacha20); | |
| 4046 | V_UINT32("crypto_stream_xchacha20_xor_STATEBYTES", sizeof(sn_crypto_stream_xchacha20_xor_state)); | |
| 4047 | ||
| 4048 | V_FUNCTION("crypto_stream_salsa20", sn_crypto_stream_salsa20); | |
| 4049 | V_UINT32("crypto_stream_salsa20_KEYBYTES", crypto_stream_salsa20_KEYBYTES); | |
| 4050 | V_UINT32("crypto_stream_salsa20_NONCEBYTES", crypto_stream_salsa20_NONCEBYTES); | |
| 4051 | V_UINT64("crypto_stream_salsa20_MESSAGEBYTES_MAX", crypto_stream_salsa20_MESSAGEBYTES_MAX); | |
| 4052 | ||
| 4053 | V_FUNCTION("crypto_stream_salsa20_xor", sn_crypto_stream_salsa20_xor); | |
| 4054 | V_FUNCTION("crypto_stream_salsa20_xor_ic", sn_crypto_stream_salsa20_xor_ic); | |
| 4055 | V_FUNCTION("crypto_stream_salsa20_xor_init", sn_crypto_stream_salsa20_xor_wrap_init); | |
| 4056 | V_FUNCTION("crypto_stream_salsa20_xor_update", sn_crypto_stream_salsa20_xor_wrap_update); | |
| 4057 | V_FUNCTION("crypto_stream_salsa20_xor_final", sn_crypto_stream_salsa20_xor_wrap_final); | |
| 4058 | V_UINT32("crypto_stream_salsa20_xor_STATEBYTES", sizeof(sn_crypto_stream_salsa20_xor_state)); | |
| 4059 | ||
| 4060 | // extensions | |
| 4061 | ||
| 4062 | // tweak | |
| 4063 | ||
| 4064 | V_FUNCTION("extension_tweak_ed25519_base", sn_extension_tweak_ed25519_base); | |
| 4065 | V_FUNCTION("extension_tweak_ed25519_sign_detached", sn_extension_tweak_ed25519_sign_detached); | |
| 4066 | V_FUNCTION("extension_tweak_ed25519_sk_to_scalar", sn_extension_tweak_ed25519_sk_to_scalar); | |
| 4067 | V_FUNCTION("extension_tweak_ed25519_scalar", sn_extension_tweak_ed25519_scalar); | |
| 4068 | V_FUNCTION("extension_tweak_ed25519_pk", sn_extension_tweak_ed25519_pk); | |
| 4069 | V_FUNCTION("extension_tweak_ed25519_keypair", sn_extension_tweak_ed25519_keypair); | |
| 4070 | V_FUNCTION("extension_tweak_ed25519_scalar_add", sn_extension_tweak_ed25519_scalar_add); | |
| 4071 | V_FUNCTION("extension_tweak_ed25519_pk_add", sn_extension_tweak_ed25519_pk_add); | |
| 4072 | V_FUNCTION("extension_tweak_ed25519_keypair_add", sn_extension_tweak_ed25519_keypair_add); | |
| 4073 | V_UINT32("extension_tweak_ed25519_BYTES", sn__extension_tweak_ed25519_BYTES); | |
| 4074 | V_UINT32("extension_tweak_ed25519_SCALARBYTES", sn__extension_tweak_ed25519_SCALARBYTES); | |
| 4075 | ||
| 4076 | // pbkdf2 | |
| 4077 | ||
| 4078 | V_FUNCTION("extension_pbkdf2_sha512", sn_extension_pbkdf2_sha512); | |
| 4079 | V_FUNCTION("extension_pbkdf2_sha512_async", sn_extension_pbkdf2_sha512_async); | |
| 4080 | V_UINT32("extension_pbkdf2_sha512_SALTBYTES", sn__extension_pbkdf2_sha512_SALTBYTES); | |
| 4081 | V_UINT32("extension_pbkdf2_sha512_HASHBYTES", sn__extension_pbkdf2_sha512_HASHBYTES); | |
| 4082 | V_UINT32("extension_pbkdf2_sha512_ITERATIONS_MIN", sn__extension_pbkdf2_sha512_ITERATIONS_MIN); | |
| 4083 | V_UINT64("extension_pbkdf2_sha512_BYTES_MAX", sn__extension_pbkdf2_sha512_BYTES_MAX); | |
| 4084 | ||
| 4085 | #undef V_FUNCTION | |
| 4086 | #undef V_FUNCTION_NOSCOPE | |
| 4087 | #undef V_UINT32 | |
| 4088 | #undef V_UINT64 | |
| 4089 | #undef V_STRING | |
| 4090 | ||
| 4091 | return exports; | |
| 4092 | } | |
| 4093 | ||
| 4094 | BARE_MODULE(sodium_native, sodium_native_exports) |
| … | … | @@ -0,0 +1,2 @@ |
| 1 | require.addon = require('require-addon') | |
| 2 | module.exports = require.addon('.', __filename) |
| … | … | @@ -0,0 +1,90 @@ |
| 1 | /*- | |
| 2 | * Copyright 2005,2007,2009 Colin Percival | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 24 | * SUCH DAMAGE. | |
| 25 | */ | |
| 26 | ||
| 27 | /* | |
| 28 | * Adapted from libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf-sha256.c | |
| 29 | */ | |
| 30 | ||
| 31 | #include <string.h> | |
| 32 | #include <sodium.h> | |
| 33 | ||
| 34 | #include "pbkdf2.h" | |
| 35 | ||
| 36 | /** | |
| 37 | * pbkdf2_sha512(passwd, passwdlen, salt, saltlen, c, buf, dkLen): | |
| 38 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and | |
| 39 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). | |
| 40 | */ | |
| 41 | int | |
| 42 | sn__extension_pbkdf2_sha512(const unsigned char *passwd, size_t passwdlen, | |
| 43 | const unsigned char *salt, size_t saltlen, uint64_t c, | |
| 44 | unsigned char *buf, size_t dkLen) | |
| 45 | { | |
| 46 | crypto_auth_hmacsha512_state PShctx, hctx; | |
| 47 | size_t i; | |
| 48 | unsigned char ivec[4]; | |
| 49 | unsigned char U[64]; | |
| 50 | unsigned char T[64]; | |
| 51 | uint64_t j; | |
| 52 | unsigned int k; | |
| 53 | size_t clen; | |
| 54 | ||
| 55 | if (dkLen > sn__extension_pbkdf2_sha512_BYTES_MAX) { | |
| 56 | return -1; | |
| 57 | } | |
| 58 | ||
| 59 | crypto_auth_hmacsha512_init(&PShctx, passwd, passwdlen); | |
| 60 | crypto_auth_hmacsha512_update(&PShctx, salt, saltlen); | |
| 61 | ||
| 62 | for (i = 0; i * crypto_auth_hmacsha512_BYTES < dkLen; i++) { | |
| 63 | SN_PBKDF2_STORE32_BE(ivec, (uint32_t)(i + 1)); | |
| 64 | memcpy(&hctx, &PShctx, sizeof(crypto_auth_hmacsha512_state)); | |
| 65 | crypto_auth_hmacsha512_update(&hctx, ivec, 4); | |
| 66 | crypto_auth_hmacsha512_final(&hctx, U); | |
| 67 | ||
| 68 | memcpy(T, U, crypto_auth_hmacsha512_BYTES); | |
| 69 | /* LCOV_EXCL_START */ | |
| 70 | for (j = 2; j <= c; j++) { | |
| 71 | crypto_auth_hmacsha512_init(&hctx, passwd, passwdlen); | |
| 72 | crypto_auth_hmacsha512_update(&hctx, U, crypto_auth_hmacsha512_BYTES); | |
| 73 | crypto_auth_hmacsha512_final(&hctx, U); | |
| 74 | ||
| 75 | for (k = 0; k < crypto_auth_hmacsha512_BYTES; k++) { | |
| 76 | T[k] ^= U[k]; | |
| 77 | } | |
| 78 | } | |
| 79 | /* LCOV_EXCL_STOP */ | |
| 80 | ||
| 81 | clen = dkLen - i * crypto_auth_hmacsha512_BYTES; | |
| 82 | if (clen > crypto_auth_hmacsha512_BYTES) { | |
| 83 | clen = crypto_auth_hmacsha512_BYTES; | |
| 84 | } | |
| 85 | memcpy(&buf[i * crypto_auth_hmacsha512_BYTES], T, clen); | |
| 86 | } | |
| 87 | sodium_memzero((void *) &PShctx, sizeof PShctx); | |
| 88 | ||
| 89 | return 0; | |
| 90 | } |
| … | … | @@ -0,0 +1,60 @@ |
| 1 | /*- | |
| 2 | * Copyright 2005,2007,2009 Colin Percival | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 24 | * SUCH DAMAGE. | |
| 25 | * | |
| 26 | */ | |
| 27 | ||
| 28 | /* | |
| 29 | * Adapted from libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf-sha256.c | |
| 30 | */ | |
| 31 | #ifdef __cplusplus | |
| 32 | extern "C" { | |
| 33 | #endif | |
| 34 | ||
| 35 | #include <sodium.h> | |
| 36 | ||
| 37 | #define SN_PBKDF2_STORE32_BE(buf, n32) \ | |
| 38 | buf[0] = n32 >> 24 & 0xff; \ | |
| 39 | buf[1] = n32 >> 16 & 0xff; \ | |
| 40 | buf[2] = n32 >> 8 & 0xff; \ | |
| 41 | buf[3] = n32 >> 0 & 0xff; | |
| 42 | ||
| 43 | #define sn__extension_pbkdf2_sha512_SALTBYTES 16U | |
| 44 | ||
| 45 | #define sn__extension_pbkdf2_sha512_HASHBYTES crypto_hash_sha512_BYTES | |
| 46 | ||
| 47 | #define sn__extension_pbkdf2_sha512_ITERATIONS_MIN 1U | |
| 48 | ||
| 49 | #define sn__extension_pbkdf2_sha512_BYTES_MAX 0x3fffffffc0ULL | |
| 50 | ||
| 51 | /** | |
| 52 | * extension_pbkdf2_sha512(passwd, passwdlen, salt, saltlen, c, buf, dkLen): | |
| 53 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and | |
| 54 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). | |
| 55 | */ | |
| 56 | int sn__extension_pbkdf2_sha512(const unsigned char *, size_t, const unsigned char *, size_t, | |
| 57 | uint64_t, unsigned char *, size_t); | |
| 58 | #ifdef __cplusplus | |
| 59 | }; | |
| 60 | #endif |
| … | … | @@ -0,0 +1,206 @@ |
| 1 | #include "tweak.h" | |
| 2 | ||
| 3 | /* | |
| 4 | *EXPERIMENTAL API* | |
| 5 | ||
| 6 | This module is an experimental implementation of a key tweaking protocol | |
| 7 | over ed25519 keys. The signature algorithm has been reimplemented from | |
| 8 | libsodium, but the nonce generation algorithm is *non-standard*. | |
| 9 | ||
| 10 | Use at your own risk | |
| 11 | */ | |
| 12 | ||
| 13 | static void _extension_tweak_nonce (unsigned char *nonce, const unsigned char *n, | |
| 14 | const unsigned char *m, unsigned long long mlen) | |
| 15 | { | |
| 16 | // dom2(x, y) with x = 0 (not prehashed) and y = "crypto_tweak_ed25519" | |
| 17 | static const unsigned char TWEAK_PREFIX[32 + 2 + 20] = { | |
| 18 | 'S', 'i', 'g', 'E', 'd', '2', '5', '5', '1', '9', ' ', | |
| 19 | 'n', 'o', ' ', 'E', 'd', '2', '5', '5', '1', '9', ' ', | |
| 20 | 'c', 'o', 'l', 'l', 'i', 's', 'i', 'o', 'n', 's', 0, | |
| 21 | 20, 'c', 'r', 'y', 'p', 't', 'o', '_', 't', 'w', 'e', | |
| 22 | 'a', 'k', '_', 'e', 'd', '2', '5', '5', '1', '9' | |
| 23 | }; | |
| 24 | ||
| 25 | crypto_hash_sha512_state hs; | |
| 26 | ||
| 27 | crypto_hash_sha512_init(&hs); | |
| 28 | crypto_hash_sha512_update(&hs, TWEAK_PREFIX, sizeof TWEAK_PREFIX); | |
| 29 | crypto_hash_sha512_update(&hs, n, 32); | |
| 30 | crypto_hash_sha512_update(&hs, m, mlen); | |
| 31 | crypto_hash_sha512_final(&hs, nonce); | |
| 32 | } | |
| 33 | ||
| 34 | static inline void | |
| 35 | _crypto_sign_ed25519_clamp(unsigned char k[32]) | |
| 36 | { | |
| 37 | k[0] &= 248; | |
| 38 | k[31] &= 127; | |
| 39 | k[31] |= 64; | |
| 40 | } | |
| 41 | ||
| 42 | static void _extension_tweak_ed25519(unsigned char *q, unsigned char *n, | |
| 43 | const unsigned char *ns, unsigned long long nslen) | |
| 44 | { | |
| 45 | sodium_memzero(q, sizeof q); | |
| 46 | ||
| 47 | crypto_hash(n, ns, nslen); | |
| 48 | n[31] &= 127; // clear highest bit | |
| 49 | ||
| 50 | crypto_scalarmult_ed25519_base_noclamp(q, n); | |
| 51 | ||
| 52 | // hash tweak until we get a valid tweaked q | |
| 53 | while (crypto_core_ed25519_is_valid_point(q) != 1) { | |
| 54 | crypto_hash(n, n, 32); | |
| 55 | n[31] &= 127; // clear highest bit | |
| 56 | ||
| 57 | crypto_scalarmult_ed25519_base_noclamp(q, n); | |
| 58 | } | |
| 59 | } | |
| 60 | ||
| 61 | void sn__extension_tweak_ed25519_base(unsigned char *pk, unsigned char *scalar, | |
| 62 | const unsigned char *ns, unsigned long long nslen) | |
| 63 | { | |
| 64 | unsigned char n64[64]; | |
| 65 | ||
| 66 | _extension_tweak_ed25519(pk, n64, ns, nslen); | |
| 67 | ||
| 68 | SN_TWEAK_COPY_32(scalar, n64) | |
| 69 | } | |
| 70 | ||
| 71 | int sn__extension_tweak_ed25519_sign_detached(unsigned char *sig, unsigned long long *siglen_p, | |
| 72 | const unsigned char *m, unsigned long long mlen, | |
| 73 | const unsigned char *n, unsigned char *pk) | |
| 74 | { | |
| 75 | crypto_hash_sha512_state hs; | |
| 76 | ||
| 77 | unsigned char nonce[64]; | |
| 78 | unsigned char R[32]; | |
| 79 | unsigned char hram[64]; | |
| 80 | unsigned char _pk[32]; | |
| 81 | ||
| 82 | // check if pk was passed | |
| 83 | if (pk == NULL) { | |
| 84 | pk = _pk; | |
| 85 | ||
| 86 | // derive pk from scalar | |
| 87 | if (crypto_scalarmult_ed25519_base_noclamp(pk, n) != 0) { | |
| 88 | return -1; | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | _extension_tweak_nonce(nonce, n, m, mlen); | |
| 93 | crypto_core_ed25519_scalar_reduce(nonce, nonce); | |
| 94 | ||
| 95 | // R = G ^ nonce : curve point from nonce | |
| 96 | if (crypto_scalarmult_ed25519_base_noclamp(R, nonce) != 0) { | |
| 97 | return -1; | |
| 98 | } | |
| 99 | ||
| 100 | // generate challenge as h(ram) = hash(R, pk, message) | |
| 101 | crypto_hash_sha512_init(&hs); | |
| 102 | crypto_hash_sha512_update(&hs, R, 32); | |
| 103 | crypto_hash_sha512_update(&hs, pk, 32); | |
| 104 | crypto_hash_sha512_update(&hs, m, mlen); | |
| 105 | ||
| 106 | crypto_hash_sha512_final(&hs, hram); | |
| 107 | ||
| 108 | crypto_core_ed25519_scalar_reduce(hram, hram); | |
| 109 | ||
| 110 | // sig = nonce + n * h(ram) | |
| 111 | crypto_core_ed25519_scalar_mul(sig, hram, n); | |
| 112 | crypto_core_ed25519_scalar_add(sig + 32, nonce, sig); | |
| 113 | ||
| 114 | SN_TWEAK_COPY_32(sig, R) | |
| 115 | ||
| 116 | if (siglen_p != NULL) { | |
| 117 | *siglen_p = 64U; | |
| 118 | } | |
| 119 | ||
| 120 | return 0; | |
| 121 | } | |
| 122 | ||
| 123 | // tweak a secret key | |
| 124 | void sn__extension_tweak_ed25519_sk_to_scalar(unsigned char *n, const unsigned char *sk) | |
| 125 | { | |
| 126 | unsigned char n64[64]; | |
| 127 | ||
| 128 | // get sk scalar from seed, cf. crypto_sign_keypair_seed | |
| 129 | crypto_hash(n64, sk, 32); | |
| 130 | _crypto_sign_ed25519_clamp(n64); | |
| 131 | ||
| 132 | SN_TWEAK_COPY_32(n, n64) | |
| 133 | } | |
| 134 | ||
| 135 | // tweak a secret key | |
| 136 | void sn__extension_tweak_ed25519_scalar(unsigned char *scalar_out, | |
| 137 | const unsigned char *scalar, | |
| 138 | const unsigned char *ns, | |
| 139 | unsigned long long nslen) | |
| 140 | { | |
| 141 | unsigned char n[64]; | |
| 142 | unsigned char q[32]; | |
| 143 | ||
| 144 | _extension_tweak_ed25519(q, n, ns, nslen); | |
| 145 | crypto_core_ed25519_scalar_add(scalar_out, scalar, n); | |
| 146 | } | |
| 147 | ||
| 148 | // tweak a public key | |
| 149 | int sn__extension_tweak_ed25519_pk(unsigned char *tpk, | |
| 150 | const unsigned char *pk, | |
| 151 | const unsigned char *ns, | |
| 152 | unsigned long long nslen) | |
| 153 | { | |
| 154 | unsigned char n[64]; | |
| 155 | unsigned char q[32]; | |
| 156 | ||
| 157 | _extension_tweak_ed25519(q, n, ns, nslen); | |
| 158 | return crypto_core_ed25519_add(tpk, q, pk); | |
| 159 | } | |
| 160 | ||
| 161 | ||
| 162 | void sn__extension_tweak_ed25519_keypair(unsigned char *pk, unsigned char *scalar_out, | |
| 163 | unsigned char *scalar, const unsigned char *ns, | |
| 164 | unsigned long long nslen) | |
| 165 | { | |
| 166 | unsigned char n64[64]; | |
| 167 | ||
| 168 | crypto_hash(n64, ns, nslen); | |
| 169 | n64[31] &= 127; // clear highest bit | |
| 170 | ||
| 171 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, n64); | |
| 172 | crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 173 | ||
| 174 | // hash tweak until we get a valid tweaked point | |
| 175 | while (crypto_core_ed25519_is_valid_point(pk) != 1) { | |
| 176 | crypto_hash(n64, n64, 32); | |
| 177 | n64[31] &= 127; // clear highest bit | |
| 178 | ||
| 179 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, n64); | |
| 180 | crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 181 | } | |
| 182 | } | |
| 183 | ||
| 184 | // add tweak to scalar | |
| 185 | void sn__extension_tweak_ed25519_scalar_add(unsigned char *scalar_out, | |
| 186 | const unsigned char *scalar, | |
| 187 | const unsigned char *n) | |
| 188 | { | |
| 189 | crypto_core_ed25519_scalar_add(scalar_out, scalar, n); | |
| 190 | } | |
| 191 | ||
| 192 | // add tweak point to public key | |
| 193 | int sn__extension_tweak_ed25519_pk_add(unsigned char *tpk, | |
| 194 | const unsigned char *pk, | |
| 195 | const unsigned char *q) | |
| 196 | { | |
| 197 | return crypto_core_ed25519_add(tpk, pk, q); | |
| 198 | } | |
| 199 | ||
| 200 | ||
| 201 | int sn__extension_tweak_ed25519_keypair_add(unsigned char *pk, unsigned char *scalar_out, | |
| 202 | unsigned char *scalar, const unsigned char *tweak) | |
| 203 | { | |
| 204 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, tweak); | |
| 205 | return crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 206 | } |
| … | … | @@ -0,0 +1,62 @@ |
| 1 | #ifdef __cplusplus | |
| 2 | extern "C" { | |
| 3 | #endif | |
| 4 | ||
| 5 | #include <sodium.h> | |
| 6 | ||
| 7 | // copy 32 bytes using int64_t pointers | |
| 8 | #define SN_TWEAK_COPY_32(a, b) \ | |
| 9 | { \ | |
| 10 | long long *dst = (long long *) a; \ | |
| 11 | long long *src = (long long *) b; \ | |
| 12 | dst[0] = src[0]; \ | |
| 13 | dst[1] = src[1]; \ | |
| 14 | dst[2] = src[2]; \ | |
| 15 | dst[3] = src[3]; \ | |
| 16 | } | |
| 17 | ||
| 18 | #define sn__extension_tweak_ed25519_BYTES crypto_sign_ed25519_PUBLICKEYBYTES | |
| 19 | ||
| 20 | #define sn__extension_tweak_ed25519_SCALARBYTES crypto_scalarmult_ed25519_SCALARBYTES | |
| 21 | ||
| 22 | int sn__extension_tweak_ed25519_sign_detached(unsigned char *sig, unsigned long long *siglen_p, | |
| 23 | const unsigned char *m, unsigned long long mlen, | |
| 24 | const unsigned char *n, unsigned char *pk); | |
| 25 | ||
| 26 | void sn__extension_tweak_ed25519_base(unsigned char *pk, unsigned char *scalar, | |
| 27 | const unsigned char *ns, unsigned long long nslen); | |
| 28 | ||
| 29 | void sn__extension_tweak_ed25519_sk_to_scalar(unsigned char *scalar, const unsigned char *sk); | |
| 30 | ||
| 31 | // tweak a secret key | |
| 32 | void sn__extension_tweak_ed25519_scalar(unsigned char *scalar_out, | |
| 33 | const unsigned char *scalar, | |
| 34 | const unsigned char *ns, | |
| 35 | unsigned long long nslen); | |
| 36 | ||
| 37 | // tweak a public key | |
| 38 | int sn__extension_tweak_ed25519_pk(unsigned char *tpk, | |
| 39 | const unsigned char *pk, | |
| 40 | const unsigned char *ns, | |
| 41 | unsigned long long nslen); | |
| 42 | ||
| 43 | void sn__extension_tweak_ed25519_keypair(unsigned char *pk, unsigned char *scalar_out, | |
| 44 | unsigned char *scalar, const unsigned char *ns, | |
| 45 | unsigned long long nslen); | |
| 46 | ||
| 47 | // add tweak scalar to private key | |
| 48 | void sn__extension_tweak_ed25519_scalar_add(unsigned char *scalar_out, | |
| 49 | const unsigned char *scalar, | |
| 50 | const unsigned char *n); | |
| 51 | ||
| 52 | // add tweak point to public key | |
| 53 | int sn__extension_tweak_ed25519_pk_add(unsigned char *tpk, | |
| 54 | const unsigned char *pk, | |
| 55 | const unsigned char *q); | |
| 56 | ||
| 57 | int sn__extension_tweak_ed25519_keypair_add(unsigned char *pk, unsigned char *scalar_out, | |
| 58 | unsigned char *scalar, const unsigned char *tweak); | |
| 59 | ||
| 60 | #ifdef __cplusplus | |
| 61 | }; | |
| 62 | #endif |
| … | … | @@ -0,0 +1,3231 @@ |
| 1 | const assert = require('assert') | |
| 2 | const binding = require('./binding') | |
| 3 | const { isNode } = require('which-runtime') | |
| 4 | ||
| 5 | const OPTIONAL = Buffer.from(new ArrayBuffer(0)) | |
| 6 | ||
| 7 | module.exports = exports = { ...binding } | |
| 8 | ||
| 9 | // memory | |
| 10 | ||
| 11 | exports.sodium_memzero = function (buf) { | |
| 12 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 13 | ||
| 14 | binding.sodium_memzero(buf) | |
| 15 | } | |
| 16 | ||
| 17 | exports.sodium_mlock = function (buf) { | |
| 18 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 19 | ||
| 20 | const res = binding.sodium_mlock(buf) | |
| 21 | ||
| 22 | if (res !== 0) throw new Error('memory lock failed') | |
| 23 | } | |
| 24 | ||
| 25 | exports.sodium_munlock = function (buf) { | |
| 26 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 27 | ||
| 28 | const res = binding.sodium_munlock(buf) | |
| 29 | ||
| 30 | if (res !== 0) throw new Error('memory unlock failed') | |
| 31 | } | |
| 32 | ||
| 33 | exports.sodium_malloc = function (size) { | |
| 34 | assert(size >= 0, 'invalid size') | |
| 35 | ||
| 36 | const buf = Buffer.from(binding.sodium_malloc(size)) | |
| 37 | buf.secure = true | |
| 38 | return buf | |
| 39 | } | |
| 40 | ||
| 41 | exports.sodium_free = function (buf) { | |
| 42 | if (!buf || !buf.secure) return | |
| 43 | ||
| 44 | binding.sodium_free(buf.buffer) | |
| 45 | } | |
| 46 | ||
| 47 | exports.sodium_mprotect_noaccess = function (buf) { | |
| 48 | const res = binding.sodium_mprotect_noaccess(buf.buffer) | |
| 49 | ||
| 50 | if (res !== 0) throw new Error('failed to lock buffer') | |
| 51 | } | |
| 52 | ||
| 53 | exports.sodium_mprotect_readonly = function (buf) { | |
| 54 | const res = binding.sodium_mprotect_readonly(buf.buffer) | |
| 55 | ||
| 56 | if (res !== 0) throw new Error('failed to unlock buffer') | |
| 57 | } | |
| 58 | ||
| 59 | exports.sodium_mprotect_readwrite = function (buf) { | |
| 60 | const res = binding.sodium_mprotect_readwrite(buf.buffer) | |
| 61 | ||
| 62 | if (res !== 0) throw new Error('failed to unlock buffer') | |
| 63 | } | |
| 64 | ||
| 65 | // crypto_randombytes | |
| 66 | ||
| 67 | exports.randombytes_buf = function (buffer) { | |
| 68 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 69 | ||
| 70 | binding.randombytes_buf(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 71 | } | |
| 72 | ||
| 73 | exports.randombytes_buf_deterministic = function (buffer, seed) { | |
| 74 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 75 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 76 | assert( | |
| 77 | seed.byteLength === binding.randombytes_SEEDBYTES, | |
| 78 | "seed must be 'randombytes_SEEDBYTES' bytes" | |
| 79 | ) | |
| 80 | ||
| 81 | binding.randombytes_buf_deterministic( | |
| 82 | buffer.buffer, | |
| 83 | buffer.byteOffset, | |
| 84 | buffer.byteLength, | |
| 85 | ||
| 86 | seed.buffer, | |
| 87 | seed.byteOffset, | |
| 88 | seed.byteLength | |
| 89 | ) | |
| 90 | } | |
| 91 | ||
| 92 | // sodium_helpers | |
| 93 | ||
| 94 | exports.sodium_memcmp = function (a, b) { | |
| 95 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 96 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 97 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 98 | ||
| 99 | return binding.sodium_memcmp(a, b) | |
| 100 | } | |
| 101 | ||
| 102 | exports.sodium_add = function (a, b) { | |
| 103 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 104 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 105 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 106 | ||
| 107 | binding.sodium_add(a, b) | |
| 108 | } | |
| 109 | ||
| 110 | exports.sodium_sub = function (a, b) { | |
| 111 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 112 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 113 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 114 | ||
| 115 | binding.sodium_sub(a, b) | |
| 116 | } | |
| 117 | ||
| 118 | exports.sodium_compare = function (a, b) { | |
| 119 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 120 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 121 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 122 | ||
| 123 | return binding.sodium_compare(a, b) | |
| 124 | } | |
| 125 | ||
| 126 | exports.sodium_is_zero = function (buffer, length) { | |
| 127 | if (length === undefined) length = buffer.byteLength | |
| 128 | ||
| 129 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 130 | assert(length >= 0 && length <= buffer.byteLength, 'invalid length') | |
| 131 | ||
| 132 | return binding.sodium_is_zero(buffer, length) | |
| 133 | } | |
| 134 | ||
| 135 | exports.sodium_pad = function (buffer, unpaddedBuflen, blockSize) { | |
| 136 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 137 | assert(unpaddedBuflen <= buffer.byteLength, 'unpadded length cannot exceed buffer length') | |
| 138 | assert(blockSize <= buffer.byteLength, 'block size cannot exceed buffer length') | |
| 139 | assert(blockSize >= 1, 'block size must be at least 1 byte') | |
| 140 | assert( | |
| 141 | buffer.byteLength >= unpaddedBuflen + (blockSize - (unpaddedBuflen % blockSize)), | |
| 142 | 'buf not long enough' | |
| 143 | ) | |
| 144 | ||
| 145 | return binding.sodium_pad(buffer, unpaddedBuflen, blockSize) | |
| 146 | } | |
| 147 | ||
| 148 | exports.sodium_unpad = function (buffer, paddedBuflen, blockSize) { | |
| 149 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 150 | assert(paddedBuflen <= buffer.byteLength, 'unpadded length cannot exceed buffer length') | |
| 151 | assert(blockSize <= buffer.byteLength, 'block size cannot exceed buffer length') | |
| 152 | assert(blockSize >= 1, 'block size must be at least 1 byte') | |
| 153 | ||
| 154 | return binding.sodium_unpad(buffer, paddedBuflen, blockSize) | |
| 155 | } | |
| 156 | ||
| 157 | // crypto_sign | |
| 158 | ||
| 159 | exports.crypto_sign_keypair = function (pk, sk) { | |
| 160 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 161 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 162 | assert( | |
| 163 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 164 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 165 | ) | |
| 166 | assert( | |
| 167 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 168 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 169 | ) | |
| 170 | ||
| 171 | const res = binding.crypto_sign_keypair(pk, sk) | |
| 172 | ||
| 173 | if (res !== 0) throw new Error('status: ' + res) | |
| 174 | } | |
| 175 | ||
| 176 | exports.crypto_sign_seed_keypair = function (pk, sk, seed) { | |
| 177 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 178 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 179 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 180 | assert( | |
| 181 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 182 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 183 | ) | |
| 184 | assert( | |
| 185 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 186 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 187 | ) | |
| 188 | assert( | |
| 189 | seed.byteLength === binding.crypto_sign_SEEDBYTES, | |
| 190 | "seed must be 'crypto_sign_SEEDBYTES' bytes" | |
| 191 | ) | |
| 192 | ||
| 193 | const res = binding.crypto_sign_seed_keypair(pk, sk, seed) | |
| 194 | ||
| 195 | if (res !== 0) throw new Error('status: ' + res) | |
| 196 | } | |
| 197 | ||
| 198 | exports.crypto_sign = function (sm, m, sk) { | |
| 199 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 200 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 201 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 202 | assert( | |
| 203 | sm.byteLength === binding.crypto_sign_BYTES + m.byteLength, | |
| 204 | "sm must be 'm.byteLength + crypto_sign_BYTES' bytes" | |
| 205 | ) | |
| 206 | assert( | |
| 207 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 208 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 209 | ) | |
| 210 | ||
| 211 | const res = binding.crypto_sign(sm, m, sk) | |
| 212 | ||
| 213 | if (res !== 0) throw new Error('status: ' + res) | |
| 214 | } | |
| 215 | ||
| 216 | exports.crypto_sign_open = function (m, sm, pk) { | |
| 217 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 218 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 219 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 220 | assert( | |
| 221 | sm.byteLength >= binding.crypto_sign_BYTES, | |
| 222 | "sm must be at least 'crypto_sign_BYTES' bytes" | |
| 223 | ) | |
| 224 | assert( | |
| 225 | m.byteLength === sm.byteLength - binding.crypto_sign_BYTES, | |
| 226 | "m must be 'sm.byteLength - crypto_sign_BYTES' bytes" | |
| 227 | ) | |
| 228 | assert( | |
| 229 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 230 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 231 | ) | |
| 232 | ||
| 233 | const res = binding.crypto_sign_open(m, sm, pk) | |
| 234 | ||
| 235 | if (res !== 0) throw new Error('status: ' + res) | |
| 236 | } | |
| 237 | ||
| 238 | exports.crypto_sign_open = function (m, sm, pk) { | |
| 239 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 240 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 241 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 242 | assert( | |
| 243 | sm.byteLength >= binding.crypto_sign_BYTES, | |
| 244 | "sm must be at least 'crypto_sign_BYTES' bytes" | |
| 245 | ) | |
| 246 | assert( | |
| 247 | m.byteLength === sm.byteLength - binding.crypto_sign_BYTES, | |
| 248 | "m must be 'sm.byteLength - crypto_sign_BYTES' bytes" | |
| 249 | ) | |
| 250 | assert( | |
| 251 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 252 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 253 | ) | |
| 254 | ||
| 255 | return binding.crypto_sign_open(m, sm, pk) | |
| 256 | } | |
| 257 | ||
| 258 | exports.crypto_sign_detached = function (sig, m, sk) { | |
| 259 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 260 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 261 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 262 | assert(sig.byteLength === binding.crypto_sign_BYTES, "sig must be 'crypto_sign_BYTES' bytes") | |
| 263 | assert( | |
| 264 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 265 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 266 | ) | |
| 267 | ||
| 268 | const res = binding.crypto_sign_detached(sig, m, sk) | |
| 269 | ||
| 270 | if (res !== 0) throw new Error('status: ' + res) | |
| 271 | } | |
| 272 | ||
| 273 | exports.crypto_sign_verify_detached = function (sig, m, pk) { | |
| 274 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 275 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 276 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 277 | assert( | |
| 278 | sig.byteLength >= binding.crypto_sign_BYTES, | |
| 279 | "sig must be at least 'crypto_sign_BYTES' bytes" | |
| 280 | ) | |
| 281 | assert( | |
| 282 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 283 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 284 | ) | |
| 285 | ||
| 286 | return binding.crypto_sign_verify_detached( | |
| 287 | sig.buffer, | |
| 288 | sig.byteOffset, | |
| 289 | sig.byteLength, | |
| 290 | ||
| 291 | m.buffer, | |
| 292 | m.byteOffset, | |
| 293 | m.byteLength, | |
| 294 | ||
| 295 | pk.buffer, | |
| 296 | pk.byteOffset, | |
| 297 | pk.byteLength | |
| 298 | ) | |
| 299 | } | |
| 300 | ||
| 301 | exports.crypto_sign_ed25519_sk_to_pk = function (pk, sk) { | |
| 302 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 303 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 304 | assert( | |
| 305 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 306 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 307 | ) | |
| 308 | assert( | |
| 309 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 310 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 311 | ) | |
| 312 | ||
| 313 | const res = binding.crypto_sign_ed25519_sk_to_pk(pk, sk) | |
| 314 | ||
| 315 | if (res !== 0) throw new Error('status: ' + res) | |
| 316 | } | |
| 317 | ||
| 318 | exports.crypto_sign_ed25519_pk_to_curve25519 = function (x25519pk, ed25519pk) { | |
| 319 | assert(ArrayBuffer.isView(x25519pk), 'x25519pk must be a typed array') | |
| 320 | assert(ArrayBuffer.isView(ed25519pk), 'ed25519pk must be a typed array') | |
| 321 | assert( | |
| 322 | x25519pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 323 | "x25519pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 324 | ) | |
| 325 | assert( | |
| 326 | ed25519pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 327 | "ed25519pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 328 | ) | |
| 329 | ||
| 330 | const res = binding.crypto_sign_ed25519_pk_to_curve25519(x25519pk, ed25519pk) | |
| 331 | ||
| 332 | if (res !== 0) throw new Error('status: ' + res) | |
| 333 | } | |
| 334 | ||
| 335 | exports.crypto_sign_ed25519_sk_to_curve25519 = function (x25519sk, ed25519sk) { | |
| 336 | assert(ArrayBuffer.isView(x25519sk), 'x25519sk must be a typed array') | |
| 337 | assert(ArrayBuffer.isView(ed25519sk), 'ed25519sk must be a typed array') | |
| 338 | assert( | |
| 339 | x25519sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 340 | "x25519sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 341 | ) | |
| 342 | ||
| 343 | const edLen = ed25519sk.byteLength | |
| 344 | ||
| 345 | assert( | |
| 346 | edLen === binding.crypto_sign_SECRETKEYBYTES || edLen === binding.crypto_box_SECRETKEYBYTES, | |
| 347 | "ed25519sk must be 'crypto_sign_SECRETKEYBYTES' or 'crypto_sign_SECRETKEYBYTES - crypto_sign_PUBLICKEYBYTES' bytes" | |
| 348 | ) | |
| 349 | ||
| 350 | const res = binding.crypto_sign_ed25519_sk_to_curve25519(x25519sk, ed25519sk) | |
| 351 | ||
| 352 | if (res !== 0) throw new Error('status: ' + res) | |
| 353 | } | |
| 354 | ||
| 355 | // crypto_box | |
| 356 | ||
| 357 | exports.crypto_box_keypair = function (pk, sk) { | |
| 358 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 359 | assert( | |
| 360 | pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 361 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 362 | ) | |
| 363 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 364 | assert( | |
| 365 | sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 366 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 367 | ) | |
| 368 | ||
| 369 | const res = binding.crypto_box_keypair(pk, sk) | |
| 370 | ||
| 371 | if (res !== 0) throw new Error('status: ' + res) | |
| 372 | } | |
| 373 | ||
| 374 | exports.crypto_box_seed_keypair = function (pk, sk, seed) { | |
| 375 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 376 | assert( | |
| 377 | pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 378 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 379 | ) | |
| 380 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 381 | assert( | |
| 382 | sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 383 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 384 | ) | |
| 385 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 386 | assert( | |
| 387 | seed.byteLength === binding.crypto_box_SEEDBYTES, | |
| 388 | "seed must be 'crypto_box_SEEDBYTES' bytes" | |
| 389 | ) | |
| 390 | ||
| 391 | const res = binding.crypto_box_seed_keypair(pk, sk, seed) | |
| 392 | ||
| 393 | if (res !== 0) throw new Error('status: ' + res) | |
| 394 | } | |
| 395 | ||
| 396 | exports.crypto_box_easy = function (c, m, n, pk, sk) { | |
| 397 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 398 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 399 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 400 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 401 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 402 | assert( | |
| 403 | c.byteLength === m.byteLength + exports.crypto_box_MACBYTES, | |
| 404 | "c must be 'm.byteLength + crypto_box_MACBYTES' bytes" | |
| 405 | ) | |
| 406 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 407 | assert( | |
| 408 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 409 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 410 | ) | |
| 411 | assert( | |
| 412 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 413 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 414 | ) | |
| 415 | ||
| 416 | const res = binding.crypto_box_easy(c, m, n, pk, sk) | |
| 417 | ||
| 418 | if (res !== 0) throw new Error('status: ' + res) | |
| 419 | } | |
| 420 | ||
| 421 | exports.crypto_box_detached = function (c, mac, m, n, pk, sk) { | |
| 422 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 423 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 424 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 425 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 426 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 427 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 428 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 429 | assert(mac.byteLength === exports.crypto_box_MACBYTES, "mac must be 'crypto_box_MACBYTES' bytes") | |
| 430 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 431 | assert( | |
| 432 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 433 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 434 | ) | |
| 435 | assert( | |
| 436 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 437 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 438 | ) | |
| 439 | ||
| 440 | const res = binding.crypto_box_detached(c, mac, m, n, pk, sk) | |
| 441 | ||
| 442 | if (res !== 0) throw new Error('status: ' + res) | |
| 443 | } | |
| 444 | ||
| 445 | exports.crypto_box_open_easy = function (m, c, n, pk, sk) { | |
| 446 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 447 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 448 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 449 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 450 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 451 | assert( | |
| 452 | c.byteLength >= exports.crypto_box_MACBYTES, | |
| 453 | "c must be at least 'crypto_box_MACBYTES' bytes" | |
| 454 | ) | |
| 455 | assert( | |
| 456 | m.byteLength === c.byteLength - exports.crypto_box_MACBYTES, | |
| 457 | "m must be 'c.byteLength - crypto_box_MACBYTES' bytes" | |
| 458 | ) | |
| 459 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 460 | assert( | |
| 461 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 462 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 463 | ) | |
| 464 | assert( | |
| 465 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 466 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 467 | ) | |
| 468 | ||
| 469 | return binding.crypto_box_open_easy(m, c, n, pk, sk) | |
| 470 | } | |
| 471 | ||
| 472 | exports.crypto_box_open_detached = function (m, c, mac, n, pk, sk) { | |
| 473 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 474 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 475 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 476 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 477 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 478 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 479 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 480 | assert(mac.byteLength === exports.crypto_box_MACBYTES, "mac must be 'crypto_box_MACBYTES' bytes") | |
| 481 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 482 | assert( | |
| 483 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 484 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 485 | ) | |
| 486 | assert( | |
| 487 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 488 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 489 | ) | |
| 490 | ||
| 491 | return binding.crypto_box_open_detached(m, c, mac, n, pk, sk) | |
| 492 | } | |
| 493 | ||
| 494 | exports.crypto_box_seal = function (c, m, pk) { | |
| 495 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 496 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 497 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 498 | assert( | |
| 499 | c.byteLength === m.byteLength + exports.crypto_box_SEALBYTES, | |
| 500 | "c must be 'm.byteLength + crypto_box_SEALBYTES' bytes" | |
| 501 | ) | |
| 502 | assert( | |
| 503 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 504 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 505 | ) | |
| 506 | ||
| 507 | const res = binding.crypto_box_seal(c, m, pk) | |
| 508 | ||
| 509 | if (res !== 0) throw new Error('status: ' + res) | |
| 510 | } | |
| 511 | ||
| 512 | exports.crypto_box_seal_open = function (m, c, pk, sk) { | |
| 513 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 514 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 515 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 516 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 517 | assert( | |
| 518 | c.byteLength >= exports.crypto_box_SEALBYTES, | |
| 519 | "c must be at least 'crypto_box_SEALBYTES' bytes" | |
| 520 | ) | |
| 521 | assert( | |
| 522 | m.byteLength === c.byteLength - exports.crypto_box_SEALBYTES, | |
| 523 | "m must be 'c.byteLength - crypto_box_SEALBYTES' bytes" | |
| 524 | ) | |
| 525 | assert( | |
| 526 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 527 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 528 | ) | |
| 529 | assert( | |
| 530 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 531 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 532 | ) | |
| 533 | ||
| 534 | return binding.crypto_box_seal_open( | |
| 535 | m.buffer, | |
| 536 | m.byteOffset, | |
| 537 | m.byteLength, | |
| 538 | ||
| 539 | c.buffer, | |
| 540 | c.byteOffset, | |
| 541 | c.byteLength, | |
| 542 | ||
| 543 | pk.buffer, | |
| 544 | pk.byteOffset, | |
| 545 | pk.byteLength, | |
| 546 | ||
| 547 | sk.buffer, | |
| 548 | sk.byteOffset, | |
| 549 | sk.byteLength | |
| 550 | ) | |
| 551 | } | |
| 552 | ||
| 553 | // crypto_secretbox | |
| 554 | ||
| 555 | exports.crypto_secretbox_easy = function (c, m, n, k) { | |
| 556 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 557 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 558 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 559 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 560 | assert( | |
| 561 | c.byteLength === m.byteLength + binding.crypto_secretbox_MACBYTES, | |
| 562 | "c must be 'm.byteLength + crypto_secretbox_MACBYTES' bytes" | |
| 563 | ) | |
| 564 | assert( | |
| 565 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 566 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 567 | ) | |
| 568 | assert( | |
| 569 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 570 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 571 | ) | |
| 572 | ||
| 573 | const res = binding.crypto_secretbox_easy(c, m, n, k) | |
| 574 | ||
| 575 | if (res !== 0) throw new Error('status: ' + res) | |
| 576 | } | |
| 577 | ||
| 578 | exports.crypto_secretbox_open_easy = function (m, c, n, k) { | |
| 579 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 580 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 581 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 582 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 583 | assert( | |
| 584 | c.byteLength >= binding.crypto_secretbox_MACBYTES, | |
| 585 | "c must be at least 'crypto_secretbox_MACBYTES' bytes" | |
| 586 | ) | |
| 587 | assert( | |
| 588 | m.byteLength === c.byteLength - binding.crypto_secretbox_MACBYTES, | |
| 589 | "m must be 'c.byteLength - crypto_secretbox_MACBYTES' bytes" | |
| 590 | ) | |
| 591 | assert( | |
| 592 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 593 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 594 | ) | |
| 595 | assert( | |
| 596 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 597 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 598 | ) | |
| 599 | ||
| 600 | return binding.crypto_secretbox_open_easy(m, c, n, k) | |
| 601 | } | |
| 602 | ||
| 603 | exports.crypto_secretbox_detached = function (c, mac, m, n, k) { | |
| 604 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 605 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 606 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 607 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 608 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 609 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 610 | assert( | |
| 611 | mac.byteLength === binding.crypto_secretbox_MACBYTES, | |
| 612 | "mac must be 'crypto_secretbox_MACBYTES' bytes" | |
| 613 | ) | |
| 614 | assert( | |
| 615 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 616 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 617 | ) | |
| 618 | assert( | |
| 619 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 620 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 621 | ) | |
| 622 | ||
| 623 | const res = binding.crypto_secretbox_detached(c, mac, m, n, k) | |
| 624 | ||
| 625 | if (res !== 0) throw new Error('status: ' + res) | |
| 626 | } | |
| 627 | ||
| 628 | exports.crypto_secretbox_open_detached = function (m, c, mac, n, k) { | |
| 629 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 630 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 631 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 632 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 633 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 634 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 635 | assert( | |
| 636 | mac.byteLength === binding.crypto_secretbox_MACBYTES, | |
| 637 | "mac must be 'crypto_secretbox_MACBYTES' bytes" | |
| 638 | ) | |
| 639 | assert( | |
| 640 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 641 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 642 | ) | |
| 643 | assert( | |
| 644 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 645 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 646 | ) | |
| 647 | ||
| 648 | return binding.crypto_secretbox_open_detached(m, c, mac, n, k) | |
| 649 | } | |
| 650 | ||
| 651 | // crypto_generichash | |
| 652 | ||
| 653 | exports.crypto_generichash = function (output, input, key) { | |
| 654 | if (!key) key = OPTIONAL | |
| 655 | ||
| 656 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 657 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 658 | assert( | |
| 659 | output.byteLength >= binding.crypto_generichash_BYTES_MIN && | |
| 660 | output.byteLength <= binding.crypto_generichash_BYTES_MAX, | |
| 661 | 'output must be between crypto_generichash_BYTES_MIN and crypto_generichash_BYTES_MAX bytes' | |
| 662 | ) | |
| 663 | ||
| 664 | if (key !== OPTIONAL) { | |
| 665 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 666 | assert( | |
| 667 | key.byteLength >= binding.crypto_generichash_KEYBYTES_MIN && | |
| 668 | key.byteLength <= binding.crypto_generichash_KEYBYTES_MAX, | |
| 669 | 'key must be between crypto_generichash_KEYBYTES_MIN and crypto_generichash_KEYBYTES_MAX bytes' | |
| 670 | ) | |
| 671 | } | |
| 672 | ||
| 673 | const res = binding.crypto_generichash( | |
| 674 | output.buffer, | |
| 675 | output.byteOffset, | |
| 676 | output.byteLength, | |
| 677 | ||
| 678 | input.buffer, | |
| 679 | input.byteOffset, | |
| 680 | input.byteLength, | |
| 681 | ||
| 682 | key.buffer, | |
| 683 | key.byteOffset, | |
| 684 | key.byteLength | |
| 685 | ) | |
| 686 | ||
| 687 | if (res !== 0) throw new Error('status: ' + res) | |
| 688 | } | |
| 689 | ||
| 690 | exports.crypto_generichash_batch = function (output, batch, key) { | |
| 691 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 692 | ||
| 693 | if (isNode || batch.length < 4) { | |
| 694 | const res = binding.crypto_generichash_batch(output, batch, !!key, key || OPTIONAL) | |
| 695 | if (res !== 0) throw new Error('status: ' + res) | |
| 696 | } else { | |
| 697 | const state = Buffer.alloc(binding.crypto_generichash_STATEBYTES) | |
| 698 | ||
| 699 | exports.crypto_generichash_init(state, key, output.byteLength) | |
| 700 | ||
| 701 | for (const buf of batch) { | |
| 702 | exports.crypto_generichash_update(state, buf) | |
| 703 | } | |
| 704 | ||
| 705 | exports.crypto_generichash_final(state, output) | |
| 706 | } | |
| 707 | } | |
| 708 | ||
| 709 | exports.crypto_generichash_keygen = function (key) { | |
| 710 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 711 | assert( | |
| 712 | key.byteLength === binding.crypto_generichash_KEYBYTES, | |
| 713 | "key must be 'crypto_generichash_KEYBYTES' bytes" | |
| 714 | ) | |
| 715 | ||
| 716 | const res = binding.crypto_generichash_keygen(key.buffer, key.byteOffset, key.byteLength) | |
| 717 | ||
| 718 | if (res !== 0) throw new Error('status: ' + res) | |
| 719 | } | |
| 720 | ||
| 721 | exports.crypto_generichash_init = function (state, key, outputLength) { | |
| 722 | if (!key) key = OPTIONAL | |
| 723 | ||
| 724 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 725 | assert( | |
| 726 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 727 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 728 | ) | |
| 729 | ||
| 730 | const res = binding.crypto_generichash_init( | |
| 731 | state.buffer, | |
| 732 | state.byteOffset, | |
| 733 | state.byteLength, | |
| 734 | ||
| 735 | key.buffer, | |
| 736 | key.byteOffset, | |
| 737 | key.byteLength, | |
| 738 | ||
| 739 | outputLength | |
| 740 | ) | |
| 741 | ||
| 742 | if (res !== 0) throw new Error('status: ' + res) | |
| 743 | } | |
| 744 | ||
| 745 | exports.crypto_generichash_update = function (state, input) { | |
| 746 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 747 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 748 | assert( | |
| 749 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 750 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 751 | ) | |
| 752 | ||
| 753 | const res = binding.crypto_generichash_update( | |
| 754 | state.buffer, | |
| 755 | state.byteOffset, | |
| 756 | state.byteLength, | |
| 757 | ||
| 758 | input.buffer, | |
| 759 | input.byteOffset, | |
| 760 | input.byteLength | |
| 761 | ) | |
| 762 | ||
| 763 | if (res !== 0) throw new Error('status: ' + res) | |
| 764 | } | |
| 765 | ||
| 766 | exports.crypto_generichash_final = function (state, output) { | |
| 767 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 768 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 769 | assert( | |
| 770 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 771 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 772 | ) | |
| 773 | ||
| 774 | const res = binding.crypto_generichash_final( | |
| 775 | state.buffer, | |
| 776 | state.byteOffset, | |
| 777 | state.byteLength, | |
| 778 | ||
| 779 | output.buffer, | |
| 780 | output.byteOffset, | |
| 781 | output.byteLength | |
| 782 | ) | |
| 783 | ||
| 784 | if (res !== 0) throw new Error('status: ' + res) | |
| 785 | } | |
| 786 | ||
| 787 | // secretstream | |
| 788 | ||
| 789 | exports.crypto_secretstream_xchacha20poly1305_keygen = function (k) { | |
| 790 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 791 | assert( | |
| 792 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 793 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 794 | ) | |
| 795 | ||
| 796 | binding.crypto_secretstream_xchacha20poly1305_keygen(k.buffer, k.byteOffset, k.byteLength) | |
| 797 | } | |
| 798 | ||
| 799 | exports.crypto_secretstream_xchacha20poly1305_init_push = function (state, header, k) { | |
| 800 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 801 | assert(ArrayBuffer.isView(header), 'header must be a typed array') | |
| 802 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 803 | assert( | |
| 804 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 805 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 806 | ) | |
| 807 | assert( | |
| 808 | header.byteLength === binding.crypto_secretstream_xchacha20poly1305_HEADERBYTES, | |
| 809 | "header must be 'crypto_secretstream_xchacha20poly1305_HEADERBYTES' bytes" | |
| 810 | ) | |
| 811 | assert( | |
| 812 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 813 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 814 | ) | |
| 815 | ||
| 816 | const res = binding.crypto_secretstream_xchacha20poly1305_init_push( | |
| 817 | state.buffer, | |
| 818 | state.byteOffset, | |
| 819 | state.byteLength, | |
| 820 | ||
| 821 | header.buffer, | |
| 822 | header.byteOffset, | |
| 823 | header.byteLength, | |
| 824 | ||
| 825 | k.buffer, | |
| 826 | k.byteOffset, | |
| 827 | k.byteLength | |
| 828 | ) | |
| 829 | ||
| 830 | if (res !== 0) throw new Error('status: ' + res) | |
| 831 | } | |
| 832 | ||
| 833 | exports.crypto_secretstream_xchacha20poly1305_init_pull = function (state, header, k) { | |
| 834 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 835 | assert(ArrayBuffer.isView(header), 'header must be a typed array') | |
| 836 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 837 | assert( | |
| 838 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 839 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 840 | ) | |
| 841 | assert( | |
| 842 | header.byteLength === binding.crypto_secretstream_xchacha20poly1305_HEADERBYTES, | |
| 843 | "header must be 'crypto_secretstream_xchacha20poly1305_HEADERBYTES' bytes" | |
| 844 | ) | |
| 845 | assert( | |
| 846 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 847 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 848 | ) | |
| 849 | ||
| 850 | const res = binding.crypto_secretstream_xchacha20poly1305_init_pull( | |
| 851 | state.buffer, | |
| 852 | state.byteOffset, | |
| 853 | state.byteLength, | |
| 854 | ||
| 855 | header.buffer, | |
| 856 | header.byteOffset, | |
| 857 | header.byteLength, | |
| 858 | ||
| 859 | k.buffer, | |
| 860 | k.byteOffset, | |
| 861 | k.byteLength | |
| 862 | ) | |
| 863 | ||
| 864 | if (res !== 0) throw new Error('status: ' + res) | |
| 865 | } | |
| 866 | ||
| 867 | exports.crypto_secretstream_xchacha20poly1305_push = function (state, c, m, ad, tag) { | |
| 868 | if (!ad) ad = OPTIONAL | |
| 869 | ||
| 870 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 871 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 872 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 873 | assert( | |
| 874 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 875 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 876 | ) | |
| 877 | assert( | |
| 878 | c.byteLength === m.byteLength + binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 879 | "c must be 'm.byteLength + crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 880 | ) | |
| 881 | ||
| 882 | const res = binding.crypto_secretstream_xchacha20poly1305_push( | |
| 883 | state.buffer, | |
| 884 | state.byteOffset, | |
| 885 | state.byteLength, | |
| 886 | ||
| 887 | c.buffer, | |
| 888 | c.byteOffset, | |
| 889 | c.byteLength, | |
| 890 | ||
| 891 | m.buffer, | |
| 892 | m.byteOffset, | |
| 893 | m.byteLength, | |
| 894 | ||
| 895 | ad.buffer, | |
| 896 | ad.byteOffset, | |
| 897 | ad.byteLength, | |
| 898 | ||
| 899 | tag | |
| 900 | ) | |
| 901 | ||
| 902 | if (res < 0) throw new Error('push failed') | |
| 903 | ||
| 904 | return res | |
| 905 | } | |
| 906 | ||
| 907 | exports.crypto_secretstream_xchacha20poly1305_pull = function (state, m, tag, c, ad) { | |
| 908 | if (!ad) ad = OPTIONAL | |
| 909 | ||
| 910 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 911 | assert( | |
| 912 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 913 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 914 | ) | |
| 915 | assert(ArrayBuffer.isView(tag), 'tag must be a typed array') | |
| 916 | assert(tag.byteLength === 1, 'tag must be 1 byte') | |
| 917 | ||
| 918 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 919 | assert( | |
| 920 | c.byteLength >= binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 921 | "c must be at least 'crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 922 | ) | |
| 923 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 924 | assert( | |
| 925 | m.byteLength === c.byteLength - binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 926 | "m must be 'c.byteLength - crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 927 | ) | |
| 928 | ||
| 929 | const res = binding.crypto_secretstream_xchacha20poly1305_pull( | |
| 930 | state.buffer, | |
| 931 | state.byteOffset, | |
| 932 | state.byteLength, | |
| 933 | ||
| 934 | m.buffer, | |
| 935 | m.byteOffset, | |
| 936 | m.byteLength, | |
| 937 | ||
| 938 | tag.buffer, | |
| 939 | tag.byteOffset, | |
| 940 | tag.byteLength, | |
| 941 | ||
| 942 | c.buffer, | |
| 943 | c.byteOffset, | |
| 944 | c.byteLength, | |
| 945 | ||
| 946 | ad.buffer, | |
| 947 | ad.byteOffset, | |
| 948 | ad.byteLength | |
| 949 | ) | |
| 950 | ||
| 951 | if (res < 0) throw new Error('pull failed') | |
| 952 | ||
| 953 | return res | |
| 954 | } | |
| 955 | ||
| 956 | exports.crypto_secretstream_xchacha20poly1305_rekey = function (state) { | |
| 957 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 958 | assert( | |
| 959 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 960 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 961 | ) | |
| 962 | ||
| 963 | binding.crypto_secretstream_xchacha20poly1305_rekey( | |
| 964 | state.buffer, | |
| 965 | state.byteOffset, | |
| 966 | state.byteLength | |
| 967 | ) | |
| 968 | } | |
| 969 | ||
| 970 | // crypto_stream | |
| 971 | ||
| 972 | exports.crypto_stream = function (c, n, k) { | |
| 973 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 974 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 975 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 976 | assert( | |
| 977 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 978 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 979 | ) | |
| 980 | assert( | |
| 981 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 982 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 983 | ) | |
| 984 | ||
| 985 | const res = binding.crypto_stream(c, n, k) | |
| 986 | ||
| 987 | if (res !== 0) throw new Error('status: ' + res) | |
| 988 | } | |
| 989 | ||
| 990 | exports.crypto_stream_xor = function (c, m, n, k) { | |
| 991 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 992 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 993 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 994 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 995 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 996 | assert( | |
| 997 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 998 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 999 | ) | |
| 1000 | assert( | |
| 1001 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 1002 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 1003 | ) | |
| 1004 | ||
| 1005 | const res = binding.crypto_stream_xor( | |
| 1006 | c.buffer, | |
| 1007 | c.byteOffset, | |
| 1008 | c.byteLength, | |
| 1009 | ||
| 1010 | m.buffer, | |
| 1011 | m.byteOffset, | |
| 1012 | m.byteLength, | |
| 1013 | ||
| 1014 | n.buffer, | |
| 1015 | n.byteOffset, | |
| 1016 | n.byteLength, | |
| 1017 | ||
| 1018 | k.buffer, | |
| 1019 | k.byteOffset, | |
| 1020 | k.byteLength | |
| 1021 | ) | |
| 1022 | ||
| 1023 | if (res !== 0) throw new Error('status: ' + res) | |
| 1024 | } | |
| 1025 | ||
| 1026 | exports.crypto_stream_chacha20 = function (c, n, k) { | |
| 1027 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1028 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1029 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1030 | assert( | |
| 1031 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1032 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1033 | ) | |
| 1034 | assert( | |
| 1035 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1036 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1037 | ) | |
| 1038 | ||
| 1039 | const res = binding.crypto_stream_chacha20(c, n, k) | |
| 1040 | ||
| 1041 | if (res !== 0) throw new Error('status: ' + res) | |
| 1042 | } | |
| 1043 | ||
| 1044 | exports.crypto_stream_chacha20_xor = function (c, m, n, k) { | |
| 1045 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1046 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1047 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1048 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1049 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1050 | assert( | |
| 1051 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1052 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1053 | ) | |
| 1054 | assert( | |
| 1055 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1056 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1057 | ) | |
| 1058 | ||
| 1059 | const res = binding.crypto_stream_chacha20_xor(c, m, n, k) | |
| 1060 | ||
| 1061 | if (res !== 0) throw new Error('status: ' + res) | |
| 1062 | } | |
| 1063 | ||
| 1064 | exports.crypto_stream_chacha20_xor_ic = function (c, m, n, ic, k) { | |
| 1065 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1066 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1067 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1068 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1069 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1070 | assert( | |
| 1071 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1072 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1073 | ) | |
| 1074 | assert( | |
| 1075 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1076 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1077 | ) | |
| 1078 | ||
| 1079 | const res = binding.crypto_stream_chacha20_xor_ic(c, m, n, ic, k) | |
| 1080 | ||
| 1081 | if (res !== 0) throw new Error('status: ' + res) | |
| 1082 | } | |
| 1083 | ||
| 1084 | exports.crypto_stream_chacha20_ietf = function (c, n, k) { | |
| 1085 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1086 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1087 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1088 | assert( | |
| 1089 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1090 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1091 | ) | |
| 1092 | assert( | |
| 1093 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1094 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1095 | ) | |
| 1096 | ||
| 1097 | const res = binding.crypto_stream_chacha20_ietf(c, n, k) | |
| 1098 | ||
| 1099 | if (res !== 0) throw new Error('status: ' + res) | |
| 1100 | } | |
| 1101 | ||
| 1102 | exports.crypto_stream_chacha20_ietf_xor = function (c, m, n, k) { | |
| 1103 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1104 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1105 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1106 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1107 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1108 | assert( | |
| 1109 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1110 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1111 | ) | |
| 1112 | assert( | |
| 1113 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1114 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1115 | ) | |
| 1116 | ||
| 1117 | const res = binding.crypto_stream_chacha20_ietf_xor(c, m, n, k) | |
| 1118 | ||
| 1119 | if (res !== 0) throw new Error('status: ' + res) | |
| 1120 | } | |
| 1121 | ||
| 1122 | exports.crypto_stream_chacha20_ietf_xor_ic = function (c, m, n, ic, k) { | |
| 1123 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1124 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1125 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1126 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1127 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1128 | assert( | |
| 1129 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1130 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1131 | ) | |
| 1132 | assert( | |
| 1133 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1134 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1135 | ) | |
| 1136 | ||
| 1137 | const res = binding.crypto_stream_chacha20_ietf_xor_ic(c, m, n, ic, k) | |
| 1138 | ||
| 1139 | if (res !== 0) throw new Error('status: ' + res) | |
| 1140 | } | |
| 1141 | ||
| 1142 | exports.crypto_stream_xchacha20 = function (c, n, k) { | |
| 1143 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1144 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1145 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1146 | assert( | |
| 1147 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1148 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1149 | ) | |
| 1150 | assert( | |
| 1151 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1152 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1153 | ) | |
| 1154 | ||
| 1155 | const res = binding.crypto_stream_xchacha20(c, n, k) | |
| 1156 | ||
| 1157 | if (res !== 0) throw new Error('status: ' + res) | |
| 1158 | } | |
| 1159 | ||
| 1160 | exports.crypto_stream_xchacha20_xor = function (c, m, n, k) { | |
| 1161 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1162 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1163 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1164 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1165 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1166 | assert( | |
| 1167 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1168 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1169 | ) | |
| 1170 | assert( | |
| 1171 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1172 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1173 | ) | |
| 1174 | ||
| 1175 | const res = binding.crypto_stream_xchacha20_xor(c, m, n, k) | |
| 1176 | ||
| 1177 | if (res !== 0) throw new Error('status: ' + res) | |
| 1178 | } | |
| 1179 | ||
| 1180 | exports.crypto_stream_xchacha20_xor_ic = function (c, m, n, ic, k) { | |
| 1181 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1182 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1183 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1184 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1185 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1186 | assert( | |
| 1187 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1188 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1189 | ) | |
| 1190 | assert( | |
| 1191 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1192 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1193 | ) | |
| 1194 | ||
| 1195 | const res = binding.crypto_stream_xchacha20_xor_ic(c, m, n, ic, k) | |
| 1196 | ||
| 1197 | if (res !== 0) throw new Error('status: ' + res) | |
| 1198 | } | |
| 1199 | ||
| 1200 | exports.crypto_stream_salsa20 = function (c, n, k) { | |
| 1201 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1202 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1203 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1204 | assert( | |
| 1205 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1206 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1207 | ) | |
| 1208 | assert( | |
| 1209 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1210 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1211 | ) | |
| 1212 | ||
| 1213 | const res = binding.crypto_stream_salsa20(c, n, k) | |
| 1214 | ||
| 1215 | if (res !== 0) throw new Error('status: ' + res) | |
| 1216 | } | |
| 1217 | ||
| 1218 | exports.crypto_stream_salsa20_xor = function (c, m, n, k) { | |
| 1219 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1220 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1221 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1222 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1223 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1224 | assert( | |
| 1225 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1226 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1227 | ) | |
| 1228 | assert( | |
| 1229 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1230 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1231 | ) | |
| 1232 | ||
| 1233 | const res = binding.crypto_stream_salsa20_xor(c, m, n, k) | |
| 1234 | ||
| 1235 | if (res !== 0) throw new Error('status: ' + res) | |
| 1236 | } | |
| 1237 | ||
| 1238 | exports.crypto_stream_salsa20_xor_ic = function (c, m, n, ic, k) { | |
| 1239 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1240 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1241 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1242 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1243 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1244 | assert( | |
| 1245 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1246 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1247 | ) | |
| 1248 | assert( | |
| 1249 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1250 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1251 | ) | |
| 1252 | ||
| 1253 | const res = binding.crypto_stream_salsa20_xor_ic(c, m, n, ic, k) | |
| 1254 | ||
| 1255 | if (res !== 0) throw new Error('status: ' + res) | |
| 1256 | } | |
| 1257 | ||
| 1258 | // crypto_auth | |
| 1259 | ||
| 1260 | exports.crypto_auth = function (out, input, k) { | |
| 1261 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1262 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1263 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1264 | assert(out.byteLength === binding.crypto_auth_BYTES, "out must be 'crypto_auth_BYTES' bytes") | |
| 1265 | assert(k.byteLength === binding.crypto_auth_KEYBYTES, "k must be 'crypto_auth_KEYBYTES' bytes") | |
| 1266 | ||
| 1267 | const res = binding.crypto_auth(out, input, k) | |
| 1268 | ||
| 1269 | if (res !== 0) throw new Error('status: ' + res) | |
| 1270 | } | |
| 1271 | ||
| 1272 | exports.crypto_auth_verify = function (h, input, k) { | |
| 1273 | assert(ArrayBuffer.isView(h), 'h must be a typed array') | |
| 1274 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1275 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1276 | assert(h.byteLength === binding.crypto_auth_BYTES, "h must be 'crypto_auth_BYTES' bytes") | |
| 1277 | assert(k.byteLength === binding.crypto_auth_KEYBYTES, "k must be 'crypto_auth_KEYBYTES' bytes") | |
| 1278 | ||
| 1279 | return binding.crypto_auth_verify(h, input, k) | |
| 1280 | } | |
| 1281 | ||
| 1282 | // crypto_onetimeauth | |
| 1283 | ||
| 1284 | exports.crypto_onetimeauth = function (out, input, k) { | |
| 1285 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1286 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1287 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1288 | assert( | |
| 1289 | out.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1290 | "out must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1291 | ) | |
| 1292 | assert( | |
| 1293 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1294 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1295 | ) | |
| 1296 | ||
| 1297 | const res = binding.crypto_onetimeauth(out, input, k) | |
| 1298 | ||
| 1299 | if (res !== 0) throw new Error('status: ' + res) | |
| 1300 | } | |
| 1301 | ||
| 1302 | exports.crypto_onetimeauth_init = function (state, k) { | |
| 1303 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1304 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1305 | assert( | |
| 1306 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1307 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1308 | ) | |
| 1309 | assert( | |
| 1310 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1311 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1312 | ) | |
| 1313 | ||
| 1314 | const res = binding.crypto_onetimeauth_init(state, k) | |
| 1315 | ||
| 1316 | if (res !== 0) throw new Error('status: ' + res) | |
| 1317 | } | |
| 1318 | ||
| 1319 | exports.crypto_onetimeauth_update = function (state, input) { | |
| 1320 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1321 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1322 | assert( | |
| 1323 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1324 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1325 | ) | |
| 1326 | ||
| 1327 | const res = binding.crypto_onetimeauth_update(state, input) | |
| 1328 | ||
| 1329 | if (res !== 0) throw new Error('status: ' + res) | |
| 1330 | } | |
| 1331 | ||
| 1332 | exports.crypto_onetimeauth_final = function (state, out) { | |
| 1333 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1334 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1335 | assert( | |
| 1336 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1337 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1338 | ) | |
| 1339 | assert( | |
| 1340 | out.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1341 | "out must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1342 | ) | |
| 1343 | ||
| 1344 | const res = binding.crypto_onetimeauth_final(state, out) | |
| 1345 | ||
| 1346 | if (res !== 0) throw new Error('status: ' + res) | |
| 1347 | } | |
| 1348 | ||
| 1349 | exports.crypto_onetimeauth_verify = function (h, input, k) { | |
| 1350 | assert(ArrayBuffer.isView(h), 'h must be a typed array') | |
| 1351 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1352 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1353 | assert( | |
| 1354 | h.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1355 | "h must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1356 | ) | |
| 1357 | assert( | |
| 1358 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1359 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1360 | ) | |
| 1361 | ||
| 1362 | return binding.crypto_onetimeauth_verify(h, input, k) | |
| 1363 | } | |
| 1364 | ||
| 1365 | // crypto_pwhash | |
| 1366 | ||
| 1367 | exports.crypto_pwhash = function (out, passwd, salt, opslimit, memlimit, alg) { | |
| 1368 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1369 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1370 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1371 | assert( | |
| 1372 | out.byteLength >= binding.crypto_pwhash_BYTES_MIN, | |
| 1373 | "out must be at least 'crypto_pwhash_BYTES_MIN' bytes" | |
| 1374 | ) | |
| 1375 | assert( | |
| 1376 | out.byteLength <= binding.crypto_pwhash_BYTES_MAX, | |
| 1377 | "out must be at most 'crypto_pwhash_BYTES_MAX' bytes" | |
| 1378 | ) | |
| 1379 | assert( | |
| 1380 | salt.byteLength === binding.crypto_pwhash_SALTBYTES, | |
| 1381 | "salt must be 'crypto_pwhash_SALTBYTES' bytes" | |
| 1382 | ) | |
| 1383 | assert( | |
| 1384 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1385 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1386 | ) | |
| 1387 | assert( | |
| 1388 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1389 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1390 | ) | |
| 1391 | assert( | |
| 1392 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1393 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1394 | ) | |
| 1395 | assert( | |
| 1396 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1397 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1398 | ) | |
| 1399 | assert(alg >= 1 && alg <= 2, 'alg must be either Argon2i 1.3 or Argon2id 1.3') | |
| 1400 | ||
| 1401 | const res = binding.crypto_pwhash(out, passwd, salt, opslimit, memlimit, alg) | |
| 1402 | ||
| 1403 | if (res !== 0) throw new Error('status: ' + res) | |
| 1404 | } | |
| 1405 | ||
| 1406 | exports.crypto_pwhash_async = function ( | |
| 1407 | out, | |
| 1408 | passwd, | |
| 1409 | salt, | |
| 1410 | opslimit, | |
| 1411 | memlimit, | |
| 1412 | alg, | |
| 1413 | callback = undefined | |
| 1414 | ) { | |
| 1415 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1416 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1417 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1418 | assert( | |
| 1419 | out.byteLength >= binding.crypto_pwhash_BYTES_MIN, | |
| 1420 | "out must be at least 'crypto_pwhash_BYTES_MIN' bytes" | |
| 1421 | ) | |
| 1422 | assert( | |
| 1423 | out.byteLength <= binding.crypto_pwhash_BYTES_MAX, | |
| 1424 | "out must be at most 'crypto_pwhash_BYTES_MAX' bytes" | |
| 1425 | ) | |
| 1426 | assert( | |
| 1427 | salt.byteLength === binding.crypto_pwhash_SALTBYTES, | |
| 1428 | "salt must be 'crypto_pwhash_SALTBYTES' bytes" | |
| 1429 | ) | |
| 1430 | assert( | |
| 1431 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1432 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1433 | ) | |
| 1434 | assert( | |
| 1435 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1436 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1437 | ) | |
| 1438 | assert( | |
| 1439 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1440 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1441 | ) | |
| 1442 | assert( | |
| 1443 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1444 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1445 | ) | |
| 1446 | assert(alg >= 1 && alg <= 2, 'alg must be either Argon2i 1.3 or Argon2id 1.3') | |
| 1447 | ||
| 1448 | const [done, promise] = checkStatus(callback) | |
| 1449 | ||
| 1450 | binding.crypto_pwhash_async( | |
| 1451 | out.buffer, | |
| 1452 | out.byteOffset, | |
| 1453 | out.byteLength, | |
| 1454 | ||
| 1455 | passwd.buffer, | |
| 1456 | passwd.byteOffset, | |
| 1457 | passwd.byteLength, | |
| 1458 | ||
| 1459 | salt.buffer, | |
| 1460 | salt.byteOffset, | |
| 1461 | salt.byteLength, | |
| 1462 | ||
| 1463 | opslimit, | |
| 1464 | memlimit, | |
| 1465 | alg, | |
| 1466 | ||
| 1467 | done | |
| 1468 | ) | |
| 1469 | ||
| 1470 | return promise | |
| 1471 | } | |
| 1472 | ||
| 1473 | exports.crypto_pwhash_str = function (out, passwd, opslimit, memlimit) { | |
| 1474 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1475 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1476 | assert( | |
| 1477 | out.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1478 | "out must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1479 | ) | |
| 1480 | assert(typeof opslimit === 'number', 'opslimit must be a number') | |
| 1481 | assert( | |
| 1482 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1483 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1484 | ) | |
| 1485 | assert( | |
| 1486 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1487 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1488 | ) | |
| 1489 | assert(typeof memlimit === 'number', 'memlimit must be a number') | |
| 1490 | assert( | |
| 1491 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1492 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1493 | ) | |
| 1494 | assert( | |
| 1495 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1496 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1497 | ) | |
| 1498 | ||
| 1499 | const res = binding.crypto_pwhash_str(out, passwd, opslimit, memlimit) | |
| 1500 | ||
| 1501 | if (res !== 0) throw new Error('status: ' + res) | |
| 1502 | } | |
| 1503 | ||
| 1504 | exports.crypto_pwhash_str_async = function (out, passwd, opslimit, memlimit, callback = undefined) { | |
| 1505 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1506 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1507 | assert( | |
| 1508 | out.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1509 | "out must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1510 | ) | |
| 1511 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1512 | assert(typeof opslimit === 'number', 'opslimit must be a number') | |
| 1513 | assert( | |
| 1514 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1515 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1516 | ) | |
| 1517 | assert( | |
| 1518 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1519 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1520 | ) | |
| 1521 | assert(typeof memlimit === 'number', 'memlimit must be a number') | |
| 1522 | assert( | |
| 1523 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1524 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1525 | ) | |
| 1526 | assert( | |
| 1527 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1528 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1529 | ) | |
| 1530 | ||
| 1531 | const [done, promise] = checkStatus(callback) | |
| 1532 | ||
| 1533 | binding.crypto_pwhash_str_async( | |
| 1534 | out.buffer, | |
| 1535 | out.byteOffset, | |
| 1536 | out.byteLength, | |
| 1537 | ||
| 1538 | passwd.buffer, | |
| 1539 | passwd.byteOffset, | |
| 1540 | passwd.byteLength, | |
| 1541 | ||
| 1542 | opslimit, | |
| 1543 | memlimit, | |
| 1544 | ||
| 1545 | done | |
| 1546 | ) | |
| 1547 | ||
| 1548 | return promise | |
| 1549 | } | |
| 1550 | ||
| 1551 | exports.crypto_pwhash_str_verify = function (str, passwd) { | |
| 1552 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1553 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1554 | assert( | |
| 1555 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1556 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1557 | ) | |
| 1558 | ||
| 1559 | return binding.crypto_pwhash_str_verify(str, passwd) | |
| 1560 | } | |
| 1561 | ||
| 1562 | exports.crypto_pwhash_str_verify_async = function (str, passwd, callback = undefined) { | |
| 1563 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1564 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1565 | assert( | |
| 1566 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1567 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1568 | ) | |
| 1569 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1570 | ||
| 1571 | const [done, promise] = checkStatus(callback, true) | |
| 1572 | ||
| 1573 | binding.crypto_pwhash_str_verify_async( | |
| 1574 | str.buffer, | |
| 1575 | str.byteOffset, | |
| 1576 | str.byteLength, | |
| 1577 | ||
| 1578 | passwd.buffer, | |
| 1579 | passwd.byteOffset, | |
| 1580 | passwd.byteLength, | |
| 1581 | ||
| 1582 | done | |
| 1583 | ) | |
| 1584 | ||
| 1585 | return promise | |
| 1586 | } | |
| 1587 | ||
| 1588 | exports.crypto_pwhash_str_needs_rehash = function (str, opslimit, memlimit) { | |
| 1589 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1590 | assert( | |
| 1591 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1592 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1593 | ) | |
| 1594 | assert( | |
| 1595 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1596 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1597 | ) | |
| 1598 | assert( | |
| 1599 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1600 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1601 | ) | |
| 1602 | assert( | |
| 1603 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1604 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1605 | ) | |
| 1606 | assert( | |
| 1607 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1608 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1609 | ) | |
| 1610 | ||
| 1611 | return binding.crypto_pwhash_str_needs_rehash(str, opslimit, memlimit) | |
| 1612 | } | |
| 1613 | ||
| 1614 | exports.crypto_pwhash_scryptsalsa208sha256 = function (out, passwd, salt, opslimit, memlimit) { | |
| 1615 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1616 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1617 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1618 | assert( | |
| 1619 | out.byteLength >= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN, | |
| 1620 | "out must be at least 'crypto_pwhash_scryptsalsa208sha256_BYTES_MIN' bytes" | |
| 1621 | ) | |
| 1622 | assert( | |
| 1623 | out.byteLength <= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX, | |
| 1624 | "out must be at most 'crypto_pwhash_scryptsalsa208sha256_BYTES_MAX' bytes" | |
| 1625 | ) | |
| 1626 | assert( | |
| 1627 | salt.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_SALTBYTES, | |
| 1628 | "salt must be 'crypto_pwhash_scryptsalsa208sha256_SALTBYTES' bytes" | |
| 1629 | ) | |
| 1630 | assert( | |
| 1631 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1632 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1633 | ) | |
| 1634 | assert( | |
| 1635 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1636 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1637 | ) | |
| 1638 | assert( | |
| 1639 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1640 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1641 | ) | |
| 1642 | assert( | |
| 1643 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1644 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1645 | ) | |
| 1646 | ||
| 1647 | const res = binding.crypto_pwhash_scryptsalsa208sha256(out, passwd, salt, opslimit, memlimit) | |
| 1648 | ||
| 1649 | if (res !== 0) throw new Error('status: ' + res) | |
| 1650 | } | |
| 1651 | ||
| 1652 | exports.crypto_pwhash_scryptsalsa208sha256_async = function ( | |
| 1653 | out, | |
| 1654 | passwd, | |
| 1655 | salt, | |
| 1656 | opslimit, | |
| 1657 | memlimit, | |
| 1658 | callback = undefined | |
| 1659 | ) { | |
| 1660 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1661 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1662 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1663 | assert( | |
| 1664 | out.byteLength >= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN, | |
| 1665 | "out must be at least 'crypto_pwhash_scryptsalsa208sha256_BYTES_MIN' bytes" | |
| 1666 | ) | |
| 1667 | assert( | |
| 1668 | out.byteLength <= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX, | |
| 1669 | "out must be at most 'crypto_pwhash_scryptsalsa208sha256_BYTES_MAX' bytes" | |
| 1670 | ) | |
| 1671 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1672 | assert( | |
| 1673 | salt.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_SALTBYTES, | |
| 1674 | "salt must be 'crypto_pwhash_scryptsalsa208sha256_SALTBYTES' bytes" | |
| 1675 | ) | |
| 1676 | assert( | |
| 1677 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1678 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1679 | ) | |
| 1680 | assert( | |
| 1681 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1682 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1683 | ) | |
| 1684 | assert( | |
| 1685 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1686 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1687 | ) | |
| 1688 | assert( | |
| 1689 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1690 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1691 | ) | |
| 1692 | ||
| 1693 | const [done, promise] = checkStatus(callback) | |
| 1694 | ||
| 1695 | binding.crypto_pwhash_scryptsalsa208sha256_async( | |
| 1696 | out.buffer, | |
| 1697 | out.byteOffset, | |
| 1698 | out.byteLength, | |
| 1699 | ||
| 1700 | passwd.buffer, | |
| 1701 | passwd.byteOffset, | |
| 1702 | passwd.byteLength, | |
| 1703 | ||
| 1704 | salt.buffer, | |
| 1705 | salt.byteOffset, | |
| 1706 | salt.byteLength, | |
| 1707 | ||
| 1708 | opslimit, | |
| 1709 | memlimit, | |
| 1710 | ||
| 1711 | done | |
| 1712 | ) | |
| 1713 | ||
| 1714 | return promise | |
| 1715 | } | |
| 1716 | ||
| 1717 | exports.crypto_pwhash_scryptsalsa208sha256_str_async = function ( | |
| 1718 | out, | |
| 1719 | passwd, | |
| 1720 | opslimit, | |
| 1721 | memlimit, | |
| 1722 | callback = undefined | |
| 1723 | ) { | |
| 1724 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1725 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1726 | assert( | |
| 1727 | out.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1728 | "out must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1729 | ) | |
| 1730 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1731 | assert( | |
| 1732 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1733 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1734 | ) | |
| 1735 | assert( | |
| 1736 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1737 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1738 | ) | |
| 1739 | assert( | |
| 1740 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1741 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1742 | ) | |
| 1743 | assert( | |
| 1744 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1745 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1746 | ) | |
| 1747 | ||
| 1748 | const [done, promise] = checkStatus(callback) | |
| 1749 | ||
| 1750 | binding.crypto_pwhash_scryptsalsa208sha256_str_async( | |
| 1751 | out.buffer, | |
| 1752 | out.byteOffset, | |
| 1753 | out.byteLength, | |
| 1754 | ||
| 1755 | passwd.buffer, | |
| 1756 | passwd.byteOffset, | |
| 1757 | passwd.byteLength, | |
| 1758 | ||
| 1759 | opslimit, | |
| 1760 | memlimit, | |
| 1761 | ||
| 1762 | done | |
| 1763 | ) | |
| 1764 | ||
| 1765 | return promise | |
| 1766 | } | |
| 1767 | ||
| 1768 | exports.crypto_pwhash_scryptsalsa208sha256_str = function (out, passwd, opslimit, memlimit) { | |
| 1769 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1770 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1771 | assert( | |
| 1772 | out.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1773 | "out must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1774 | ) | |
| 1775 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1776 | assert( | |
| 1777 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1778 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1779 | ) | |
| 1780 | assert( | |
| 1781 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1782 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1783 | ) | |
| 1784 | assert( | |
| 1785 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1786 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1787 | ) | |
| 1788 | assert( | |
| 1789 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1790 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1791 | ) | |
| 1792 | ||
| 1793 | const res = binding.crypto_pwhash_scryptsalsa208sha256_str(out, passwd, opslimit, memlimit) | |
| 1794 | ||
| 1795 | if (res !== 0) throw new Error('status: ' + res) | |
| 1796 | } | |
| 1797 | ||
| 1798 | exports.crypto_pwhash_scryptsalsa208sha256_str_verify_async = function ( | |
| 1799 | str, | |
| 1800 | passwd, | |
| 1801 | callback = undefined | |
| 1802 | ) { | |
| 1803 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1804 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1805 | assert( | |
| 1806 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1807 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1808 | ) | |
| 1809 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1810 | ||
| 1811 | const [done, promise] = checkStatus(callback, true) | |
| 1812 | ||
| 1813 | binding.crypto_pwhash_scryptsalsa208sha256_str_verify_async( | |
| 1814 | str.buffer, | |
| 1815 | str.byteOffset, | |
| 1816 | str.byteLength, | |
| 1817 | ||
| 1818 | passwd.buffer, | |
| 1819 | passwd.byteOffset, | |
| 1820 | passwd.byteLength, | |
| 1821 | ||
| 1822 | done | |
| 1823 | ) | |
| 1824 | ||
| 1825 | return promise | |
| 1826 | } | |
| 1827 | ||
| 1828 | exports.crypto_pwhash_scryptsalsa208sha256_str_verify = function (str, passwd) { | |
| 1829 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1830 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1831 | assert( | |
| 1832 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1833 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1834 | ) | |
| 1835 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1836 | ||
| 1837 | return binding.crypto_pwhash_scryptsalsa208sha256_str_verify(str, passwd) | |
| 1838 | } | |
| 1839 | ||
| 1840 | exports.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash = function (str, opslimit, memlimit) { | |
| 1841 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1842 | assert( | |
| 1843 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1844 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1845 | ) | |
| 1846 | assert( | |
| 1847 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1848 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1849 | ) | |
| 1850 | assert( | |
| 1851 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1852 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1853 | ) | |
| 1854 | assert( | |
| 1855 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1856 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1857 | ) | |
| 1858 | assert( | |
| 1859 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1860 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1861 | ) | |
| 1862 | ||
| 1863 | return binding.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(str, opslimit, memlimit) | |
| 1864 | } | |
| 1865 | ||
| 1866 | // crypto_kx | |
| 1867 | ||
| 1868 | exports.crypto_kx_keypair = function (pk, sk) { | |
| 1869 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 1870 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 1871 | assert( | |
| 1872 | pk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1873 | "pk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1874 | ) | |
| 1875 | assert( | |
| 1876 | sk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1877 | "sk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1878 | ) | |
| 1879 | ||
| 1880 | const res = binding.crypto_kx_keypair(pk, sk) | |
| 1881 | ||
| 1882 | if (res !== 0) throw new Error('status: ' + res) | |
| 1883 | } | |
| 1884 | ||
| 1885 | exports.crypto_kx_seed_keypair = function (pk, sk, seed) { | |
| 1886 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 1887 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 1888 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 1889 | assert( | |
| 1890 | pk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1891 | "pk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1892 | ) | |
| 1893 | assert( | |
| 1894 | sk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1895 | "sk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1896 | ) | |
| 1897 | assert( | |
| 1898 | seed.byteLength === binding.crypto_kx_SEEDBYTES, | |
| 1899 | "seed must be 'crypto_kx_SEEDBYTES' bytes" | |
| 1900 | ) | |
| 1901 | ||
| 1902 | const res = binding.crypto_kx_seed_keypair(pk, sk, seed) | |
| 1903 | ||
| 1904 | if (res !== 0) throw new Error('status: ' + res) | |
| 1905 | } | |
| 1906 | ||
| 1907 | exports.crypto_kx_client_session_keys = function (rx, tx, clientPk, clientSk, serverPk) { | |
| 1908 | if (!rx) rx = undefined | |
| 1909 | if (!tx) tx = undefined | |
| 1910 | ||
| 1911 | assert(rx || tx, 'at least one session key must be specified') | |
| 1912 | ||
| 1913 | if (rx) { | |
| 1914 | assert(ArrayBuffer.isView(rx), 'rx must be a typed array') | |
| 1915 | assert( | |
| 1916 | rx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1917 | "rx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1918 | ) | |
| 1919 | } | |
| 1920 | ||
| 1921 | if (tx) { | |
| 1922 | assert(ArrayBuffer.isView(tx), 'tx must be a typed array') | |
| 1923 | assert( | |
| 1924 | tx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1925 | "tx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1926 | ) | |
| 1927 | } | |
| 1928 | ||
| 1929 | assert(ArrayBuffer.isView(clientPk), 'clientPk must be a typed array') | |
| 1930 | assert(ArrayBuffer.isView(clientSk), 'clientSk must be a typed array') | |
| 1931 | assert(ArrayBuffer.isView(serverPk), 'serverPk must be a typed array') | |
| 1932 | assert( | |
| 1933 | clientPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1934 | "clientPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1935 | ) | |
| 1936 | assert( | |
| 1937 | clientSk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1938 | "clientSk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1939 | ) | |
| 1940 | assert( | |
| 1941 | serverPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1942 | "serverPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1943 | ) | |
| 1944 | ||
| 1945 | const res = binding.crypto_kx_client_session_keys(rx, tx, clientPk, clientSk, serverPk) | |
| 1946 | ||
| 1947 | if (res !== 0) throw new Error('status: ' + res) | |
| 1948 | } | |
| 1949 | ||
| 1950 | exports.crypto_kx_server_session_keys = function (rx, tx, serverPk, serverSk, clientPk) { | |
| 1951 | if (!rx) rx = undefined | |
| 1952 | if (!tx) tx = undefined | |
| 1953 | ||
| 1954 | assert(rx || tx, 'at least one session key must be specified') | |
| 1955 | ||
| 1956 | if (rx) { | |
| 1957 | assert(ArrayBuffer.isView(rx), 'rx must be a typed array') | |
| 1958 | assert( | |
| 1959 | rx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1960 | "rx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1961 | ) | |
| 1962 | } | |
| 1963 | ||
| 1964 | if (tx) { | |
| 1965 | assert(ArrayBuffer.isView(tx), 'tx must be a typed array') | |
| 1966 | assert( | |
| 1967 | tx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1968 | "tx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1969 | ) | |
| 1970 | } | |
| 1971 | ||
| 1972 | assert(ArrayBuffer.isView(serverPk), 'serverPk must be a typed array') | |
| 1973 | assert(ArrayBuffer.isView(serverSk), 'serverSk must be a typed array') | |
| 1974 | assert(ArrayBuffer.isView(clientPk), 'clientPk must be a typed array') | |
| 1975 | assert( | |
| 1976 | serverPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1977 | "serverPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1978 | ) | |
| 1979 | assert( | |
| 1980 | serverSk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1981 | "serverSk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1982 | ) | |
| 1983 | assert( | |
| 1984 | clientPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1985 | "clientPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1986 | ) | |
| 1987 | ||
| 1988 | const res = binding.crypto_kx_server_session_keys(rx, tx, serverPk, serverSk, clientPk) | |
| 1989 | ||
| 1990 | if (res !== 0) throw new Error('status: ' + res) | |
| 1991 | } | |
| 1992 | ||
| 1993 | // crypto_scalarmult | |
| 1994 | ||
| 1995 | exports.crypto_scalarmult_base = function (q, n) { | |
| 1996 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 1997 | assert( | |
| 1998 | q.byteLength === binding.crypto_scalarmult_BYTES, | |
| 1999 | "q must be 'crypto_scalarmult_BYTES' bytes" | |
| 2000 | ) | |
| 2001 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2002 | assert( | |
| 2003 | n.byteLength === binding.crypto_scalarmult_SCALARBYTES, | |
| 2004 | "n must be 'crypto_scalarmult_SCALARBYTES' bytes" | |
| 2005 | ) | |
| 2006 | ||
| 2007 | const res = binding.crypto_scalarmult_base(q, n) | |
| 2008 | ||
| 2009 | if (res !== 0) throw new Error('status: ' + res) | |
| 2010 | } | |
| 2011 | ||
| 2012 | exports.crypto_scalarmult = function (q, n, p) { | |
| 2013 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2014 | assert( | |
| 2015 | q.byteLength === binding.crypto_scalarmult_BYTES, | |
| 2016 | "q must be 'crypto_scalarmult_BYTES' bytes" | |
| 2017 | ) | |
| 2018 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2019 | assert( | |
| 2020 | n.byteLength === binding.crypto_scalarmult_SCALARBYTES, | |
| 2021 | "n must be 'crypto_scalarmult_SCALARBYTES' bytes" | |
| 2022 | ) | |
| 2023 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2024 | assert( | |
| 2025 | p.byteLength === binding.crypto_scalarmult_BYTES, | |
| 2026 | "p must be 'crypto_scalarmult_BYTES' bytes" | |
| 2027 | ) | |
| 2028 | ||
| 2029 | const res = binding.crypto_scalarmult(q, n, p) | |
| 2030 | ||
| 2031 | if (res !== 0) throw new Error('status: ' + res) | |
| 2032 | } | |
| 2033 | ||
| 2034 | exports.crypto_scalarmult_ed25519_base = function (q, n) { | |
| 2035 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2036 | assert( | |
| 2037 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2038 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2039 | ) | |
| 2040 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2041 | assert( | |
| 2042 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2043 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2044 | ) | |
| 2045 | ||
| 2046 | const res = binding.crypto_scalarmult_ed25519_base(q, n) | |
| 2047 | ||
| 2048 | if (res !== 0) throw new Error('status: ' + res) | |
| 2049 | } | |
| 2050 | ||
| 2051 | exports.crypto_scalarmult_ed25519 = function (q, n, p) { | |
| 2052 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2053 | assert( | |
| 2054 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2055 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2056 | ) | |
| 2057 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2058 | assert( | |
| 2059 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2060 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2061 | ) | |
| 2062 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2063 | assert( | |
| 2064 | p.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2065 | "p must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2066 | ) | |
| 2067 | ||
| 2068 | const res = binding.crypto_scalarmult_ed25519(q, n, p) | |
| 2069 | ||
| 2070 | if (res !== 0) throw new Error('status: ' + res) | |
| 2071 | } | |
| 2072 | ||
| 2073 | exports.crypto_core_ed25519_is_valid_point = function (p) { | |
| 2074 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2075 | assert( | |
| 2076 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2077 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2078 | ) | |
| 2079 | ||
| 2080 | return binding.crypto_core_ed25519_is_valid_point(p) | |
| 2081 | } | |
| 2082 | ||
| 2083 | exports.crypto_core_ed25519_from_uniform = function (p, r) { | |
| 2084 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2085 | assert( | |
| 2086 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2087 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2088 | ) | |
| 2089 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2090 | assert( | |
| 2091 | r.byteLength === binding.crypto_core_ed25519_UNIFORMBYTES, | |
| 2092 | "r must be 'crypto_core_ed25519_UNIFORMBYTES' bytes" | |
| 2093 | ) | |
| 2094 | ||
| 2095 | const res = binding.crypto_core_ed25519_from_uniform(p, r) | |
| 2096 | ||
| 2097 | if (res !== 0) throw new Error('status: ' + res) | |
| 2098 | } | |
| 2099 | ||
| 2100 | exports.crypto_scalarmult_ed25519_base_noclamp = function (q, n) { | |
| 2101 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2102 | assert( | |
| 2103 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2104 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2105 | ) | |
| 2106 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2107 | assert( | |
| 2108 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2109 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2110 | ) | |
| 2111 | ||
| 2112 | const res = binding.crypto_scalarmult_ed25519_base_noclamp(q, n) | |
| 2113 | ||
| 2114 | if (res !== 0) throw new Error('status: ' + res) | |
| 2115 | } | |
| 2116 | ||
| 2117 | exports.crypto_scalarmult_ed25519_noclamp = function (q, n, p) { | |
| 2118 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2119 | assert( | |
| 2120 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2121 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2122 | ) | |
| 2123 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2124 | assert( | |
| 2125 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2126 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2127 | ) | |
| 2128 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2129 | assert( | |
| 2130 | p.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2131 | "p must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2132 | ) | |
| 2133 | ||
| 2134 | const res = binding.crypto_scalarmult_ed25519_noclamp(q, n, p) | |
| 2135 | ||
| 2136 | if (res !== 0) throw new Error('status: ' + res) | |
| 2137 | } | |
| 2138 | ||
| 2139 | // crypto_core | |
| 2140 | ||
| 2141 | exports.crypto_core_ed25519_add = function (r, p, q) { | |
| 2142 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2143 | assert( | |
| 2144 | r.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2145 | "r must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2146 | ) | |
| 2147 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2148 | assert( | |
| 2149 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2150 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2151 | ) | |
| 2152 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2153 | assert( | |
| 2154 | q.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2155 | "q must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2156 | ) | |
| 2157 | ||
| 2158 | const res = binding.crypto_core_ed25519_add(r, p, q) | |
| 2159 | ||
| 2160 | if (res !== 0) throw new Error('status: ' + res) | |
| 2161 | } | |
| 2162 | ||
| 2163 | exports.crypto_core_ed25519_sub = function (r, p, q) { | |
| 2164 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2165 | assert( | |
| 2166 | r.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2167 | "r must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2168 | ) | |
| 2169 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2170 | assert( | |
| 2171 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2172 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2173 | ) | |
| 2174 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2175 | assert( | |
| 2176 | q.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2177 | "q must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2178 | ) | |
| 2179 | ||
| 2180 | const res = binding.crypto_core_ed25519_sub(r, p, q) | |
| 2181 | ||
| 2182 | if (res !== 0) throw new Error('status: ' + res) | |
| 2183 | } | |
| 2184 | ||
| 2185 | exports.crypto_core_ed25519_scalar_random = function (r) { | |
| 2186 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2187 | assert( | |
| 2188 | r.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2189 | "r must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2190 | ) | |
| 2191 | ||
| 2192 | binding.crypto_core_ed25519_scalar_random(r) | |
| 2193 | } | |
| 2194 | ||
| 2195 | exports.crypto_core_ed25519_scalar_reduce = function (r, s) { | |
| 2196 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2197 | assert( | |
| 2198 | r.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2199 | "r must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2200 | ) | |
| 2201 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2202 | assert( | |
| 2203 | s.byteLength === binding.crypto_core_ed25519_NONREDUCEDSCALARBYTES, | |
| 2204 | "s must be 'crypto_core_ed25519_NONREDUCEDSCALARBYTES' bytes" | |
| 2205 | ) | |
| 2206 | ||
| 2207 | binding.crypto_core_ed25519_scalar_reduce(r, s) | |
| 2208 | } | |
| 2209 | ||
| 2210 | exports.crypto_core_ed25519_scalar_invert = function (recip, s) { | |
| 2211 | assert(ArrayBuffer.isView(recip), 'recip must be a typed array') | |
| 2212 | assert( | |
| 2213 | recip.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2214 | "recip must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2215 | ) | |
| 2216 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2217 | assert( | |
| 2218 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2219 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2220 | ) | |
| 2221 | ||
| 2222 | binding.crypto_core_ed25519_scalar_invert(recip, s) | |
| 2223 | } | |
| 2224 | ||
| 2225 | exports.crypto_core_ed25519_scalar_negate = function (neg, s) { | |
| 2226 | assert(ArrayBuffer.isView(neg), 'neg must be a typed array') | |
| 2227 | assert( | |
| 2228 | neg.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2229 | "neg must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2230 | ) | |
| 2231 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2232 | assert( | |
| 2233 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2234 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2235 | ) | |
| 2236 | ||
| 2237 | binding.crypto_core_ed25519_scalar_negate(neg, s) | |
| 2238 | } | |
| 2239 | ||
| 2240 | exports.crypto_core_ed25519_scalar_complement = function (comp, s) { | |
| 2241 | assert(ArrayBuffer.isView(comp), 'comp must be a typed array') | |
| 2242 | assert( | |
| 2243 | comp.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2244 | "comp must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2245 | ) | |
| 2246 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2247 | assert( | |
| 2248 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2249 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2250 | ) | |
| 2251 | ||
| 2252 | binding.crypto_core_ed25519_scalar_complement(comp, s) | |
| 2253 | } | |
| 2254 | ||
| 2255 | exports.crypto_core_ed25519_scalar_add = function (z, x, y) { | |
| 2256 | assert(ArrayBuffer.isView(z), 'z must be a typed array') | |
| 2257 | assert( | |
| 2258 | z.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2259 | "z must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2260 | ) | |
| 2261 | assert(ArrayBuffer.isView(x), 'x must be a typed array') | |
| 2262 | assert( | |
| 2263 | x.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2264 | "x must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2265 | ) | |
| 2266 | assert(ArrayBuffer.isView(y), 'y must be a typed array') | |
| 2267 | assert( | |
| 2268 | y.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2269 | "y must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2270 | ) | |
| 2271 | ||
| 2272 | binding.crypto_core_ed25519_scalar_add(z, x, y) | |
| 2273 | } | |
| 2274 | ||
| 2275 | exports.crypto_core_ed25519_scalar_sub = function (z, x, y) { | |
| 2276 | assert(ArrayBuffer.isView(z), 'z must be a typed array') | |
| 2277 | assert( | |
| 2278 | z.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2279 | "z must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2280 | ) | |
| 2281 | assert(ArrayBuffer.isView(x), 'x must be a typed array') | |
| 2282 | assert( | |
| 2283 | x.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2284 | "x must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2285 | ) | |
| 2286 | assert(ArrayBuffer.isView(y), 'y must be a typed array') | |
| 2287 | assert( | |
| 2288 | y.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2289 | "y must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2290 | ) | |
| 2291 | ||
| 2292 | binding.crypto_core_ed25519_scalar_sub(z, x, y) | |
| 2293 | } | |
| 2294 | ||
| 2295 | // crypto_shorthash | |
| 2296 | ||
| 2297 | exports.crypto_shorthash = function (out, input, k) { | |
| 2298 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2299 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2300 | assert( | |
| 2301 | out.byteLength === binding.crypto_shorthash_BYTES, | |
| 2302 | "out must be 'crypto_shorthash_BYTES' bytes" | |
| 2303 | ) | |
| 2304 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2305 | assert( | |
| 2306 | k.byteLength === binding.crypto_shorthash_KEYBYTES, | |
| 2307 | "k must be 'crypto_shorthash_KEYBYTES' bytes" | |
| 2308 | ) | |
| 2309 | ||
| 2310 | const res = binding.crypto_shorthash(out, input, k) | |
| 2311 | ||
| 2312 | if (res !== 0) throw new Error('status: ' + res) | |
| 2313 | } | |
| 2314 | ||
| 2315 | // crypto_kdf | |
| 2316 | ||
| 2317 | exports.crypto_kdf_keygen = function (key) { | |
| 2318 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 2319 | assert(key.byteLength === binding.crypto_kdf_KEYBYTES, "key must be 'crypto_kdf_KEYBYTES' bytes") | |
| 2320 | ||
| 2321 | binding.crypto_kdf_keygen(key) | |
| 2322 | } | |
| 2323 | ||
| 2324 | exports.crypto_kdf_derive_from_key = function (subkey, subkeyId, ctx, key) { | |
| 2325 | assert(ArrayBuffer.isView(subkey), 'subkey must be a typed array') | |
| 2326 | assert( | |
| 2327 | subkey.byteLength >= binding.crypto_kdf_BYTES_MIN, | |
| 2328 | "subkey must be at least 'crypto_kdf_BYTES_MIN' bytes" | |
| 2329 | ) | |
| 2330 | assert( | |
| 2331 | subkey.byteLength <= binding.crypto_kdf_BYTES_MAX, | |
| 2332 | "subkey must be at most 'crypto_kdf_BYTES_MAX' bytes" | |
| 2333 | ) | |
| 2334 | assert(ArrayBuffer.isView(ctx), 'ctx must be a typed array') | |
| 2335 | assert( | |
| 2336 | ctx.byteLength === binding.crypto_kdf_CONTEXTBYTES, | |
| 2337 | "ctx must be 'crypto_kdf_CONTEXTBYTES' bytes" | |
| 2338 | ) | |
| 2339 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 2340 | assert(key.byteLength === binding.crypto_kdf_KEYBYTES, "key must be 'crypto_kdf_KEYBYTES' bytes") | |
| 2341 | ||
| 2342 | const res = binding.crypto_kdf_derive_from_key(subkey, subkeyId, ctx, key) | |
| 2343 | ||
| 2344 | if (res !== 0) throw new Error('status: ' + res) | |
| 2345 | } | |
| 2346 | ||
| 2347 | // crypto_hash | |
| 2348 | ||
| 2349 | exports.crypto_hash = function (out, input) { | |
| 2350 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2351 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2352 | assert(out.byteLength === binding.crypto_hash_BYTES, "out must be 'crypto_hash_BYTES' bytes") | |
| 2353 | ||
| 2354 | const res = binding.crypto_hash(out, input) | |
| 2355 | ||
| 2356 | if (res !== 0) throw new Error('status: ' + res) | |
| 2357 | } | |
| 2358 | ||
| 2359 | exports.crypto_hash_sha256 = function (out, input) { | |
| 2360 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2361 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2362 | assert( | |
| 2363 | out.byteLength === binding.crypto_hash_sha256_BYTES, | |
| 2364 | "out must be 'crypto_hash_sha256_BYTES' bytes" | |
| 2365 | ) | |
| 2366 | ||
| 2367 | const res = binding.crypto_hash_sha256(out, input) | |
| 2368 | ||
| 2369 | if (res !== 0) throw new Error('status: ' + res) | |
| 2370 | } | |
| 2371 | ||
| 2372 | exports.crypto_hash_sha256_init = function (state) { | |
| 2373 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2374 | assert( | |
| 2375 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2376 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2377 | ) | |
| 2378 | ||
| 2379 | const res = binding.crypto_hash_sha256_init(state) | |
| 2380 | ||
| 2381 | if (res !== 0) throw new Error('status: ' + res) | |
| 2382 | } | |
| 2383 | ||
| 2384 | exports.crypto_hash_sha256_update = function (state, input) { | |
| 2385 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2386 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2387 | assert( | |
| 2388 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2389 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2390 | ) | |
| 2391 | ||
| 2392 | const res = binding.crypto_hash_sha256_update(state, input) | |
| 2393 | ||
| 2394 | if (res !== 0) throw new Error('status: ' + res) | |
| 2395 | } | |
| 2396 | ||
| 2397 | exports.crypto_hash_sha256_final = function (state, out) { | |
| 2398 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2399 | assert( | |
| 2400 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2401 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2402 | ) | |
| 2403 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2404 | assert( | |
| 2405 | out.byteLength === binding.crypto_hash_sha256_BYTES, | |
| 2406 | "out must be 'crypto_hash_sha256_BYTES' bytes" | |
| 2407 | ) | |
| 2408 | ||
| 2409 | const res = binding.crypto_hash_sha256_final(state, out) | |
| 2410 | ||
| 2411 | if (res !== 0) throw new Error('status: ' + res) | |
| 2412 | } | |
| 2413 | ||
| 2414 | exports.crypto_hash_sha512 = function (out, input) { | |
| 2415 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2416 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2417 | assert( | |
| 2418 | out.byteLength === binding.crypto_hash_sha512_BYTES, | |
| 2419 | "out must be 'crypto_hash_sha512_BYTES' bytes" | |
| 2420 | ) | |
| 2421 | ||
| 2422 | const res = binding.crypto_hash_sha512(out, input) | |
| 2423 | ||
| 2424 | if (res !== 0) throw new Error('status: ' + res) | |
| 2425 | } | |
| 2426 | ||
| 2427 | exports.crypto_hash_sha512_init = function (state) { | |
| 2428 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2429 | assert( | |
| 2430 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2431 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2432 | ) | |
| 2433 | ||
| 2434 | const res = binding.crypto_hash_sha512_init(state) | |
| 2435 | ||
| 2436 | if (res !== 0) throw new Error('status: ' + res) | |
| 2437 | } | |
| 2438 | ||
| 2439 | exports.crypto_hash_sha512_update = function (state, input) { | |
| 2440 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2441 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2442 | assert( | |
| 2443 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2444 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2445 | ) | |
| 2446 | ||
| 2447 | const res = binding.crypto_hash_sha512_update(state, input) | |
| 2448 | ||
| 2449 | if (res !== 0) throw new Error('status: ' + res) | |
| 2450 | } | |
| 2451 | ||
| 2452 | exports.crypto_hash_sha512_final = function (state, out) { | |
| 2453 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2454 | assert( | |
| 2455 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2456 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2457 | ) | |
| 2458 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2459 | assert( | |
| 2460 | out.byteLength === binding.crypto_hash_sha512_BYTES, | |
| 2461 | "out must be 'crypto_hash_sha512_BYTES' bytes" | |
| 2462 | ) | |
| 2463 | ||
| 2464 | const res = binding.crypto_hash_sha512_final(state, out) | |
| 2465 | ||
| 2466 | if (res !== 0) throw new Error('status: ' + res) | |
| 2467 | } | |
| 2468 | ||
| 2469 | // crypto_aead | |
| 2470 | ||
| 2471 | exports.crypto_aead_xchacha20poly1305_ietf_keygen = function (k) { | |
| 2472 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2473 | assert( | |
| 2474 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2475 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2476 | ) | |
| 2477 | ||
| 2478 | binding.crypto_aead_xchacha20poly1305_ietf_keygen(k) | |
| 2479 | } | |
| 2480 | ||
| 2481 | exports.crypto_aead_xchacha20poly1305_ietf_encrypt = function (c, m, ad, nsec, npub, k) { | |
| 2482 | if (!ad) ad = undefined | |
| 2483 | ||
| 2484 | assert(nsec === null, 'nsec must always be set to null') | |
| 2485 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2486 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2487 | assert( | |
| 2488 | c.byteLength === m.byteLength + binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2489 | "c must be 'm.byteLength + crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2490 | ) | |
| 2491 | assert(c.byteLength <= 0xffffffff, 'c.byteLength must be a 32bit integer') | |
| 2492 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2493 | assert( | |
| 2494 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2495 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2496 | ) | |
| 2497 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2498 | assert( | |
| 2499 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2500 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2501 | ) | |
| 2502 | ||
| 2503 | const res = binding.crypto_aead_xchacha20poly1305_ietf_encrypt(c, m, ad, npub, k) | |
| 2504 | ||
| 2505 | if (res < 0) throw new Error('could not encrypt data') | |
| 2506 | ||
| 2507 | return res | |
| 2508 | } | |
| 2509 | ||
| 2510 | exports.crypto_aead_xchacha20poly1305_ietf_decrypt = function (m, nsec, c, ad, npub, k) { | |
| 2511 | if (!ad) ad = undefined | |
| 2512 | ||
| 2513 | assert(nsec === null, 'nsec must always be set to null') | |
| 2514 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2515 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2516 | assert( | |
| 2517 | m.byteLength === c.byteLength - binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2518 | "m must be 'c.byteLength - crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2519 | ) | |
| 2520 | assert(m.byteLength <= 0xffffffff, 'm.byteLength must be a 32bit integer') | |
| 2521 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2522 | assert( | |
| 2523 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2524 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2525 | ) | |
| 2526 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2527 | assert( | |
| 2528 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2529 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2530 | ) | |
| 2531 | ||
| 2532 | const res = binding.crypto_aead_xchacha20poly1305_ietf_decrypt(m, c, ad, npub, k) | |
| 2533 | ||
| 2534 | if (res < 0) throw new Error('could not verify data') | |
| 2535 | ||
| 2536 | return res | |
| 2537 | } | |
| 2538 | ||
| 2539 | exports.crypto_aead_xchacha20poly1305_ietf_encrypt_detached = function ( | |
| 2540 | c, | |
| 2541 | mac, | |
| 2542 | m, | |
| 2543 | ad, | |
| 2544 | nsec, | |
| 2545 | npub, | |
| 2546 | k | |
| 2547 | ) { | |
| 2548 | if (!ad) ad = undefined | |
| 2549 | ||
| 2550 | assert(nsec === null, 'nsec must always be set to null') | |
| 2551 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2552 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2553 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2554 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2555 | assert( | |
| 2556 | mac.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2557 | "mac must be 'crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2558 | ) | |
| 2559 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2560 | assert( | |
| 2561 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2562 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2563 | ) | |
| 2564 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2565 | assert( | |
| 2566 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2567 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2568 | ) | |
| 2569 | ||
| 2570 | const res = binding.crypto_aead_xchacha20poly1305_ietf_encrypt_detached(c, mac, m, ad, npub, k) | |
| 2571 | ||
| 2572 | if (res < 0) throw new Error('could not encrypt data') | |
| 2573 | ||
| 2574 | return res | |
| 2575 | } | |
| 2576 | ||
| 2577 | exports.crypto_aead_xchacha20poly1305_ietf_decrypt_detached = function ( | |
| 2578 | m, | |
| 2579 | nsec, | |
| 2580 | c, | |
| 2581 | mac, | |
| 2582 | ad, | |
| 2583 | npub, | |
| 2584 | k | |
| 2585 | ) { | |
| 2586 | if (!ad) ad = undefined | |
| 2587 | ||
| 2588 | assert(nsec === null, 'nsec must always be set to null') | |
| 2589 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2590 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2591 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 2592 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2593 | assert( | |
| 2594 | mac.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2595 | "mac must be 'crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2596 | ) | |
| 2597 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2598 | assert( | |
| 2599 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2600 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2601 | ) | |
| 2602 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2603 | assert( | |
| 2604 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2605 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2606 | ) | |
| 2607 | ||
| 2608 | const res = binding.crypto_aead_xchacha20poly1305_ietf_decrypt_detached(m, c, mac, ad, npub, k) | |
| 2609 | ||
| 2610 | if (res !== 0) throw new Error('could not verify data') | |
| 2611 | } | |
| 2612 | ||
| 2613 | exports.crypto_aead_chacha20poly1305_ietf_keygen = function (k) { | |
| 2614 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2615 | assert( | |
| 2616 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2617 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2618 | ) | |
| 2619 | ||
| 2620 | binding.crypto_aead_chacha20poly1305_ietf_keygen(k) | |
| 2621 | } | |
| 2622 | ||
| 2623 | exports.crypto_aead_chacha20poly1305_ietf_encrypt = function (c, m, ad, nsec, npub, k) { | |
| 2624 | if (!ad) ad = undefined | |
| 2625 | ||
| 2626 | assert(nsec === null, 'nsec must always be set to null') | |
| 2627 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2628 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2629 | assert( | |
| 2630 | c.byteLength === m.byteLength + binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2631 | "c must be 'm.byteLength + crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2632 | ) | |
| 2633 | assert(c.byteLength <= 0xffffffff, 'c.byteLength must be a 32bit integer') | |
| 2634 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2635 | assert( | |
| 2636 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2637 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2638 | ) | |
| 2639 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2640 | assert( | |
| 2641 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2642 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2643 | ) | |
| 2644 | ||
| 2645 | const res = binding.crypto_aead_chacha20poly1305_ietf_encrypt(c, m, ad, npub, k) | |
| 2646 | ||
| 2647 | if (res < 0) throw new Error('could not encrypt data') | |
| 2648 | ||
| 2649 | return res | |
| 2650 | } | |
| 2651 | ||
| 2652 | exports.crypto_aead_chacha20poly1305_ietf_decrypt = function (m, nsec, c, ad, npub, k) { | |
| 2653 | if (!ad) ad = undefined | |
| 2654 | ||
| 2655 | assert(nsec === null, 'nsec must always be set to null') | |
| 2656 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2657 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2658 | assert( | |
| 2659 | m.byteLength === c.byteLength - binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2660 | "m must be 'c.byteLength - crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2661 | ) | |
| 2662 | assert(m.byteLength <= 0xffffffff, 'm.byteLength must be a 32bit integer') | |
| 2663 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2664 | assert( | |
| 2665 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2666 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2667 | ) | |
| 2668 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2669 | assert( | |
| 2670 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2671 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2672 | ) | |
| 2673 | ||
| 2674 | const res = binding.crypto_aead_chacha20poly1305_ietf_decrypt(m, c, ad, npub, k) | |
| 2675 | ||
| 2676 | if (res < 0) throw new Error('could not verify data') | |
| 2677 | ||
| 2678 | return res | |
| 2679 | } | |
| 2680 | ||
| 2681 | exports.crypto_aead_chacha20poly1305_ietf_encrypt_detached = function ( | |
| 2682 | c, | |
| 2683 | mac, | |
| 2684 | m, | |
| 2685 | ad, | |
| 2686 | nsec, | |
| 2687 | npub, | |
| 2688 | k | |
| 2689 | ) { | |
| 2690 | if (!ad) ad = undefined | |
| 2691 | ||
| 2692 | assert(nsec === null, 'nsec must always be set to null') | |
| 2693 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2694 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2695 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2696 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2697 | assert( | |
| 2698 | mac.byteLength === binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2699 | "mac must be 'crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2700 | ) | |
| 2701 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2702 | assert( | |
| 2703 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2704 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2705 | ) | |
| 2706 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2707 | assert( | |
| 2708 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2709 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2710 | ) | |
| 2711 | ||
| 2712 | const res = binding.crypto_aead_chacha20poly1305_ietf_encrypt_detached(c, mac, m, ad, npub, k) | |
| 2713 | ||
| 2714 | if (res < 0) throw new Error('could not encrypt data') | |
| 2715 | ||
| 2716 | return res | |
| 2717 | } | |
| 2718 | ||
| 2719 | exports.crypto_aead_chacha20poly1305_ietf_decrypt_detached = function ( | |
| 2720 | m, | |
| 2721 | nsec, | |
| 2722 | c, | |
| 2723 | mac, | |
| 2724 | ad, | |
| 2725 | npub, | |
| 2726 | k | |
| 2727 | ) { | |
| 2728 | if (!ad) ad = undefined | |
| 2729 | ||
| 2730 | assert(nsec === null, 'nsec must always be set to null') | |
| 2731 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2732 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2733 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 2734 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2735 | assert( | |
| 2736 | mac.byteLength === binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2737 | "mac must be 'crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2738 | ) | |
| 2739 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2740 | assert( | |
| 2741 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2742 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2743 | ) | |
| 2744 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2745 | assert( | |
| 2746 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2747 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2748 | ) | |
| 2749 | ||
| 2750 | const res = binding.crypto_aead_chacha20poly1305_ietf_decrypt_detached(m, c, mac, ad, npub, k) | |
| 2751 | ||
| 2752 | if (res !== 0) throw new Error('could not verify data') | |
| 2753 | } | |
| 2754 | ||
| 2755 | // crypto_stream | |
| 2756 | ||
| 2757 | exports.crypto_stream_xor_wrap_init = function (state, n, k) { | |
| 2758 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2759 | assert( | |
| 2760 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2761 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2762 | ) | |
| 2763 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2764 | assert( | |
| 2765 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 2766 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 2767 | ) | |
| 2768 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2769 | assert( | |
| 2770 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 2771 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 2772 | ) | |
| 2773 | ||
| 2774 | binding.crypto_stream_xor_wrap_init(state, n, k) | |
| 2775 | } | |
| 2776 | ||
| 2777 | exports.crypto_stream_xor_wrap_update = function (state, c, m) { | |
| 2778 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2779 | assert( | |
| 2780 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2781 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2782 | ) | |
| 2783 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2784 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2785 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2786 | ||
| 2787 | binding.crypto_stream_xor_wrap_update(state, c, m) | |
| 2788 | } | |
| 2789 | ||
| 2790 | exports.crypto_stream_xor_wrap_final = function (state) { | |
| 2791 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2792 | assert( | |
| 2793 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2794 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2795 | ) | |
| 2796 | ||
| 2797 | binding.crypto_stream_xor_wrap_final(state) | |
| 2798 | } | |
| 2799 | ||
| 2800 | exports.crypto_stream_chacha20_xor_wrap_init = function (state, n, k) { | |
| 2801 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2802 | assert( | |
| 2803 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2804 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2805 | ) | |
| 2806 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2807 | assert( | |
| 2808 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 2809 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 2810 | ) | |
| 2811 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2812 | assert( | |
| 2813 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 2814 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 2815 | ) | |
| 2816 | ||
| 2817 | binding.crypto_stream_chacha20_xor_wrap_init(state, n, k) | |
| 2818 | } | |
| 2819 | ||
| 2820 | exports.crypto_stream_chacha20_xor_wrap_update = function (state, c, m) { | |
| 2821 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2822 | assert( | |
| 2823 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2824 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2825 | ) | |
| 2826 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2827 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2828 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2829 | ||
| 2830 | binding.crypto_stream_chacha20_xor_wrap_update(state, c, m) | |
| 2831 | } | |
| 2832 | ||
| 2833 | exports.crypto_stream_chacha20_xor_wrap_final = function (state) { | |
| 2834 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2835 | assert( | |
| 2836 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2837 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2838 | ) | |
| 2839 | ||
| 2840 | binding.crypto_stream_chacha20_xor_wrap_final(state) | |
| 2841 | } | |
| 2842 | ||
| 2843 | exports.crypto_stream_chacha20_ietf_xor_wrap_init = function (state, n, k) { | |
| 2844 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2845 | assert( | |
| 2846 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2847 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2848 | ) | |
| 2849 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2850 | assert( | |
| 2851 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 2852 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 2853 | ) | |
| 2854 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2855 | assert( | |
| 2856 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 2857 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 2858 | ) | |
| 2859 | ||
| 2860 | binding.crypto_stream_chacha20_ietf_xor_wrap_init(state, n, k) | |
| 2861 | } | |
| 2862 | ||
| 2863 | exports.crypto_stream_chacha20_ietf_xor_wrap_update = function (state, c, m) { | |
| 2864 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2865 | assert( | |
| 2866 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2867 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2868 | ) | |
| 2869 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2870 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2871 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2872 | ||
| 2873 | binding.crypto_stream_chacha20_ietf_xor_wrap_update(state, c, m) | |
| 2874 | } | |
| 2875 | ||
| 2876 | exports.crypto_stream_chacha20_ietf_xor_wrap_final = function (state) { | |
| 2877 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2878 | assert( | |
| 2879 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2880 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2881 | ) | |
| 2882 | ||
| 2883 | binding.crypto_stream_chacha20_ietf_xor_wrap_final(state) | |
| 2884 | } | |
| 2885 | ||
| 2886 | exports.crypto_stream_xchacha20_xor_wrap_init = function (state, n, k) { | |
| 2887 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2888 | assert( | |
| 2889 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2890 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2891 | ) | |
| 2892 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2893 | assert( | |
| 2894 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 2895 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 2896 | ) | |
| 2897 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2898 | assert( | |
| 2899 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 2900 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 2901 | ) | |
| 2902 | ||
| 2903 | binding.crypto_stream_xchacha20_xor_wrap_init(state, n, k) | |
| 2904 | } | |
| 2905 | ||
| 2906 | exports.crypto_stream_xchacha20_xor_wrap_update = function (state, c, m) { | |
| 2907 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2908 | assert( | |
| 2909 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2910 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2911 | ) | |
| 2912 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2913 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2914 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2915 | ||
| 2916 | binding.crypto_stream_xchacha20_xor_wrap_update(state, c, m) | |
| 2917 | } | |
| 2918 | ||
| 2919 | exports.crypto_stream_xchacha20_xor_wrap_final = function (state) { | |
| 2920 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2921 | assert( | |
| 2922 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2923 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2924 | ) | |
| 2925 | ||
| 2926 | binding.crypto_stream_xchacha20_xor_wrap_final(state) | |
| 2927 | } | |
| 2928 | ||
| 2929 | exports.crypto_stream_salsa20_xor_wrap_init = function (state, n, k) { | |
| 2930 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2931 | assert( | |
| 2932 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2933 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2934 | ) | |
| 2935 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2936 | assert( | |
| 2937 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 2938 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 2939 | ) | |
| 2940 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2941 | assert( | |
| 2942 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 2943 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 2944 | ) | |
| 2945 | ||
| 2946 | binding.crypto_stream_salsa20_xor_wrap_init(state, n, k) | |
| 2947 | } | |
| 2948 | ||
| 2949 | exports.crypto_stream_salsa20_xor_wrap_update = function (state, c, m) { | |
| 2950 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2951 | assert( | |
| 2952 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2953 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2954 | ) | |
| 2955 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2956 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2957 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2958 | ||
| 2959 | binding.crypto_stream_salsa20_xor_wrap_update(state, c, m) | |
| 2960 | } | |
| 2961 | ||
| 2962 | exports.crypto_stream_salsa20_xor_wrap_final = function (state) { | |
| 2963 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2964 | assert( | |
| 2965 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2966 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2967 | ) | |
| 2968 | ||
| 2969 | binding.crypto_stream_salsa20_xor_wrap_final(state) | |
| 2970 | } | |
| 2971 | ||
| 2972 | // experimental | |
| 2973 | ||
| 2974 | exports.extension_tweak_ed25519_base = function (n, p, ns) { | |
| 2975 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2976 | assert( | |
| 2977 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 2978 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 2979 | ) | |
| 2980 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2981 | assert( | |
| 2982 | p.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 2983 | "p must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 2984 | ) | |
| 2985 | ||
| 2986 | binding.extension_tweak_ed25519_base(n, p, ns) | |
| 2987 | } | |
| 2988 | ||
| 2989 | exports.extension_tweak_ed25519_sign_detached = function (sig, m, scalar, pk) { | |
| 2990 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 2991 | assert(sig.byteLength === binding.crypto_sign_BYTES, "sig must be 'crypto_sign_BYTES' bytes") | |
| 2992 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2993 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 2994 | assert( | |
| 2995 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 2996 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 2997 | ) | |
| 2998 | ||
| 2999 | if (pk) { | |
| 3000 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3001 | assert( | |
| 3002 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3003 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3004 | ) | |
| 3005 | } | |
| 3006 | ||
| 3007 | const res = binding.extension_tweak_ed25519_sign_detached(sig, m, scalar, pk) | |
| 3008 | ||
| 3009 | if (res !== 0) throw new Error('failed to compute signature') | |
| 3010 | } | |
| 3011 | ||
| 3012 | exports.extension_tweak_ed25519_sk_to_scalar = function (n, sk) { | |
| 3013 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 3014 | assert( | |
| 3015 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3016 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3017 | ) | |
| 3018 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 3019 | assert( | |
| 3020 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 3021 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 3022 | ) | |
| 3023 | ||
| 3024 | binding.extension_tweak_ed25519_sk_to_scalar(n, sk) | |
| 3025 | } | |
| 3026 | ||
| 3027 | exports.extension_tweak_ed25519_scalar = function (scalarOut, scalar, ns) { | |
| 3028 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3029 | assert( | |
| 3030 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3031 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3032 | ) | |
| 3033 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 3034 | assert( | |
| 3035 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3036 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3037 | ) | |
| 3038 | ||
| 3039 | binding.extension_tweak_ed25519_scalar(scalarOut, scalar, ns) | |
| 3040 | } | |
| 3041 | ||
| 3042 | exports.extension_tweak_ed25519_pk = function (tpk, pk, ns) { | |
| 3043 | assert(ArrayBuffer.isView(tpk), 'tpk must be a typed array') | |
| 3044 | assert( | |
| 3045 | tpk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3046 | "tpk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3047 | ) | |
| 3048 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3049 | assert( | |
| 3050 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3051 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3052 | ) | |
| 3053 | ||
| 3054 | const res = binding.extension_tweak_ed25519_pk(tpk, pk, ns) | |
| 3055 | ||
| 3056 | if (res !== 0) throw new Error('failed to tweak public key') | |
| 3057 | } | |
| 3058 | ||
| 3059 | exports.extension_tweak_ed25519_keypair = function (pk, scalarOut, scalarIn, ns) { | |
| 3060 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3061 | assert( | |
| 3062 | pk.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 3063 | "pk must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 3064 | ) | |
| 3065 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3066 | assert( | |
| 3067 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3068 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3069 | ) | |
| 3070 | assert(ArrayBuffer.isView(scalarIn), 'scalarIn must be a typed array') | |
| 3071 | assert( | |
| 3072 | scalarIn.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3073 | "scalarIn must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3074 | ) | |
| 3075 | ||
| 3076 | binding.extension_tweak_ed25519_keypair(pk, scalarOut, scalarIn, ns) | |
| 3077 | } | |
| 3078 | ||
| 3079 | exports.extension_tweak_ed25519_scalar_add = function (scalarOut, scalar, n) { | |
| 3080 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3081 | assert( | |
| 3082 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3083 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3084 | ) | |
| 3085 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 3086 | assert( | |
| 3087 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3088 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3089 | ) | |
| 3090 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 3091 | assert( | |
| 3092 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3093 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3094 | ) | |
| 3095 | ||
| 3096 | binding.extension_tweak_ed25519_scalar_add(scalarOut, scalar, n) | |
| 3097 | } | |
| 3098 | ||
| 3099 | exports.extension_tweak_ed25519_pk_add = function (tpk, pk, p) { | |
| 3100 | assert(ArrayBuffer.isView(tpk), 'tpk must be a typed array') | |
| 3101 | assert( | |
| 3102 | tpk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3103 | "tpk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3104 | ) | |
| 3105 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3106 | assert( | |
| 3107 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3108 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3109 | ) | |
| 3110 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 3111 | assert( | |
| 3112 | p.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3113 | "p must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3114 | ) | |
| 3115 | ||
| 3116 | const res = binding.extension_tweak_ed25519_pk_add(tpk, pk, p) | |
| 3117 | ||
| 3118 | if (res !== 0) throw new Error('failed to add tweak to public key') | |
| 3119 | } | |
| 3120 | ||
| 3121 | exports.extension_tweak_ed25519_keypair_add = function (pk, scalarOut, scalarIn, tweak) { | |
| 3122 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3123 | assert( | |
| 3124 | pk.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 3125 | "pk must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 3126 | ) | |
| 3127 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3128 | assert( | |
| 3129 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3130 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3131 | ) | |
| 3132 | assert(ArrayBuffer.isView(scalarIn), 'scalarIn must be a typed array') | |
| 3133 | assert( | |
| 3134 | scalarIn.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3135 | "scalarIn must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3136 | ) | |
| 3137 | assert(ArrayBuffer.isView(tweak), 'tweak must be a typed array') | |
| 3138 | assert( | |
| 3139 | tweak.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3140 | "tweak must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3141 | ) | |
| 3142 | ||
| 3143 | const res = binding.extension_tweak_ed25519_keypair_add(pk, scalarOut, scalarIn, tweak) | |
| 3144 | ||
| 3145 | if (res !== 0) throw new Error('failed to add tweak to keypair') | |
| 3146 | } | |
| 3147 | ||
| 3148 | exports.extension_pbkdf2_sha512_async = function (out, passwd, salt, iter, outlen, callback) { | |
| 3149 | assert( | |
| 3150 | iter >= binding.extension_pbkdf2_sha512_ITERATIONS_MIN, | |
| 3151 | "iter must be at least 'extension_pbkdf2_sha512_ITERATIONS_MIN'" | |
| 3152 | ) | |
| 3153 | assert( | |
| 3154 | outlen <= binding.extension_pbkdf2_sha512_BYTES_MAX, | |
| 3155 | "outlen must be at most 'extension_pbkdf2_sha512_BYTES_MAX'" | |
| 3156 | ) | |
| 3157 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 3158 | assert(out.byteLength >= outlen, "out must be at least 'outlen' bytes") | |
| 3159 | assert(out.byteLength > 0, 'out must not be empty') | |
| 3160 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 3161 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 3162 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 3163 | assert(salt.byteLength > 0, 'salt must not be empty') | |
| 3164 | ||
| 3165 | const [done, promise] = checkStatus(callback) | |
| 3166 | ||
| 3167 | binding.extension_pbkdf2_sha512_async( | |
| 3168 | out.buffer, | |
| 3169 | out.byteOffset, | |
| 3170 | out.byteLength, | |
| 3171 | ||
| 3172 | passwd.buffer, | |
| 3173 | passwd.byteOffset, | |
| 3174 | passwd.byteLength, | |
| 3175 | ||
| 3176 | salt.buffer, | |
| 3177 | salt.byteOffset, | |
| 3178 | salt.byteLength, | |
| 3179 | ||
| 3180 | iter, | |
| 3181 | outlen, | |
| 3182 | ||
| 3183 | done | |
| 3184 | ) | |
| 3185 | ||
| 3186 | return promise | |
| 3187 | } | |
| 3188 | ||
| 3189 | exports.extension_pbkdf2_sha512 = function (out, passwd, salt, iter, outlen) { | |
| 3190 | assert( | |
| 3191 | iter >= binding.extension_pbkdf2_sha512_ITERATIONS_MIN, | |
| 3192 | "iter must be at least 'extension_pbkdf2_sha512_ITERATIONS_MIN'" | |
| 3193 | ) | |
| 3194 | assert( | |
| 3195 | outlen <= binding.extension_pbkdf2_sha512_BYTES_MAX, | |
| 3196 | "outlen must be at most 'extension_pbkdf2_sha512_BYTES_MAX'" | |
| 3197 | ) | |
| 3198 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 3199 | assert(out.byteLength >= outlen, "out must be at least 'outlen' bytes") | |
| 3200 | assert(out.byteLength > 0, 'out must not be empty') | |
| 3201 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 3202 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 3203 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 3204 | assert(salt.byteLength > 0, 'salt must not be empty') | |
| 3205 | ||
| 3206 | const res = binding.extension_pbkdf2_sha512(out, passwd, salt, iter, outlen) | |
| 3207 | ||
| 3208 | if (res !== 0) throw new Error('failed to add tweak to public key') | |
| 3209 | } | |
| 3210 | ||
| 3211 | function checkStatus(callback, booleanResult = false) { | |
| 3212 | let done, promise | |
| 3213 | ||
| 3214 | if (typeof callback === 'function') { | |
| 3215 | done = function (status) { | |
| 3216 | if (booleanResult) callback(null, status === 0) | |
| 3217 | else if (status === 0) callback(null) | |
| 3218 | else callback(new Error('status: ' + status)) | |
| 3219 | } | |
| 3220 | } else { | |
| 3221 | promise = new Promise(function (resolve, reject) { | |
| 3222 | done = function (status) { | |
| 3223 | if (booleanResult) resolve(status === 0) | |
| 3224 | else if (status === 0) resolve() | |
| 3225 | else reject(new Error('status: ' + status)) | |
| 3226 | } | |
| 3227 | }) | |
| 3228 | } | |
| 3229 | ||
| 3230 | return [done, promise] | |
| 3231 | } |
| … | … | @@ -0,0 +1,62 @@ |
| 1 | { | |
| 2 | "name": "sodium-native", | |
| 3 | "version": "5.1.0", | |
| 4 | "description": "Low level bindings for libsodium", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js", | |
| 8 | "binding.cc", | |
| 9 | "binding.js", | |
| 10 | "extensions", | |
| 11 | "prebuilds", | |
| 12 | "CMakeLists.txt" | |
| 13 | ], | |
| 14 | "imports": { | |
| 15 | "assert": { | |
| 16 | "bare": "bare-assert", | |
| 17 | "node": "assert" | |
| 18 | } | |
| 19 | }, | |
| 20 | "addon": true, | |
| 21 | "dependencies": { | |
| 22 | "bare-assert": "^1.2.0", | |
| 23 | "require-addon": "^1.1.0", | |
| 24 | "which-runtime": "^1.2.1" | |
| 25 | }, | |
| 26 | "devDependencies": { | |
| 27 | "bare-compat-napi": "^1.3.5", | |
| 28 | "brittle": "^3.16.2", | |
| 29 | "cmake-bare": "^1.6.1", | |
| 30 | "cmake-fetch": "^1.4.3", | |
| 31 | "cmake-napi": "^1.2.1", | |
| 32 | "prettier": "^3.6.2", | |
| 33 | "prettier-config-holepunch": "^2.0.0" | |
| 34 | }, | |
| 35 | "scripts": { | |
| 36 | "test": "prettier . --check && npm run test:node && npm run test:bare", | |
| 37 | "test:node": "node test/all.js", | |
| 38 | "test:bare": "bare test/all.js" | |
| 39 | }, | |
| 40 | "standard": { | |
| 41 | "ignore": [ | |
| 42 | "/test/fixtures/*.js" | |
| 43 | ] | |
| 44 | }, | |
| 45 | "engines": { | |
| 46 | "bare": ">=1.16.0" | |
| 47 | }, | |
| 48 | "repository": { | |
| 49 | "type": "git", | |
| 50 | "url": "git+https://github.com/holepunchto/sodium-native.git" | |
| 51 | }, | |
| 52 | "contributors": [ | |
| 53 | "Emil Bay <github@tixz.dk> (http://bayes.dk)", | |
| 54 | "Mathias Buus <mathiasbuus@gmail.com> (https://mafinto.sh)", | |
| 55 | "Christophe Diederichs <chm-diederichs@hyperdivision.dk>" | |
| 56 | ], | |
| 57 | "license": "MIT", | |
| 58 | "bugs": { | |
| 59 | "url": "https://github.com/holepunchto/sodium-native/issues" | |
| 60 | }, | |
| 61 | "homepage": "https://github.com/holepunchto/sodium-native" | |
| 62 | } |
| … | … | @@ -0,0 +1,7 @@ |
| 1 | Copyright 2017 Mathias Buus & Emil Bay | |
| 2 | ||
| 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
| 4 | ||
| 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
| 6 | ||
| 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| … | … | @@ -0,0 +1,288 @@ |
| 1 | # sodium-universal | |
| 2 | ||
| 3 | Universal wrapper for `sodium-javascript` and `sodium-native` working in Node.js and the Browser | |
| 4 | ||
| 5 | ```sh | |
| 6 | npm install sodium-universal | |
| 7 | ``` | |
| 8 | ||
| 9 | `sodium-native` ships with prebuilds for macOS, Windows, and Linux. | |
| 10 | ||
| 11 | Note that the module itself is generated on a prepublish hook. | |
| 12 | ||
| 13 | ## Usage | |
| 14 | ||
| 15 | ```js | |
| 16 | var sodium = require('sodium-universal') | |
| 17 | ||
| 18 | var rnd = Buffer.allocUnsafe(12) // Cryptographically random data | |
| 19 | sodium.randombytes_buf(rnd) | |
| 20 | ||
| 21 | console.log(rnd.toString('hex')) | |
| 22 | ||
| 23 | ``` | |
| 24 | ||
| 25 | Works seamlessly with Node.js: | |
| 26 | ||
| 27 | ```sh | |
| 28 | $ node example.js | |
| 29 | c7dbd46a6cc84ff2e0d1285c | |
| 30 | ``` | |
| 31 | ||
| 32 | And the browser: | |
| 33 | ||
| 34 | First install the peer dep, `sodium-javascript`. | |
| 35 | ||
| 36 | ```sh | |
| 37 | npm install --save sodium-javascript | |
| 38 | ``` | |
| 39 | ||
| 40 | And then browserify | |
| 41 | ||
| 42 | ```sh | |
| 43 | browserify example.js > bundle.js | |
| 44 | ``` | |
| 45 | ||
| 46 | ## Introduction | |
| 47 | ||
| 48 | [`libsodium`](https://download.libsodium.org/doc/) is a collection of cryptographic primitives, providing a low-level foundation to build higher-level cryptographic applications and protocols. It is often put in contrast to RSA based cryptography and OpenSSL, even though they all share overlapping algorithms and target slightly different audiences. Sodium is a collection of modern collection of primitives, fulfilling the same cryptographic tasks, but based on simpler and more efficient algorithms. | |
| 49 | ||
| 50 | This library provides seamless bindings to [`sodium-native`](https://github.com/sodium-friends/sodium-native), which is the original C implementation of `libsodium` exposed as a Node native module. For the browser we expose [`sodium-javascript`](https://github.com/sodium-friends/sodium-javascript), using the `package.json` [`browser`](https://github.com/defunctzombie/package-browser-field-spec) field, which is supported by most bundlers. | |
| 51 | ||
| 52 | ## API | |
| 53 | ||
| 54 | Please refer to [`sodium-native`](https://github.com/sodium-friends/sodium-native#api) and [`sodium-javascript`](https://github.com/sodium-friends/sodium-javascript#api). Note that the two modules do not yet have feature parity, where `sodium-native` is the more featureful of the two. | |
| 55 | ||
| 56 | ## Compatibilty | |
| 57 | ||
| 58 | <!-- Generated by build-scripts/symbols.js with | |
| 59 | sodium-native@3.2.0 | |
| 60 | sodium-javascript@0.6.0 --> | |
| 61 | | C Library Symbol | `sodium-native` | `sodium-javascript` | | |
| 62 | |:--------------------------------------------------------:|:--------------------:|:--------------------:| | |
| 63 | | `crypto_aead_chacha20poly1305_ietf_ABYTES` | :white_check_mark: | :white_check_mark: | | |
| 64 | | `crypto_aead_chacha20poly1305_ietf_decrypt` | :white_check_mark: | :white_check_mark: | | |
| 65 | | `crypto_aead_chacha20poly1305_ietf_decrypt_detached` | :white_check_mark: | :white_check_mark: | | |
| 66 | | `crypto_aead_chacha20poly1305_ietf_encrypt` | :white_check_mark: | :white_check_mark: | | |
| 67 | | `crypto_aead_chacha20poly1305_ietf_encrypt_detached` | :white_check_mark: | :white_check_mark: | | |
| 68 | | `crypto_aead_chacha20poly1305_ietf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 69 | | `crypto_aead_chacha20poly1305_ietf_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 70 | | `crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 71 | | `crypto_aead_chacha20poly1305_ietf_NPUBBYTES` | :white_check_mark: | :white_check_mark: | | |
| 72 | | `crypto_aead_chacha20poly1305_ietf_NSECBYTES` | :white_check_mark: | :white_check_mark: | | |
| 73 | | `crypto_aead_xchacha20poly1305_ietf_ABYTES` | :white_check_mark: | :small_red_triangle: | | |
| 74 | | `crypto_aead_xchacha20poly1305_ietf_decrypt` | :white_check_mark: | :small_red_triangle: | | |
| 75 | | `crypto_aead_xchacha20poly1305_ietf_decrypt_detached` | :white_check_mark: | :small_red_triangle: | | |
| 76 | | `crypto_aead_xchacha20poly1305_ietf_encrypt` | :white_check_mark: | :small_red_triangle: | | |
| 77 | | `crypto_aead_xchacha20poly1305_ietf_encrypt_detached` | :white_check_mark: | :small_red_triangle: | | |
| 78 | | `crypto_aead_xchacha20poly1305_ietf_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 79 | | `crypto_aead_xchacha20poly1305_ietf_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 80 | | `crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 81 | | `crypto_aead_xchacha20poly1305_ietf_NPUBBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 82 | | `crypto_aead_xchacha20poly1305_ietf_NSECBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 83 | | `crypto_auth` | :white_check_mark: | :small_red_triangle: | | |
| 84 | | `crypto_auth_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 85 | | `crypto_auth_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 86 | | `crypto_auth_verify` | :white_check_mark: | :small_red_triangle: | | |
| 87 | | `crypto_box_BEFORENMBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 88 | | `crypto_box_BOXZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 89 | | `crypto_box_detached` | :white_check_mark: | :small_red_triangle: | | |
| 90 | | `crypto_box_easy` | :white_check_mark: | :small_red_triangle: | | |
| 91 | | `crypto_box_keypair` | :white_check_mark: | :white_check_mark: | | |
| 92 | | `crypto_box_MACBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 93 | | `crypto_box_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 94 | | `crypto_box_open_detached` | :white_check_mark: | :small_red_triangle: | | |
| 95 | | `crypto_box_open_easy` | :white_check_mark: | :small_red_triangle: | | |
| 96 | | `crypto_box_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 97 | | `crypto_box_seal` | :white_check_mark: | :white_check_mark: | | |
| 98 | | `crypto_box_seal_open` | :white_check_mark: | :white_check_mark: | | |
| 99 | | `crypto_box_SEALBYTES` | :white_check_mark: | :white_check_mark: | | |
| 100 | | `crypto_box_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 101 | | `crypto_box_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 102 | | `crypto_box_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 103 | | `crypto_box_ZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 104 | | `crypto_core_ed25519_add` | :white_check_mark: | :small_red_triangle: | | |
| 105 | | `crypto_core_ed25519_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 106 | | `crypto_core_ed25519_from_uniform` | :white_check_mark: | :small_red_triangle: | | |
| 107 | | `crypto_core_ed25519_is_valid_point` | :white_check_mark: | :small_red_triangle: | | |
| 108 | | `crypto_core_ed25519_NONREDUCEDSCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 109 | | `crypto_core_ed25519_scalar_add` | :white_check_mark: | :small_red_triangle: | | |
| 110 | | `crypto_core_ed25519_scalar_complement` | :white_check_mark: | :small_red_triangle: | | |
| 111 | | `crypto_core_ed25519_scalar_invert` | :white_check_mark: | :small_red_triangle: | | |
| 112 | | `crypto_core_ed25519_scalar_negate` | :white_check_mark: | :small_red_triangle: | | |
| 113 | | `crypto_core_ed25519_scalar_random` | :white_check_mark: | :small_red_triangle: | | |
| 114 | | `crypto_core_ed25519_scalar_reduce` | :white_check_mark: | :small_red_triangle: | | |
| 115 | | `crypto_core_ed25519_scalar_sub` | :white_check_mark: | :small_red_triangle: | | |
| 116 | | `crypto_core_ed25519_SCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 117 | | `crypto_core_ed25519_sub` | :white_check_mark: | :small_red_triangle: | | |
| 118 | | `crypto_core_ed25519_UNIFORMBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 119 | | `crypto_generichash` | :white_check_mark: | :white_check_mark: | | |
| 120 | | `crypto_generichash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 121 | | `crypto_generichash_BYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 122 | | `crypto_generichash_BYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 123 | | `crypto_generichash_final` | :white_check_mark: | :small_red_triangle: | | |
| 124 | | `crypto_generichash_init` | :white_check_mark: | :small_red_triangle: | | |
| 125 | | `crypto_generichash_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 126 | | `crypto_generichash_KEYBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 127 | | `crypto_generichash_KEYBYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 128 | | `crypto_generichash_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 129 | | `crypto_generichash_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 130 | | `crypto_generichash_update` | :white_check_mark: | :small_red_triangle: | | |
| 131 | | `crypto_hash` | :white_check_mark: | :white_check_mark: | | |
| 132 | | `crypto_hash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 133 | | `crypto_hash_sha256` | :white_check_mark: | :white_check_mark: | | |
| 134 | | `crypto_hash_sha256_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 135 | | `crypto_hash_sha256_final` | :white_check_mark: | :small_red_triangle: | | |
| 136 | | `crypto_hash_sha256_init` | :white_check_mark: | :small_red_triangle: | | |
| 137 | | `crypto_hash_sha256_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 138 | | `crypto_hash_sha256_update` | :white_check_mark: | :small_red_triangle: | | |
| 139 | | `crypto_hash_sha512` | :white_check_mark: | :white_check_mark: | | |
| 140 | | `crypto_hash_sha512_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 141 | | `crypto_hash_sha512_final` | :white_check_mark: | :small_red_triangle: | | |
| 142 | | `crypto_hash_sha512_init` | :white_check_mark: | :small_red_triangle: | | |
| 143 | | `crypto_hash_sha512_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 144 | | `crypto_hash_sha512_update` | :white_check_mark: | :small_red_triangle: | | |
| 145 | | `crypto_kdf_BYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 146 | | `crypto_kdf_BYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 147 | | `crypto_kdf_CONTEXTBYTES` | :white_check_mark: | :white_check_mark: | | |
| 148 | | `crypto_kdf_derive_from_key` | :white_check_mark: | :white_check_mark: | | |
| 149 | | `crypto_kdf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 150 | | `crypto_kdf_keygen` | :white_check_mark: | :white_check_mark: | | |
| 151 | | `crypto_kx_client_session_keys` | :white_check_mark: | :small_red_triangle: | | |
| 152 | | `crypto_kx_keypair` | :white_check_mark: | :white_check_mark: | | |
| 153 | | `crypto_kx_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 154 | | `crypto_kx_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 155 | | `crypto_kx_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 156 | | `crypto_kx_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 157 | | `crypto_kx_server_session_keys` | :white_check_mark: | :small_red_triangle: | | |
| 158 | | `crypto_kx_SESSIONKEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 159 | | `crypto_onetimeauth` | :white_check_mark: | :white_check_mark: | | |
| 160 | | `crypto_onetimeauth_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 161 | | `crypto_onetimeauth_final` | :white_check_mark: | :small_red_triangle: | | |
| 162 | | `crypto_onetimeauth_init` | :white_check_mark: | :small_red_triangle: | | |
| 163 | | `crypto_onetimeauth_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 164 | | `crypto_onetimeauth_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 165 | | `crypto_onetimeauth_update` | :white_check_mark: | :small_red_triangle: | | |
| 166 | | `crypto_onetimeauth_verify` | :white_check_mark: | :white_check_mark: | | |
| 167 | | `crypto_pwhash` | :white_check_mark: | :small_red_triangle: | | |
| 168 | | `crypto_pwhash_BYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 169 | | `crypto_pwhash_BYTES_MIN` | :white_check_mark: | :small_red_triangle: | | |
| 170 | | `crypto_pwhash_SALTBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 171 | | `crypto_pwhash_scryptsalsa208sha256` | :white_check_mark: | :small_red_triangle: | | |
| 172 | | `crypto_pwhash_scryptsalsa208sha256_BYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 173 | | `crypto_pwhash_scryptsalsa208sha256_BYTES_MIN` | :white_check_mark: | :small_red_triangle: | | |
| 174 | | `crypto_pwhash_scryptsalsa208sha256_SALTBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 175 | | `crypto_pwhash_scryptsalsa208sha256_str` | :white_check_mark: | :small_red_triangle: | | |
| 176 | | `crypto_pwhash_scryptsalsa208sha256_str_needs_rehash` | :white_check_mark: | :small_red_triangle: | | |
| 177 | | `crypto_pwhash_scryptsalsa208sha256_str_verify` | :white_check_mark: | :small_red_triangle: | | |
| 178 | | `crypto_pwhash_scryptsalsa208sha256_STRBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 179 | | `crypto_pwhash_str` | :white_check_mark: | :small_red_triangle: | | |
| 180 | | `crypto_pwhash_str_needs_rehash` | :white_check_mark: | :small_red_triangle: | | |
| 181 | | `crypto_pwhash_str_verify` | :white_check_mark: | :small_red_triangle: | | |
| 182 | | `crypto_pwhash_STRBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 183 | | `crypto_scalarmult` | :white_check_mark: | :white_check_mark: | | |
| 184 | | `crypto_scalarmult_base` | :white_check_mark: | :white_check_mark: | | |
| 185 | | `crypto_scalarmult_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 186 | | `crypto_scalarmult_ed25519` | :white_check_mark: | :small_red_triangle: | | |
| 187 | | `crypto_scalarmult_ed25519_base` | :white_check_mark: | :small_red_triangle: | | |
| 188 | | `crypto_scalarmult_ed25519_base_noclamp` | :white_check_mark: | :small_red_triangle: | | |
| 189 | | `crypto_scalarmult_ed25519_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 190 | | `crypto_scalarmult_ed25519_noclamp` | :white_check_mark: | :small_red_triangle: | | |
| 191 | | `crypto_scalarmult_ed25519_SCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 192 | | `crypto_scalarmult_SCALARBYTES` | :white_check_mark: | :white_check_mark: | | |
| 193 | | `crypto_secretbox` | :small_red_triangle: | :white_check_mark: | | |
| 194 | | `crypto_secretbox_BOXZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 195 | | `crypto_secretbox_detached` | :white_check_mark: | :white_check_mark: | | |
| 196 | | `crypto_secretbox_easy` | :white_check_mark: | :white_check_mark: | | |
| 197 | | `crypto_secretbox_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 198 | | `crypto_secretbox_MACBYTES` | :white_check_mark: | :white_check_mark: | | |
| 199 | | `crypto_secretbox_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 200 | | `crypto_secretbox_open` | :small_red_triangle: | :white_check_mark: | | |
| 201 | | `crypto_secretbox_open_detached` | :white_check_mark: | :white_check_mark: | | |
| 202 | | `crypto_secretbox_open_easy` | :white_check_mark: | :white_check_mark: | | |
| 203 | | `crypto_secretbox_ZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 204 | | `crypto_secretstream_xchacha20poly1305_ABYTES` | :white_check_mark: | :small_red_triangle: | | |
| 205 | | `crypto_secretstream_xchacha20poly1305_HEADERBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 206 | | `crypto_secretstream_xchacha20poly1305_init_pull` | :white_check_mark: | :small_red_triangle: | | |
| 207 | | `crypto_secretstream_xchacha20poly1305_init_push` | :white_check_mark: | :small_red_triangle: | | |
| 208 | | `crypto_secretstream_xchacha20poly1305_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 209 | | `crypto_secretstream_xchacha20poly1305_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 210 | | `crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 211 | | `crypto_secretstream_xchacha20poly1305_pull` | :white_check_mark: | :small_red_triangle: | | |
| 212 | | `crypto_secretstream_xchacha20poly1305_push` | :white_check_mark: | :small_red_triangle: | | |
| 213 | | `crypto_secretstream_xchacha20poly1305_rekey` | :white_check_mark: | :small_red_triangle: | | |
| 214 | | `crypto_secretstream_xchacha20poly1305_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 215 | | `crypto_shorthash` | :white_check_mark: | :white_check_mark: | | |
| 216 | | `crypto_shorthash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 217 | | `crypto_shorthash_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 218 | | `crypto_sign` | :white_check_mark: | :white_check_mark: | | |
| 219 | | `crypto_sign_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 220 | | `crypto_sign_detached` | :white_check_mark: | :white_check_mark: | | |
| 221 | | `crypto_sign_ed25519_pk_to_curve25519` | :white_check_mark: | :small_red_triangle: | | |
| 222 | | `crypto_sign_ed25519_sk_to_curve25519` | :white_check_mark: | :small_red_triangle: | | |
| 223 | | `crypto_sign_ed25519_sk_to_pk` | :white_check_mark: | :small_red_triangle: | | |
| 224 | | `crypto_sign_keypair` | :white_check_mark: | :white_check_mark: | | |
| 225 | | `crypto_sign_open` | :white_check_mark: | :white_check_mark: | | |
| 226 | | `crypto_sign_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 227 | | `crypto_sign_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 228 | | `crypto_sign_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 229 | | `crypto_sign_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 230 | | `crypto_sign_verify_detached` | :white_check_mark: | :white_check_mark: | | |
| 231 | | `crypto_stream` | :white_check_mark: | :white_check_mark: | | |
| 232 | | `crypto_stream_chacha20` | :white_check_mark: | :white_check_mark: | | |
| 233 | | `crypto_stream_chacha20_ietf` | :white_check_mark: | :white_check_mark: | | |
| 234 | | `crypto_stream_chacha20_ietf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 235 | | `crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 236 | | `crypto_stream_chacha20_ietf_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 237 | | `crypto_stream_chacha20_ietf_xor` | :white_check_mark: | :white_check_mark: | | |
| 238 | | `crypto_stream_chacha20_ietf_xor_ic` | :white_check_mark: | :white_check_mark: | | |
| 239 | | `crypto_stream_chacha20_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 240 | | `crypto_stream_chacha20_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 241 | | `crypto_stream_chacha20_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 242 | | `crypto_stream_chacha20_xor` | :white_check_mark: | :white_check_mark: | | |
| 243 | | `crypto_stream_chacha20_xor_ic` | :white_check_mark: | :white_check_mark: | | |
| 244 | | `crypto_stream_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 245 | | `crypto_stream_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 246 | | `crypto_stream_salsa20_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 247 | | `crypto_stream_salsa20_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 248 | | `crypto_stream_salsa20_NONCEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 249 | | `crypto_stream_salsa20_xor` | :white_check_mark: | :small_red_triangle: | | |
| 250 | | `crypto_stream_salsa20_xor_ic` | :white_check_mark: | :small_red_triangle: | | |
| 251 | | `crypto_stream_xchacha20` | :white_check_mark: | :small_red_triangle: | | |
| 252 | | `crypto_stream_xchacha20_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 253 | | `crypto_stream_xchacha20_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 254 | | `crypto_stream_xchacha20_NONCEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 255 | | `crypto_stream_xchacha20_xor` | :white_check_mark: | :small_red_triangle: | | |
| 256 | | `crypto_stream_xchacha20_xor_ic` | :white_check_mark: | :small_red_triangle: | | |
| 257 | | `crypto_stream_xor` | :white_check_mark: | :white_check_mark: | | |
| 258 | | `crypto_verify_16` | :small_red_triangle: | :white_check_mark: | | |
| 259 | | `crypto_verify_32` | :small_red_triangle: | :white_check_mark: | | |
| 260 | | `randombytes_buf` | :white_check_mark: | :white_check_mark: | | |
| 261 | | `randombytes_buf_deterministic` | :white_check_mark: | :small_red_triangle: | | |
| 262 | | `randombytes_random` | :white_check_mark: | :small_red_triangle: | | |
| 263 | | `randombytes_SEEDBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 264 | | `randombytes_uniform` | :white_check_mark: | :small_red_triangle: | | |
| 265 | | `sodium_add` | :white_check_mark: | :small_red_triangle: | | |
| 266 | | `sodium_compare` | :white_check_mark: | :small_red_triangle: | | |
| 267 | | `sodium_free` | :white_check_mark: | :small_red_triangle: | | |
| 268 | | `sodium_increment` | :white_check_mark: | :small_red_triangle: | | |
| 269 | | `sodium_is_zero` | :white_check_mark: | :white_check_mark: | | |
| 270 | | `sodium_malloc` | :white_check_mark: | :white_check_mark: | | |
| 271 | | `sodium_memcmp` | :white_check_mark: | :white_check_mark: | | |
| 272 | | `sodium_memzero` | :white_check_mark: | :white_check_mark: | | |
| 273 | | `sodium_mlock` | :white_check_mark: | :small_red_triangle: | | |
| 274 | | `sodium_mprotect_noaccess` | :white_check_mark: | :small_red_triangle: | | |
| 275 | | `sodium_mprotect_readonly` | :white_check_mark: | :small_red_triangle: | | |
| 276 | | `sodium_mprotect_readwrite` | :white_check_mark: | :small_red_triangle: | | |
| 277 | | `sodium_munlock` | :white_check_mark: | :small_red_triangle: | | |
| 278 | | `sodium_pad` | :white_check_mark: | :small_red_triangle: | | |
| 279 | | `sodium_sub` | :white_check_mark: | :small_red_triangle: | | |
| 280 | | `sodium_unpad` | :white_check_mark: | :small_red_triangle: | | |
| 281 | ||
| 282 | ### Missing altogether (Ctrl + F friendly) | |
| 283 | ||
| 284 | `crypto_aead_aes256gcm_ABYTES`, `crypto_aead_aes256gcm_beforenm`, `crypto_aead_aes256gcm_decrypt`, `crypto_aead_aes256gcm_decrypt_afternm`, `crypto_aead_aes256gcm_decrypt_detached`, `crypto_aead_aes256gcm_decrypt_detached_afternm`, `crypto_aead_aes256gcm_encrypt`, `crypto_aead_aes256gcm_encrypt_afternm`, `crypto_aead_aes256gcm_encrypt_detached`, `crypto_aead_aes256gcm_encrypt_detached_afternm`, `crypto_aead_aes256gcm_is_available`, `crypto_aead_aes256gcm_KEYBYTES`, `crypto_aead_aes256gcm_keygen`, `crypto_aead_aes256gcm_MESSAGEBYTES_MAX`, `crypto_aead_aes256gcm_NPUBBYTES`, `crypto_aead_aes256gcm_NSECBYTES`, `crypto_aead_aes256gcm_STATEBYTES`, `crypto_aead_chacha20poly1305_ABYTES`, `crypto_aead_chacha20poly1305_decrypt`, `crypto_aead_chacha20poly1305_decrypt_detached`, `crypto_aead_chacha20poly1305_encrypt`, `crypto_aead_chacha20poly1305_encrypt_detached`, `crypto_aead_chacha20poly1305_KEYBYTES`, `crypto_aead_chacha20poly1305_keygen`, `crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX`, `crypto_aead_chacha20poly1305_NPUBBYTES`, `crypto_aead_chacha20poly1305_NSECBYTES`, `crypto_auth_hmacsha256`, `crypto_auth_hmacsha256_BYTES`, `crypto_auth_hmacsha256_final`, `crypto_auth_hmacsha256_init`, `crypto_auth_hmacsha256_KEYBYTES`, `crypto_auth_hmacsha256_keygen`, `crypto_auth_hmacsha256_STATEBYTES`, `crypto_auth_hmacsha256_update`, `crypto_auth_hmacsha256_verify`, `crypto_auth_hmacsha512`, `crypto_auth_hmacsha512256`, `crypto_auth_hmacsha512256_BYTES`, `crypto_auth_hmacsha512256_final`, `crypto_auth_hmacsha512256_init`, `crypto_auth_hmacsha512256_KEYBYTES`, `crypto_auth_hmacsha512256_keygen`, `crypto_auth_hmacsha512256_STATEBYTES`, `crypto_auth_hmacsha512256_update`, `crypto_auth_hmacsha512256_verify`, `crypto_auth_hmacsha512_BYTES`, `crypto_auth_hmacsha512_final`, `crypto_auth_hmacsha512_init`, `crypto_auth_hmacsha512_KEYBYTES`, `crypto_auth_hmacsha512_keygen`, `crypto_auth_hmacsha512_STATEBYTES`, `crypto_auth_hmacsha512_update`, `crypto_auth_hmacsha512_verify`, `crypto_auth_keygen`, `crypto_auth_primitive`, `crypto_box`, `crypto_box_afternm`, `crypto_box_beforenm`, `crypto_box_curve25519xchacha20poly1305_beforenm`, `crypto_box_curve25519xchacha20poly1305_BEFORENMBYTES`, `crypto_box_curve25519xchacha20poly1305_detached`, `crypto_box_curve25519xchacha20poly1305_detached_afternm`, `crypto_box_curve25519xchacha20poly1305_easy`, `crypto_box_curve25519xchacha20poly1305_easy_afternm`, `crypto_box_curve25519xchacha20poly1305_keypair`, `crypto_box_curve25519xchacha20poly1305_MACBYTES`, `crypto_box_curve25519xchacha20poly1305_MESSAGEBYTES_MAX`, `crypto_box_curve25519xchacha20poly1305_NONCEBYTES`, `crypto_box_curve25519xchacha20poly1305_open_detached`, `crypto_box_curve25519xchacha20poly1305_open_detached_afternm`, `crypto_box_curve25519xchacha20poly1305_open_easy`, `crypto_box_curve25519xchacha20poly1305_open_easy_afternm`, `crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES`, `crypto_box_curve25519xchacha20poly1305_seal`, `crypto_box_curve25519xchacha20poly1305_seal_open`, `crypto_box_curve25519xchacha20poly1305_SEALBYTES`, `crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES`, `crypto_box_curve25519xchacha20poly1305_seed_keypair`, `crypto_box_curve25519xchacha20poly1305_SEEDBYTES`, `crypto_box_curve25519xsalsa20poly1305`, `crypto_box_curve25519xsalsa20poly1305_afternm`, `crypto_box_curve25519xsalsa20poly1305_beforenm`, `crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES`, `crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES`, `crypto_box_curve25519xsalsa20poly1305_keypair`, `crypto_box_curve25519xsalsa20poly1305_MACBYTES`, `crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX`, `crypto_box_curve25519xsalsa20poly1305_NONCEBYTES`, `crypto_box_curve25519xsalsa20poly1305_open`, `crypto_box_curve25519xsalsa20poly1305_open_afternm`, `crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES`, `crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES`, `crypto_box_curve25519xsalsa20poly1305_seed_keypair`, `crypto_box_curve25519xsalsa20poly1305_SEEDBYTES`, `crypto_box_curve25519xsalsa20poly1305_ZEROBYTES`, `crypto_box_detached_afternm`, `crypto_box_easy_afternm`, `crypto_box_MESSAGEBYTES_MAX`, `crypto_box_open`, `crypto_box_open_afternm`, `crypto_box_open_detached_afternm`, `crypto_box_open_easy_afternm`, `crypto_box_primitive`, `crypto_core_ed25519_from_hash`, `crypto_core_ed25519_HASHBYTES`, `crypto_core_ed25519_random`, `crypto_core_ed25519_scalar_mul`, `crypto_core_hchacha20`, `crypto_core_hchacha20_CONSTBYTES`, `crypto_core_hchacha20_INPUTBYTES`, `crypto_core_hchacha20_KEYBYTES`, `crypto_core_hchacha20_OUTPUTBYTES`, `crypto_core_hsalsa20`, `crypto_core_hsalsa20_CONSTBYTES`, `crypto_core_hsalsa20_INPUTBYTES`, `crypto_core_hsalsa20_KEYBYTES`, `crypto_core_hsalsa20_OUTPUTBYTES`, `crypto_core_ristretto255_add`, `crypto_core_ristretto255_BYTES`, `crypto_core_ristretto255_from_hash`, `crypto_core_ristretto255_HASHBYTES`, `crypto_core_ristretto255_is_valid_point`, `crypto_core_ristretto255_NONREDUCEDSCALARBYTES`, `crypto_core_ristretto255_random`, `crypto_core_ristretto255_scalar_add`, `crypto_core_ristretto255_scalar_complement`, `crypto_core_ristretto255_scalar_invert`, `crypto_core_ristretto255_scalar_mul`, `crypto_core_ristretto255_scalar_negate`, `crypto_core_ristretto255_scalar_random`, `crypto_core_ristretto255_scalar_reduce`, `crypto_core_ristretto255_scalar_sub`, `crypto_core_ristretto255_SCALARBYTES`, `crypto_core_ristretto255_sub`, `crypto_core_salsa20`, `crypto_core_salsa2012`, `crypto_core_salsa2012_CONSTBYTES`, `crypto_core_salsa2012_INPUTBYTES`, `crypto_core_salsa2012_KEYBYTES`, `crypto_core_salsa2012_OUTPUTBYTES`, `crypto_core_salsa208`, `crypto_core_salsa208_CONSTBYTES`, `crypto_core_salsa208_INPUTBYTES`, `crypto_core_salsa208_KEYBYTES`, `crypto_core_salsa208_OUTPUTBYTES`, `crypto_core_salsa20_CONSTBYTES`, `crypto_core_salsa20_INPUTBYTES`, `crypto_core_salsa20_KEYBYTES`, `crypto_core_salsa20_OUTPUTBYTES`, `crypto_generichash_blake2b`, `crypto_generichash_blake2b_BYTES`, `crypto_generichash_blake2b_BYTES_MAX`, `crypto_generichash_blake2b_BYTES_MIN`, `crypto_generichash_blake2b_final`, `crypto_generichash_blake2b_init`, `crypto_generichash_blake2b_init_salt_personal`, `crypto_generichash_blake2b_KEYBYTES`, `crypto_generichash_blake2b_KEYBYTES_MAX`, `crypto_generichash_blake2b_KEYBYTES_MIN`, `crypto_generichash_blake2b_keygen`, `crypto_generichash_blake2b_PERSONALBYTES`, `crypto_generichash_blake2b_salt_personal`, `crypto_generichash_blake2b_SALTBYTES`, `crypto_generichash_blake2b_STATEBYTES`, `crypto_generichash_blake2b_update`, `crypto_generichash_primitive`, `crypto_hash_primitive`, `crypto_kdf_blake2b_BYTES_MAX`, `crypto_kdf_blake2b_BYTES_MIN`, `crypto_kdf_blake2b_CONTEXTBYTES`, `crypto_kdf_blake2b_derive_from_key`, `crypto_kdf_blake2b_KEYBYTES`, `crypto_kdf_primitive`, `crypto_kx_primitive`, `crypto_onetimeauth_keygen`, `crypto_onetimeauth_poly1305`, `crypto_onetimeauth_poly1305_BYTES`, `crypto_onetimeauth_poly1305_final`, `crypto_onetimeauth_poly1305_init`, `crypto_onetimeauth_poly1305_KEYBYTES`, `crypto_onetimeauth_poly1305_keygen`, `crypto_onetimeauth_poly1305_STATEBYTES`, `crypto_onetimeauth_poly1305_update`, `crypto_onetimeauth_poly1305_verify`, `crypto_onetimeauth_primitive`, `crypto_pwhash_alg_argon2i13`, `crypto_pwhash_alg_argon2id13`, `crypto_pwhash_alg_default`, `crypto_pwhash_argon2i`, `crypto_pwhash_argon2i_alg_argon2i13`, `crypto_pwhash_argon2i_BYTES_MAX`, `crypto_pwhash_argon2i_BYTES_MIN`, `crypto_pwhash_argon2i_memlimit_interactive`, `crypto_pwhash_argon2i_memlimit_max`, `crypto_pwhash_argon2i_memlimit_min`, `crypto_pwhash_argon2i_memlimit_moderate`, `crypto_pwhash_argon2i_memlimit_sensitive`, `crypto_pwhash_argon2i_opslimit_interactive`, `crypto_pwhash_argon2i_opslimit_max`, `crypto_pwhash_argon2i_opslimit_min`, `crypto_pwhash_argon2i_opslimit_moderate`, `crypto_pwhash_argon2i_opslimit_sensitive`, `crypto_pwhash_argon2i_passwd_max`, `crypto_pwhash_argon2i_passwd_min`, `crypto_pwhash_argon2i_SALTBYTES`, `crypto_pwhash_argon2i_str`, `crypto_pwhash_argon2i_str_needs_rehash`, `crypto_pwhash_argon2i_str_verify`, `crypto_pwhash_argon2i_STRBYTES`, `crypto_pwhash_argon2i_strprefix`, `crypto_pwhash_argon2id`, `crypto_pwhash_argon2id_alg_argon2id13`, `crypto_pwhash_argon2id_BYTES_MAX`, `crypto_pwhash_argon2id_BYTES_MIN`, `crypto_pwhash_argon2id_memlimit_interactive`, `crypto_pwhash_argon2id_memlimit_max`, `crypto_pwhash_argon2id_memlimit_min`, `crypto_pwhash_argon2id_memlimit_moderate`, `crypto_pwhash_argon2id_memlimit_sensitive`, `crypto_pwhash_argon2id_opslimit_interactive`, `crypto_pwhash_argon2id_opslimit_max`, `crypto_pwhash_argon2id_opslimit_min`, `crypto_pwhash_argon2id_opslimit_moderate`, `crypto_pwhash_argon2id_opslimit_sensitive`, `crypto_pwhash_argon2id_passwd_max`, `crypto_pwhash_argon2id_passwd_min`, `crypto_pwhash_argon2id_SALTBYTES`, `crypto_pwhash_argon2id_str`, `crypto_pwhash_argon2id_str_needs_rehash`, `crypto_pwhash_argon2id_str_verify`, `crypto_pwhash_argon2id_STRBYTES`, `crypto_pwhash_argon2id_strprefix`, `crypto_pwhash_memlimit_interactive`, `crypto_pwhash_memlimit_max`, `crypto_pwhash_memlimit_min`, `crypto_pwhash_memlimit_moderate`, `crypto_pwhash_memlimit_sensitive`, `crypto_pwhash_opslimit_interactive`, `crypto_pwhash_opslimit_max`, `crypto_pwhash_opslimit_min`, `crypto_pwhash_opslimit_moderate`, `crypto_pwhash_opslimit_sensitive`, `crypto_pwhash_passwd_max`, `crypto_pwhash_passwd_min`, `crypto_pwhash_primitive`, `crypto_pwhash_scryptsalsa208sha256_ll`, `crypto_pwhash_scryptsalsa208sha256_memlimit_interactive`, `crypto_pwhash_scryptsalsa208sha256_memlimit_max`, `crypto_pwhash_scryptsalsa208sha256_memlimit_min`, `crypto_pwhash_scryptsalsa208sha256_memlimit_sensitive`, `crypto_pwhash_scryptsalsa208sha256_opslimit_interactive`, `crypto_pwhash_scryptsalsa208sha256_opslimit_max`, `crypto_pwhash_scryptsalsa208sha256_opslimit_min`, `crypto_pwhash_scryptsalsa208sha256_opslimit_sensitive`, `crypto_pwhash_scryptsalsa208sha256_passwd_max`, `crypto_pwhash_scryptsalsa208sha256_passwd_min`, `crypto_pwhash_scryptsalsa208sha256_strprefix`, `crypto_pwhash_str_alg`, `crypto_pwhash_strprefix`, `crypto_scalarmult_curve25519`, `crypto_scalarmult_curve25519_base`, `crypto_scalarmult_curve25519_BYTES`, `crypto_scalarmult_curve25519_SCALARBYTES`, `crypto_scalarmult_primitive`, `crypto_scalarmult_ristretto255`, `crypto_scalarmult_ristretto255_base`, `crypto_scalarmult_ristretto255_BYTES`, `crypto_scalarmult_ristretto255_SCALARBYTES`, `crypto_secretbox_keygen`, `crypto_secretbox_MESSAGEBYTES_MAX`, `crypto_secretbox_primitive`, `crypto_secretbox_xchacha20poly1305_detached`, `crypto_secretbox_xchacha20poly1305_easy`, `crypto_secretbox_xchacha20poly1305_KEYBYTES`, `crypto_secretbox_xchacha20poly1305_MACBYTES`, `crypto_secretbox_xchacha20poly1305_MESSAGEBYTES_MAX`, `crypto_secretbox_xchacha20poly1305_NONCEBYTES`, `crypto_secretbox_xchacha20poly1305_open_detached`, `crypto_secretbox_xchacha20poly1305_open_easy`, `crypto_secretbox_xsalsa20poly1305`, `crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES`, `crypto_secretbox_xsalsa20poly1305_KEYBYTES`, `crypto_secretbox_xsalsa20poly1305_keygen`, `crypto_secretbox_xsalsa20poly1305_MACBYTES`, `crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX`, `crypto_secretbox_xsalsa20poly1305_NONCEBYTES`, `crypto_secretbox_xsalsa20poly1305_open`, `crypto_secretbox_xsalsa20poly1305_ZEROBYTES`, `crypto_secretstream_xchacha20poly1305_tag_final`, `crypto_secretstream_xchacha20poly1305_tag_message`, `crypto_secretstream_xchacha20poly1305_tag_push`, `crypto_secretstream_xchacha20poly1305_tag_rekey`, `crypto_shorthash_keygen`, `crypto_shorthash_primitive`, `crypto_shorthash_siphash24`, `crypto_shorthash_siphash24_BYTES`, `crypto_shorthash_siphash24_KEYBYTES`, `crypto_shorthash_siphashx24`, `crypto_shorthash_siphashx24_BYTES`, `crypto_shorthash_siphashx24_KEYBYTES`, `crypto_sign_ed25519`, `crypto_sign_ed25519_BYTES`, `crypto_sign_ed25519_detached`, `crypto_sign_ed25519_keypair`, `crypto_sign_ed25519_MESSAGEBYTES_MAX`, `crypto_sign_ed25519_open`, `crypto_sign_ed25519_PUBLICKEYBYTES`, `crypto_sign_ed25519_SECRETKEYBYTES`, `crypto_sign_ed25519_seed_keypair`, `crypto_sign_ed25519_SEEDBYTES`, `crypto_sign_ed25519_sk_to_seed`, `crypto_sign_ed25519_verify_detached`, `crypto_sign_ed25519ph_final_create`, `crypto_sign_ed25519ph_final_verify`, `crypto_sign_ed25519ph_init`, `crypto_sign_ed25519ph_STATEBYTES`, `crypto_sign_ed25519ph_update`, `crypto_sign_edwards25519sha512batch`, `crypto_sign_edwards25519sha512batch_keypair`, `crypto_sign_edwards25519sha512batch_open`, `crypto_sign_final_create`, `crypto_sign_final_verify`, `crypto_sign_init`, `crypto_sign_MESSAGEBYTES_MAX`, `crypto_sign_primitive`, `crypto_sign_STATEBYTES`, `crypto_sign_update`, `crypto_stream_chacha20_ietf_keygen`, `crypto_stream_chacha20_keygen`, `crypto_stream_keygen`, `crypto_stream_MESSAGEBYTES_MAX`, `crypto_stream_primitive`, `crypto_stream_salsa20`, `crypto_stream_salsa2012`, `crypto_stream_salsa2012_KEYBYTES`, `crypto_stream_salsa2012_keygen`, `crypto_stream_salsa2012_MESSAGEBYTES_MAX`, `crypto_stream_salsa2012_NONCEBYTES`, `crypto_stream_salsa2012_xor`, `crypto_stream_salsa208`, `crypto_stream_salsa208_KEYBYTES`, `crypto_stream_salsa208_keygen`, `crypto_stream_salsa208_MESSAGEBYTES_MAX`, `crypto_stream_salsa208_NONCEBYTES`, `crypto_stream_salsa208_xor`, `crypto_stream_salsa20_keygen`, `crypto_stream_xchacha20_keygen`, `crypto_stream_xsalsa20`, `crypto_stream_xsalsa20_KEYBYTES`, `crypto_stream_xsalsa20_keygen`, `crypto_stream_xsalsa20_MESSAGEBYTES_MAX`, `crypto_stream_xsalsa20_NONCEBYTES`, `crypto_stream_xsalsa20_xor`, `crypto_stream_xsalsa20_xor_ic`, `crypto_verify_16_BYTES`, `crypto_verify_32_BYTES`, `crypto_verify_64`, `crypto_verify_64_BYTES`, `randombytes`, `randombytes_close`, `randombytes_implementation_name`, `randombytes_internal_implementation`, `randombytes_set_implementation`, `randombytes_stir`, `randombytes_sysrandom_implementation`, `sodium_allocarray`, `sodium_base642bin`, `sodium_base64_encoded_len`, `sodium_bin2base64`, `sodium_bin2hex`, `sodium_hex2bin`, `sodium_init`, `sodium_library_minimal`, `sodium_library_version_major`, `sodium_library_version_minor`, `sodium_misuse`, `sodium_runtime_has_aesni`, `sodium_runtime_has_avx`, `sodium_runtime_has_avx2`, `sodium_runtime_has_avx512f`, `sodium_runtime_has_neon`, `sodium_runtime_has_pclmul`, `sodium_runtime_has_rdrand`, `sodium_runtime_has_sse2`, `sodium_runtime_has_sse3`, `sodium_runtime_has_sse41`, `sodium_runtime_has_ssse3`, `sodium_set_misuse_handler`, `sodium_stackzero`, `sodium_version_string` | |
| 285 | ||
| 286 | ## License | |
| 287 | ||
| 288 | [MIT](LICENSE.md) |
| … | … | @@ -0,0 +1,33 @@ |
| 1 | #!/usr/bin/env node | |
| 2 | ||
| 3 | const fs = require('fs') | |
| 4 | const path = require('path') | |
| 5 | const root = path.dirname(require.resolve('sodium-javascript/package.json')) | |
| 6 | ||
| 7 | const tmp = "module.exports = require('sodium-native')\n" | |
| 8 | ||
| 9 | function recurse (dir) { | |
| 10 | const ls = fs.readdirSync(dir) | |
| 11 | const subdir = path.relative(root, dir) | |
| 12 | if (subdir) fs.mkdirSync(subdir, { recursive: true }) | |
| 13 | ||
| 14 | for (const file of ls) { | |
| 15 | if (file === 'internal') recurse(path.join(dir, file)) | |
| 16 | if (!/\.js$/i.test(file)) continue | |
| 17 | if (file === 'example.js') continue | |
| 18 | if (file === 'test.js') continue | |
| 19 | ||
| 20 | fs.writeFileSync(path.join(__dirname, '..', subdir, file), tmp) | |
| 21 | } | |
| 22 | } | |
| 23 | ||
| 24 | recurse(root) | |
| 25 | ||
| 26 | const pkg = require('sodium-javascript/package.json') | |
| 27 | const myPkg = require('../package.json') | |
| 28 | ||
| 29 | for (const key of Object.keys(pkg.dependencies)) { | |
| 30 | myPkg.dependencies[key] = pkg.dependencies[key] | |
| 31 | } | |
| 32 | ||
| 33 | fs.writeFileSync(path.join(__dirname, '../package.json'), JSON.stringify(myPkg, null, 2) + '\n') |
| … | … | @@ -0,0 +1,32 @@ |
| 1 | var exec = require('child_process').execSync | |
| 2 | var path = require('path') | |
| 3 | ||
| 4 | var symbols = exec(`nm -gU "${path.join(require.resolve('sodium-native'), '../prebuilds/darwin-x64/libsodium.dylib')}"`).toString().trim().split('\n') | |
| 5 | var native = new Set(Object.keys(require('sodium-native')).sort()) | |
| 6 | var js = new Set(Object.keys(require('sodium-javascript')).sort()) | |
| 7 | ||
| 8 | symbols = symbols.map(l => l.split(' ').pop().replace(/^_/, '').replace(/_[^_]*bytes.*/, (s) => s.toUpperCase())) | |
| 9 | ||
| 10 | var missing = [] | |
| 11 | ||
| 12 | console.log(` | |
| 13 | <!-- Generated by build-scripts/symbols.js with | |
| 14 | sodium-native@${require('sodium-native/package.json').version} | |
| 15 | sodium-javascript@${require('sodium-javascript/package.json').version} -->\ | |
| 16 | `) | |
| 17 | ||
| 18 | const max = symbols.filter(s => native.has(s) || js.has(s)).map(s => s.length).reduce((a, b) => Math.max(a, b)) | |
| 19 | const lines = ':-' + ''.padEnd(max, '--') + '-:' | |
| 20 | const center = s => s.padStart(Math.floor((max + s.length) / 2), ' ').padEnd(max, ' ') | |
| 21 | ||
| 22 | console.log(`| ${center('C Library Symbol')} | \`sodium-native\` | \`sodium-javascript\` |`) | |
| 23 | console.log(`|${lines}|:--------------------:|:--------------------:|`) | |
| 24 | ||
| 25 | for (var i = 0; i < symbols.length; i++) { | |
| 26 | var s = symbols[i] | |
| 27 | if (!(native.has(s) || js.has(s))) missing.push(s) | |
| 28 | else console.log(`| \`${(s + '`').padEnd(max + 1, ' ')} | ${native.has(s) ? ' :white_check_mark: ' : ':small_red_triangle:'} | ${js.has(s) ? ' :white_check_mark: ' : ':small_red_triangle:'} |`) | |
| 29 | } | |
| 30 | ||
| 31 | console.log('\n### Missing altogether (Ctrl + F friendly)\n') | |
| 32 | console.log(missing.map(s => '`' + s + '`').join(', ')) |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | const fs = require('fs') | |
| 2 | const path = require('path') | |
| 3 | const { Transform, PassThrough } = require('stream') | |
| 4 | ||
| 5 | module.exports = function (file, opts) { | |
| 6 | const basedir = path.resolve(__dirname, '..') | |
| 7 | const relname = path.relative(basedir, file) | |
| 8 | ||
| 9 | if (opts._flags.browserField === false) { | |
| 10 | return new PassThrough() | |
| 11 | } | |
| 12 | ||
| 13 | return new Transform({ | |
| 14 | transform (data, enc, cb) { | |
| 15 | cb() | |
| 16 | }, | |
| 17 | flush (cb) { | |
| 18 | const filename = require.resolve('sodium-javascript/' + relname) | |
| 19 | fs.readFile(filename, (err, buf) => { | |
| 20 | if (err) return cb(err) | |
| 21 | this.push(buf) | |
| 22 | cb(null) | |
| 23 | }) | |
| 24 | } | |
| 25 | }) | |
| 26 | } |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1,49 @@ |
| 1 | { | |
| 2 | "name": "sodium-universal", | |
| 3 | "version": "5.0.1", | |
| 4 | "description": "Universal wrapper for sodium-javascript and sodium-native working in Node.js and the Browser", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "sodium-native": "^5.0.1" | |
| 8 | }, | |
| 9 | "peerDependencies": { | |
| 10 | "sodium-javascript": "~0.8.0" | |
| 11 | }, | |
| 12 | "peerDependenciesMeta": { | |
| 13 | "sodium-javascript": { | |
| 14 | "optional": true | |
| 15 | } | |
| 16 | }, | |
| 17 | "scripts": { | |
| 18 | "prepublish": "./build-scripts/generate.js" | |
| 19 | }, | |
| 20 | "browser": { | |
| 21 | "sodium-native": "sodium-javascript" | |
| 22 | }, | |
| 23 | "browserify": { | |
| 24 | "transform": [ | |
| 25 | "./build-scripts/transform.js" | |
| 26 | ] | |
| 27 | }, | |
| 28 | "repository": { | |
| 29 | "type": "git", | |
| 30 | "url": "git+https://github.com/holepunchto/sodium-universal.git" | |
| 31 | }, | |
| 32 | "keywords": [ | |
| 33 | "libsodium", | |
| 34 | "sodium", | |
| 35 | "sodium-native", | |
| 36 | "sodium-javascript", | |
| 37 | "browserify" | |
| 38 | ], | |
| 39 | "contributors": [ | |
| 40 | "Emil Bay <github@tixz.dk> (http://bayes.dk)", | |
| 41 | "Mathias Buus <mathiasbuus@gmail.com> (https://mafinto.sh)", | |
| 42 | "Christophe Diederichs <chm-diederichs@hyperdivision.dk>" | |
| 43 | ], | |
| 44 | "license": "MIT", | |
| 45 | "bugs": { | |
| 46 | "url": "https://github.com/holepunchto/sodium-universal/issues" | |
| 47 | }, | |
| 48 | "homepage": "https://github.com/holepunchto/sodium-universal#readme" | |
| 49 | } |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1,301 @@ |
| 1 | const assert = require('nanoassert') | |
| 2 | const b4a = require('b4a') | |
| 3 | ||
| 4 | const SymmetricState = require('./symmetric-state') | |
| 5 | const { HASHLEN } = require('./hkdf') | |
| 6 | ||
| 7 | const PRESHARE_IS = Symbol('initiator static key preshared') | |
| 8 | const PRESHARE_RS = Symbol('responder static key preshared') | |
| 9 | ||
| 10 | const TOK_PSK = Symbol('psk') | |
| 11 | ||
| 12 | const TOK_S = Symbol('s') | |
| 13 | const TOK_E = Symbol('e') | |
| 14 | ||
| 15 | const TOK_ES = Symbol('es') | |
| 16 | const TOK_SE = Symbol('se') | |
| 17 | const TOK_EE = Symbol('ee') | |
| 18 | const TOK_SS = Symbol('ss') | |
| 19 | ||
| 20 | const HANDSHAKES = Object.freeze({ | |
| 21 | NN: [ | |
| 22 | [TOK_E], | |
| 23 | [TOK_E, TOK_EE] | |
| 24 | ], | |
| 25 | NNpsk0: [ | |
| 26 | [TOK_PSK, TOK_E], | |
| 27 | [TOK_E, TOK_EE] | |
| 28 | ], | |
| 29 | XX: [ | |
| 30 | [TOK_E], | |
| 31 | [TOK_E, TOK_EE, TOK_S, TOK_ES], | |
| 32 | [TOK_S, TOK_SE] | |
| 33 | ], | |
| 34 | XXpsk0: [ | |
| 35 | [TOK_PSK, TOK_E], | |
| 36 | [TOK_E, TOK_EE, TOK_S, TOK_ES], | |
| 37 | [TOK_S, TOK_SE] | |
| 38 | ], | |
| 39 | IK: [ | |
| 40 | PRESHARE_RS, | |
| 41 | [TOK_E, TOK_ES, TOK_S, TOK_SS], | |
| 42 | [TOK_E, TOK_EE, TOK_SE] | |
| 43 | ], | |
| 44 | XK: [ | |
| 45 | PRESHARE_RS, | |
| 46 | [TOK_E, TOK_ES], | |
| 47 | [TOK_E, TOK_EE], | |
| 48 | [TOK_S, TOK_SE] | |
| 49 | ] | |
| 50 | }) | |
| 51 | ||
| 52 | class Writer { | |
| 53 | constructor () { | |
| 54 | this.size = 0 | |
| 55 | this.buffers = [] | |
| 56 | } | |
| 57 | ||
| 58 | push (b) { | |
| 59 | this.size += b.byteLength | |
| 60 | this.buffers.push(b) | |
| 61 | } | |
| 62 | ||
| 63 | end () { | |
| 64 | const all = b4a.alloc(this.size) | |
| 65 | let offset = 0 | |
| 66 | for (const b of this.buffers) { | |
| 67 | all.set(b, offset) | |
| 68 | offset += b.byteLength | |
| 69 | } | |
| 70 | return all | |
| 71 | } | |
| 72 | } | |
| 73 | ||
| 74 | class Reader { | |
| 75 | constructor (buf) { | |
| 76 | this.offset = 0 | |
| 77 | this.buffer = buf | |
| 78 | } | |
| 79 | ||
| 80 | shift (n) { | |
| 81 | const start = this.offset | |
| 82 | const end = this.offset += n | |
| 83 | if (end > this.buffer.byteLength) throw new Error('Insufficient bytes') | |
| 84 | return this.buffer.subarray(start, end) | |
| 85 | } | |
| 86 | ||
| 87 | end () { | |
| 88 | return this.shift(this.buffer.byteLength - this.offset) | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | module.exports = class NoiseState extends SymmetricState { | |
| 93 | constructor (pattern, initiator, staticKeypair, opts = {}) { | |
| 94 | super(opts) | |
| 95 | ||
| 96 | this.s = staticKeypair || this.curve.generateKeyPair() | |
| 97 | this.e = null | |
| 98 | ||
| 99 | this.psk = null | |
| 100 | if (opts && opts.psk) this.psk = opts.psk | |
| 101 | ||
| 102 | this.re = null | |
| 103 | this.rs = null | |
| 104 | ||
| 105 | this.pattern = pattern | |
| 106 | this.handshake = HANDSHAKES[this.pattern].slice() | |
| 107 | ||
| 108 | this.isPskHandshake = !!this.psk && hasPskToken(this.handshake) | |
| 109 | ||
| 110 | this.protocol = b4a.from([ | |
| 111 | 'Noise', | |
| 112 | this.pattern, | |
| 113 | this.DH_ALG, | |
| 114 | this.CIPHER_ALG, | |
| 115 | 'BLAKE2b' | |
| 116 | ].join('_')) | |
| 117 | ||
| 118 | this.initiator = initiator | |
| 119 | this.complete = false | |
| 120 | ||
| 121 | this.rx = null | |
| 122 | this.tx = null | |
| 123 | this.hash = null | |
| 124 | } | |
| 125 | ||
| 126 | initialise (prologue, remoteStatic) { | |
| 127 | if (this.protocol.byteLength <= HASHLEN) this.digest.set(this.protocol) | |
| 128 | else this.mixHash(this.protocol) | |
| 129 | ||
| 130 | this.chainingKey = b4a.from(this.digest) | |
| 131 | ||
| 132 | this.mixHash(prologue) | |
| 133 | ||
| 134 | while (!Array.isArray(this.handshake[0])) { | |
| 135 | const message = this.handshake.shift() | |
| 136 | ||
| 137 | // handshake steps should be as arrays, only | |
| 138 | // preshare tokens are provided otherwise | |
| 139 | assert(message === PRESHARE_RS || message === PRESHARE_IS, | |
| 140 | 'Unexpected pattern') | |
| 141 | ||
| 142 | const takeRemoteKey = this.initiator | |
| 143 | ? message === PRESHARE_RS | |
| 144 | : message === PRESHARE_IS | |
| 145 | ||
| 146 | if (takeRemoteKey) this.rs = remoteStatic | |
| 147 | ||
| 148 | const key = takeRemoteKey ? this.rs : this.s.publicKey | |
| 149 | assert(key != null, 'Remote pubkey required') | |
| 150 | ||
| 151 | this.mixHash(key) | |
| 152 | } | |
| 153 | } | |
| 154 | ||
| 155 | final () { | |
| 156 | const [k1, k2] = this.split() | |
| 157 | ||
| 158 | this.tx = this.initiator ? k1 : k2 | |
| 159 | this.rx = this.initiator ? k2 : k1 | |
| 160 | ||
| 161 | this.complete = true | |
| 162 | this.hash = this.getHandshakeHash() | |
| 163 | ||
| 164 | this._clear() | |
| 165 | } | |
| 166 | ||
| 167 | recv (buf) { | |
| 168 | const r = new Reader(buf) | |
| 169 | ||
| 170 | for (const pattern of this.handshake.shift()) { | |
| 171 | switch (pattern) { | |
| 172 | case TOK_PSK : | |
| 173 | this.mixKeyAndHash(this.psk) | |
| 174 | break | |
| 175 | ||
| 176 | case TOK_E : | |
| 177 | this.re = r.shift(this.curve.PKLEN) | |
| 178 | this.mixHash(this.re) | |
| 179 | if (this.isPskHandshake) this.mixKeyNormal(this.re) | |
| 180 | break | |
| 181 | ||
| 182 | case TOK_S : { | |
| 183 | const klen = this.hasKey ? this.curve.PKLEN + 16 : this.curve.PKLEN | |
| 184 | this.rs = this.decryptAndHash(r.shift(klen)) | |
| 185 | break | |
| 186 | } | |
| 187 | ||
| 188 | case TOK_EE : | |
| 189 | case TOK_ES : | |
| 190 | case TOK_SE : | |
| 191 | case TOK_SS : { | |
| 192 | const useStatic = keyPattern(pattern, this.initiator) | |
| 193 | ||
| 194 | const localKey = useStatic.local ? this.s : this.e | |
| 195 | const remoteKey = useStatic.remote ? this.rs : this.re | |
| 196 | ||
| 197 | this.mixKey(remoteKey, localKey) | |
| 198 | break | |
| 199 | } | |
| 200 | ||
| 201 | default : | |
| 202 | throw new Error('Unexpected message') | |
| 203 | } | |
| 204 | } | |
| 205 | ||
| 206 | const payload = this.decryptAndHash(r.end()) | |
| 207 | ||
| 208 | if (!this.handshake.length) this.final() | |
| 209 | return payload | |
| 210 | } | |
| 211 | ||
| 212 | send (payload = b4a.alloc(0)) { | |
| 213 | const w = new Writer() | |
| 214 | ||
| 215 | for (const pattern of this.handshake.shift()) { | |
| 216 | switch (pattern) { | |
| 217 | case TOK_PSK : | |
| 218 | this.mixKeyAndHash(this.psk) | |
| 219 | break | |
| 220 | ||
| 221 | case TOK_E : | |
| 222 | if (this.e === null) this.e = this.curve.generateKeyPair() | |
| 223 | this.mixHash(this.e.publicKey) | |
| 224 | if (this.isPskHandshake) this.mixKeyNormal(this.e.publicKey) | |
| 225 | w.push(this.e.publicKey) | |
| 226 | break | |
| 227 | ||
| 228 | case TOK_S : | |
| 229 | w.push(this.encryptAndHash(this.s.publicKey)) | |
| 230 | break | |
| 231 | ||
| 232 | case TOK_ES : | |
| 233 | case TOK_SE : | |
| 234 | case TOK_EE : | |
| 235 | case TOK_SS : { | |
| 236 | const useStatic = keyPattern(pattern, this.initiator) | |
| 237 | ||
| 238 | const localKey = useStatic.local ? this.s : this.e | |
| 239 | const remoteKey = useStatic.remote ? this.rs : this.re | |
| 240 | ||
| 241 | this.mixKey(remoteKey, localKey) | |
| 242 | break | |
| 243 | } | |
| 244 | ||
| 245 | default : | |
| 246 | throw new Error('Unexpected message') | |
| 247 | } | |
| 248 | } | |
| 249 | ||
| 250 | w.push(this.encryptAndHash(payload)) | |
| 251 | const response = w.end() | |
| 252 | ||
| 253 | if (!this.handshake.length) this.final() | |
| 254 | return response | |
| 255 | } | |
| 256 | ||
| 257 | _clear () { | |
| 258 | super._clear() | |
| 259 | ||
| 260 | this.e.secretKey.fill(0) | |
| 261 | this.e.publicKey.fill(0) | |
| 262 | ||
| 263 | this.re.fill(0) | |
| 264 | ||
| 265 | this.e = null | |
| 266 | this.re = null | |
| 267 | } | |
| 268 | } | |
| 269 | ||
| 270 | function keyPattern (pattern, initiator) { | |
| 271 | const ret = { | |
| 272 | local: false, | |
| 273 | remote: false | |
| 274 | } | |
| 275 | ||
| 276 | switch (pattern) { | |
| 277 | case TOK_EE: | |
| 278 | return ret | |
| 279 | ||
| 280 | case TOK_ES: | |
| 281 | ret.local ^= !initiator | |
| 282 | ret.remote ^= initiator | |
| 283 | return ret | |
| 284 | ||
| 285 | case TOK_SE: | |
| 286 | ret.local ^= initiator | |
| 287 | ret.remote ^= !initiator | |
| 288 | return ret | |
| 289 | ||
| 290 | case TOK_SS: | |
| 291 | ret.local ^= 1 | |
| 292 | ret.remote ^= 1 | |
| 293 | return ret | |
| 294 | } | |
| 295 | } | |
| 296 | ||
| 297 | function hasPskToken (handshake) { | |
| 298 | return handshake.some(x => { | |
| 299 | return Array.isArray(x) && x.indexOf(TOK_PSK) !== -1 | |
| 300 | }) | |
| 301 | } |
| … | … | @@ -0,0 +1,28 @@ |
| 1 | { | |
| 2 | "name": "noise-handshake", | |
| 3 | "version": "4.2.0", | |
| 4 | "description": "Noise protocol handshake", | |
| 5 | "main": "noise.js", | |
| 6 | "files": [ | |
| 7 | "*.js" | |
| 8 | ], | |
| 9 | "scripts": { | |
| 10 | "test": "standard && brittle test/*.js" | |
| 11 | }, | |
| 12 | "author": "Holepunch", | |
| 13 | "license": "Apache-2.0", | |
| 14 | "repository": { | |
| 15 | "type": "git", | |
| 16 | "url": "git+https://github.com/holepunchto/noise-handshake.git" | |
| 17 | }, | |
| 18 | "dependencies": { | |
| 19 | "b4a": "^1.1.0", | |
| 20 | "nanoassert": "^2.0.0", | |
| 21 | "sodium-universal": "^5.0.0" | |
| 22 | }, | |
| 23 | "devDependencies": { | |
| 24 | "brittle": "^3.3.2", | |
| 25 | "noise-protocol": "chm-diederichs/noise-protocol.git#xx-ephemeral-key", | |
| 26 | "standard": "^16.0.3" | |
| 27 | } | |
| 28 | } |
| … | … | @@ -0,0 +1,90 @@ |
| 1 | const sodium = require('sodium-universal') | |
| 2 | const assert = require('nanoassert') | |
| 3 | const b4a = require('b4a') | |
| 4 | const CipherState = require('./cipher') | |
| 5 | const curve = require('./dh') | |
| 6 | const { HASHLEN, hkdf } = require('./hkdf') | |
| 7 | ||
| 8 | module.exports = class SymmetricState extends CipherState { | |
| 9 | constructor (opts = {}) { | |
| 10 | super() | |
| 11 | ||
| 12 | this.curve = opts.curve || curve | |
| 13 | this.digest = b4a.alloc(HASHLEN) | |
| 14 | this.chainingKey = null | |
| 15 | this.offset = 0 | |
| 16 | ||
| 17 | this.DH_ALG = this.curve.ALG | |
| 18 | } | |
| 19 | ||
| 20 | mixHash (data) { | |
| 21 | accumulateDigest(this.digest, data) | |
| 22 | } | |
| 23 | ||
| 24 | mixKeyAndHash (key) { | |
| 25 | const [ck, tempH, tempK] = hkdf(this.chainingKey, key, '', 3 * HASHLEN) | |
| 26 | this.chainingKey = ck | |
| 27 | this.mixHash(tempH) | |
| 28 | this.initialiseKey(tempK.subarray(0, 32)) | |
| 29 | } | |
| 30 | ||
| 31 | mixKeyNormal (key) { | |
| 32 | const [ck, tempK] = hkdf(this.chainingKey, key) | |
| 33 | this.chainingKey = ck | |
| 34 | this.initialiseKey(tempK.subarray(0, 32)) | |
| 35 | } | |
| 36 | ||
| 37 | mixKey (remoteKey, localKey) { | |
| 38 | const dh = this.curve.dh(remoteKey, localKey) | |
| 39 | const hkdfResult = hkdf(this.chainingKey, dh) | |
| 40 | this.chainingKey = hkdfResult[0] | |
| 41 | this.initialiseKey(hkdfResult[1].subarray(0, 32)) | |
| 42 | } | |
| 43 | ||
| 44 | encryptAndHash (plaintext) { | |
| 45 | const ciphertext = this.encrypt(plaintext, this.digest) | |
| 46 | accumulateDigest(this.digest, ciphertext) | |
| 47 | return ciphertext | |
| 48 | } | |
| 49 | ||
| 50 | decryptAndHash (ciphertext) { | |
| 51 | const plaintext = this.decrypt(ciphertext, this.digest) | |
| 52 | accumulateDigest(this.digest, ciphertext) | |
| 53 | return plaintext | |
| 54 | } | |
| 55 | ||
| 56 | getHandshakeHash (out) { | |
| 57 | if (!out) return this.getHandshakeHash(b4a.alloc(HASHLEN)) | |
| 58 | assert(out.byteLength === HASHLEN, `output must be ${HASHLEN} bytes`) | |
| 59 | ||
| 60 | out.set(this.digest) | |
| 61 | return out | |
| 62 | } | |
| 63 | ||
| 64 | split () { | |
| 65 | const res = hkdf(this.chainingKey, b4a.alloc(0)) | |
| 66 | return res.map(k => k.subarray(0, 32)) | |
| 67 | } | |
| 68 | ||
| 69 | _clear () { | |
| 70 | super._clear() | |
| 71 | ||
| 72 | sodium.sodium_memzero(this.digest) | |
| 73 | sodium.sodium_memzero(this.chainingKey) | |
| 74 | ||
| 75 | this.digest = null | |
| 76 | this.chainingKey = null | |
| 77 | this.offset = null | |
| 78 | ||
| 79 | this.curve = null | |
| 80 | } | |
| 81 | ||
| 82 | static get alg () { | |
| 83 | return CipherState.alg + '_BLAKE2b' | |
| 84 | } | |
| 85 | } | |
| 86 | ||
| 87 | function accumulateDigest (digest, input) { | |
| 88 | const toHash = b4a.concat([digest, input]) | |
| 89 | sodium.crypto_generichash(digest, toHash) | |
| 90 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,398 @@ |
| 1 | # paparam | |
| 2 | ||
| 3 | Strict and fair parameter papa | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install paparam | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ### Simple Command | |
| 12 | ||
| 13 | Use `paparam` to build a simple cli parser. Here is an example app invocation from the command line: | |
| 14 | ||
| 15 | ```bash | |
| 16 | > app -i uuid-284h43j -b peer1 -b peer2 --storage /tmp/file/place link-32832uifsdjsfda | |
| 17 | ``` | |
| 18 | ||
| 19 | To handle this invocation, here is the `paparam` code | |
| 20 | ||
| 21 | ```js | |
| 22 | import { header, summary, command, flag, arg } from 'paparam' | |
| 23 | const cmd = command( | |
| 24 | 'run', | |
| 25 | header('A simple example app'), | |
| 26 | summary('Example app that uses a roomLink to join a room and do work.'), | |
| 27 | arg('<roomLink>', 'the room link key'), | |
| 28 | flag('--uuid|-i [uuid]', 'The specific schema uuid to use'), | |
| 29 | flag('--user|-u [user]', 'The user name associated with the entry'), | |
| 30 | flag('--storage|-s [storage]', 'Path to storage directory'), | |
| 31 | flag('--blind|-b [blind]', 'Blind peer keys (can be specified multiple times)').multiple() | |
| 32 | ) | |
| 33 | const program = cmd.parse() // default args: process.argv.slice(2) | |
| 34 | if (program === null) throw new Error('missing arg') | |
| 35 | console.log('user', program.flags.user) | |
| 36 | console.log('storagePath', program.flags.storage || '/tmp') | |
| 37 | console.log('blindPeers', program.flags.blind || []) | |
| 38 | console.log('roomLink', program.args.roomLink) | |
| 39 | cmd.add(flag('--another', 'lazy added')) | |
| 40 | cmd.add({ | |
| 41 | '--more': 'via description object', | |
| 42 | '--modifiers': ['are defined with array [desc, opts]', { multiple: true }] | |
| 43 | }) | |
| 44 | cmd.parse(['-h']) | |
| 45 | ``` | |
| 46 | ||
| 47 | ### Composed Commands | |
| 48 | ||
| 49 | Use `paparam` exports to compose commands together: | |
| 50 | ||
| 51 | ```js | |
| 52 | const { header, footer, command, flag, arg, summary, description, rest } = require('paparam') | |
| 53 | const run = command( | |
| 54 | 'run', | |
| 55 | summary('Run an app from a link'), | |
| 56 | description( | |
| 57 | 'Run an app from a file link (or path) or from a pear link.\nOptionally supply store for custom store path' | |
| 58 | ), | |
| 59 | flag('--store|-s [path]', 'store path'), | |
| 60 | arg('<link|channel>', 'link to run'), | |
| 61 | rest('[...app-args]'), | |
| 62 | () => console.log('ACTION ->', 'run', run.args.link, 'flags:', run.flags) | |
| 63 | ) | |
| 64 | const cmd = command( | |
| 65 | 'pear', | |
| 66 | summary('pear cli'), | |
| 67 | { | |
| 68 | // definition objects can also be used with form {[modifier <arg1>]: '<arg2>' | ['<arg2>', adjusters {...}]}: | |
| 69 | 'flag --log': 'log', | |
| 70 | header: 'Welcome to the IoP' | |
| 71 | }, | |
| 72 | header('Welcome to the IoP'), | |
| 73 | footer('holepunch.to | pears.com | keet.io'), | |
| 74 | run | |
| 75 | ) | |
| 76 | cmd.parse(['--help']) // print pear help | |
| 77 | cmd.parse(['run', '-h']) // print run help | |
| 78 | cmd.parse(['run', '-s', '/path/to/store', 'pear://link']) // exec run command | |
| 79 | ||
| 80 | run.add(flag('--pre-io', 'Show stdout & stderr of pre scripts')) // update command | |
| 81 | ||
| 82 | cmd.parse(['run', '-s', '/path/to/store', '--pre-io', 'pear://link']) // exec run command | |
| 83 | ``` | |
| 84 | ||
| 85 | This should output: | |
| 86 | ||
| 87 | ``` | |
| 88 | Welcome to the IoP | |
| 89 | ||
| 90 | pear [flags] [command] | |
| 91 | ||
| 92 | pear cli | |
| 93 | ||
| 94 | Flags: | |
| 95 | --log log | |
| 96 | --help|-h Show help | |
| 97 | ||
| 98 | Commands: | |
| 99 | run Run an app from a link | |
| 100 | ||
| 101 | holepunch.to | pears.com | keet.io | |
| 102 | ||
| 103 | Welcome to the IoP | |
| 104 | ||
| 105 | pear run [flags] <link|channel> [...app-args] | |
| 106 | ||
| 107 | Run an app from a link | |
| 108 | ||
| 109 | Run an app from a file link (or path) or from a pear link. | |
| 110 | Optionally supply store for custom store path | |
| 111 | ||
| 112 | Arguments: | |
| 113 | <link|channel> link to run | |
| 114 | [...app-args] | |
| 115 | ||
| 116 | Flags: | |
| 117 | --store|-s [path] store path | |
| 118 | --help|-h Show help | |
| 119 | ||
| 120 | holepunch.to | pears.com | keet.io | |
| 121 | ||
| 122 | ACTION -> run pear://link flags: { store: '/path/to/store', s: '/path/to/store', help: false, h: false } | |
| 123 | ACTION -> run pear://link flags: { | |
| 124 | store: '/path/to/store', | |
| 125 | s: '/path/to/store', | |
| 126 | help: false, | |
| 127 | h: false, | |
| 128 | preIo: true | |
| 129 | } | |
| 130 | ``` | |
| 131 | ||
| 132 | ## API | |
| 133 | ||
| 134 | ### `command(name, ...args)` | |
| 135 | ||
| 136 | Defines a command with a specific behavior based on the supplied modifiers such as flags, arguments, and descriptions. This function is used to create the command object. | |
| 137 | ||
| 138 | - **Arguments**: | |
| 139 | - `name` `<String>`: The name of the command. | |
| 140 | - `...args` `<Modifier> | <Command> | <Object> | <Function>`: Supply modifiers and subcommands as arguments. Supply a [definition object](#definition-object) to declaratively define modifiers. Supply a single function argument as the command runner. | |
| 141 | ||
| 142 | - **Returns**: | |
| 143 | - `cmd` `<Command>`: The command object capable of parsing CLI arguments and executing associated actions. | |
| 144 | ||
| 145 | #### `cmd.add(...args)` | |
| 146 | ||
| 147 | Update command with additional modifiers & subcommands. A runner function may also be added, but only one runner is supported so will in that case replace the prior command runner function. | |
| 148 | ||
| 149 | - **Arguments**: | |
| 150 | - `...args` `<Modifier> | <Command> | <Function>`: Supply modifiers and subcommands as arguments. Supply a single function argument as the command runner. | |
| 151 | ||
| 152 | - **Returns**: | |
| 153 | - `cmd` `<Command>`: The command object capable of parsing CLI arguments and executing associated actions. | |
| 154 | ||
| 155 | #### `cmd.parse(argv = process.argv.slice(2), opts)` | |
| 156 | ||
| 157 | Parses an array of command line arguments and executes the command based on the provided definition. Automatically handles '--help' or '-h' flags to display help information. | |
| 158 | ||
| 159 | - **Arguments**: | |
| 160 | - `argv` `<String[]>`: An array of strings representing the command line arguments. Defaults to program arguments `process.argv.slice(2)` if unspecified. If the `-h` or `--help` flag is supplied help for the related command will be shown. | |
| 161 | - `opts` `<Object>`: Parse options | |
| 162 | - `sync` `<Boolean>`: Synchronous parse, only use this with synchronous function runners. Default `false`. | |
| 163 | - `silent` `<Boolean>`: Suppress the help display, even if `-h` or `--help` is provided. | |
| 164 | - `run` `<Boolean>`: If false, prevents the command runner from being executed. | |
| 165 | - **Returns**: | |
| 166 | - `null` if the parsing leads to an error (with an error output) or the command object if the command executes without errors. | |
| 167 | ||
| 168 | #### `cmd.help(...subcommands)` | |
| 169 | ||
| 170 | Generates and returns help text for the command. Can take subcommand names as arguments to generate help for a specific subcommand path. When calling `cmd.parse(argv)`, if the `-h` or `--help` flag is supplied, this method is automatically called and output. | |
| 171 | ||
| 172 | #### `cmd.usage(...subcommands)` | |
| 173 | ||
| 174 | Returns help text without header or footer. Can take subcommand names as arguments to generate help for a specific subcommand path. | |
| 175 | ||
| 176 | #### `cmd.overview({ full = false })` | |
| 177 | ||
| 178 | Returns a string with a command overview. Set `full` to `true` to include detailed usage for each command. | |
| 179 | ||
| 180 | - **Arguments**: | |
| 181 | - `full` `<Boolean>`: Whether to display a full overview that includes details of all defined sub-commands and flags. | |
| 182 | ||
| 183 | #### `cmd.running` `<Promise> | null` | |
| 184 | ||
| 185 | Either `null` if the command is not running or a promise that resolves when the command has completed running. | |
| 186 | ||
| 187 | #### `cmd.bailed` `<Object> | null` | |
| 188 | ||
| 189 | Either `null` if the command did not bail or an object descibing the bail. The object can be `{ bail <Object>, error <Error>?, output <String>? }`. If the object has an `error` property it failed to create the bail output (which could happen with a user defined bail function), | |
| 190 | otherwise the object has an `output` property which explains the reason for the bail. The `bail` property of the `bailed` object has the following shape: `{ reason <String>, flag <Flag>, arg <Arg>, err <Error>}`. When present, `err` property on the `bail` objects would have been thrown from the runner, whereas the `bailed.error` property holds errors that occur during bailing. | |
| 191 | ||
| 192 | #### `cmd.flags` `<Object>` | |
| 193 | ||
| 194 | After `cmd.parse` has been called, contains parsed flag values. | |
| 195 | ||
| 196 | #### `cmd.args` `<Object>` | |
| 197 | ||
| 198 | After `cmd.parse` has been called, contains parsed arguments. | |
| 199 | ||
| 200 | #### `cmd.positionals` `<Array>` | |
| 201 | ||
| 202 | After `cmd.parse` has been called, contains original arguments in isolation. | |
| 203 | ||
| 204 | #### `cmd.rest` `<Object>` | |
| 205 | ||
| 206 | After `cmd.parse` has been called, contains rest arguments, if any, on `cmd.rest.restName` where `restName` is defined | |
| 207 | by `rest(spec)` where `spec` is `[..rest-name]`. | |
| 208 | ||
| 209 | #### `cmd.parent` `<Command>` | |
| 210 | ||
| 211 | Command parent | |
| 212 | ||
| 213 | #### `cmd.name` `<String>` | |
| 214 | ||
| 215 | Command name | |
| 216 | ||
| 217 | #### `cmd.description` `<String>` | |
| 218 | ||
| 219 | Command description | |
| 220 | ||
| 221 | #### `cmd.summary` `<String>` | |
| 222 | ||
| 223 | Command summary | |
| 224 | ||
| 225 | #### `cmd.header` `<String>` | |
| 226 | ||
| 227 | Command header | |
| 228 | ||
| 229 | #### `cmd.footer` `<String> | <Object>` | |
| 230 | ||
| 231 | Command footer | |
| 232 | ||
| 233 | #### `cmd.indices.flags` `<Object>` | |
| 234 | ||
| 235 | After `cmd.parse` has been called, contains the index of each flag in the original array. | |
| 236 | ||
| 237 | #### `cmd.indices.args` `<Object>` | |
| 238 | ||
| 239 | After `cmd.parse` has been called, contains the index of each parsed argument in the original array. | |
| 240 | ||
| 241 | #### `cmd.indices.positionals` `<Array>` | |
| 242 | ||
| 243 | After `cmd.parse` has been called, contains the indexes of each positional argument in the original array. | |
| 244 | ||
| 245 | #### `cmd.indices.rest` `<Number>` | |
| 246 | ||
| 247 | After `cmd.parse` has been called, contains the index of the first rest argument in the positional array. | |
| 248 | ||
| 249 | ### `flag(spec, description)` | |
| 250 | ||
| 251 | Defines a flag for a command. Flags can be simple boolean switches or can expect a value. | |
| 252 | ||
| 253 | - **Arguments**: | |
| 254 | - `spec` `<String>`: `--long|-l (<value> | [value])?` , e.g., `--boolean-flag|-b`, `--string-flag [optional-value]`, `--string-flag <required-value>` | |
| 255 | - `description` `<String>`: A description of what the flag does. | |
| 256 | ||
| 257 | - **Returns**: | |
| 258 | - `flag` `<Flag>`: A modifier that configures the command to recognize and handle the specified flag. | |
| 259 | - `flag.hide()` to hide the flag from help. | |
| 260 | - `flag.multiple()` to make this flag into an array of all passed values instead of the latest one only. | |
| 261 | ||
| 262 | ### `arg(spec, description)` | |
| 263 | ||
| 264 | Defines a positional argument for a command. | |
| 265 | ||
| 266 | - **Arguments**: | |
| 267 | - `spec` `<String>`: `[arg] | <arg>` where arg is the placeholder name for the argument, e.g. `<name>`, (required) `[dir]` (optional) | |
| 268 | - `description` `<String>`: A description of what the argument is for. | |
| 269 | ||
| 270 | - **Returns**: | |
| 271 | - `<Arg>`: A modifier that configures the command to accept and process the specified argument. | |
| 272 | ||
| 273 | ### `rest(spec, description)` | |
| 274 | ||
| 275 | Defines rest arguments that are captured after all flags and named arguments. | |
| 276 | ||
| 277 | - **Arguments**: | |
| 278 | - `spec` `<String>`: `[...rest-name]`. The name to collect rest arguments under, e.g. `[...app-args]` array will be stored at `cmd.rest.appArgs`. | |
| 279 | - `description` `<String>`: Description of what these rest arguments represent. | |
| 280 | ||
| 281 | - **Returns**: | |
| 282 | - `<Rest>`: A modifier that configures the command to capture additional arguments not explicitly defined. | |
| 283 | ||
| 284 | ### `validate(validator, description)` | |
| 285 | ||
| 286 | Defines a validation function for the command. This function is used to enforce custom validation logic on the command after parsing, such as checking the relationship of flags, arguments, rest arguments, etc. | |
| 287 | ||
| 288 | - **Arguments**: | |
| 289 | - `validator` `<Function>`: A function that takes the parsed command object and returns a boolean indicating whether the validation passed. If the validation fails, the function should throw an error or return a string describing the validation error. | |
| 290 | - **Parameters**: | |
| 291 | - `args` `<Object>`: An object containing the parsed arguments. | |
| 292 | - `flags` `<Object>`: An object containing the parsed flags. | |
| 293 | - `positionals` `<Array>`: An array containing the positional arguments. | |
| 294 | - `rest` `<Object>`: An object containing the rest arguments. | |
| 295 | - `indices` `<Object>`: An object containing the indices of flags, arguments, positionals, and rest. | |
| 296 | - `flags` `<Object>`: An object containing the indices of the flags. | |
| 297 | - `args` `<Object>`: An object containing the indices of the arguments. | |
| 298 | - `positionals` `<Array>`: An array containing the indices of the positional arguments. | |
| 299 | - `rest` `<Number>`: The index of the rest arguments. | |
| 300 | - `command` `<Command>`: The command object being validated. | |
| 301 | - **Returns**: | |
| 302 | - `<Boolean>`: The validator function should return true if the validation passes, indicating that the command is valid. If the validation fails, it should return false or throw an error. Returning false indicates that the command is invalid. | |
| 303 | ||
| 304 | - **Returns**: | |
| 305 | - `<Validation>`: A modifier that configures the command to use the specified validation function. | |
| 306 | ||
| 307 | ### `summary(text)` | |
| 308 | ||
| 309 | Defines a brief summary of the command's functionality. | |
| 310 | ||
| 311 | - **Arguments**: | |
| 312 | - `text` `<String>`: Summary of the command's purpose. | |
| 313 | ||
| 314 | - **Returns**: | |
| 315 | - `<Data>`: A modifier containing the summary information. | |
| 316 | ||
| 317 | ### `` description(text) | description`text` `` | |
| 318 | ||
| 319 | Defines a detailed description of the command's functionality. | |
| 320 | When used as a tagged template function, `description` automatically dedents the text. | |
| 321 | ||
| 322 | - **Arguments**: | |
| 323 | - `text` `<String>`: Detailed information about the command. | |
| 324 | ||
| 325 | - **Returns**: | |
| 326 | - `<Data>`: A modifier containing the description information. | |
| 327 | ||
| 328 | ### `header(text)` | |
| 329 | ||
| 330 | Define a header for the command's help output. | |
| 331 | ||
| 332 | - **Arguments**: | |
| 333 | - `text` `<String>`: Text to display at the top of the output. | |
| 334 | ||
| 335 | - **Returns**: | |
| 336 | - `<Data>`: A modifier that configures the header text. | |
| 337 | ||
| 338 | ### `footer(text | { overview: text, help: text })` | |
| 339 | ||
| 340 | Define a footer for the command's help & overview output. | |
| 341 | ||
| 342 | - **Arguments**: | |
| 343 | - `text` `<String> | { overview: text <String>, help: text <String> }`| : Text to display at the bottom of the help and/or overview output. | |
| 344 | ||
| 345 | - **Returns**: | |
| 346 | - `<Data>`: A modifier that configures the footer text. | |
| 347 | ||
| 348 | ### `bail(fn)` | |
| 349 | ||
| 350 | Set the bail handler to `fn`. | |
| 351 | ||
| 352 | ### `sloppy(opts)` | |
| 353 | ||
| 354 | Configures the command to be non-strict when parsing unknown flags or arguments. | |
| 355 | ||
| 356 | ### Definition Object | |
| 357 | ||
| 358 | The `command` function can also accept plain objects with the following form: | |
| 359 | ||
| 360 | ``` | |
| 361 | {[modifier <arg1>]: '<arg2>' | ['<arg2>', adjusters {...}]} | |
| 362 | ``` | |
| 363 | ||
| 364 | `modifier`: names of exported functions designed to passed to `command` function such as `flag(...)`, `arg(...)`, `summary(...)` and so on. | |
| 365 | `<arg1>`: first arg passed, e.g. `flag('--some value') -> 'flag --some value'`, `rest('[...args]') -> 'rest [...args]'` | |
| 366 | `<arg2>`: second arg passed to modifier `flag('--some value', 'some flag') -> {'flag --some value': 'some flag'}` | |
| 367 | `['<arg2>', adjusters {...}]`: adjusters are methods on modifiers, such as `flag.multiple()`, `flag.choices(...)`, `flag.hide()` and `arg.hide()`. For adjusters with args the value should be the arg eg `{choices: ['a', 'b', 'c']}`, otherwise set to `true` to enable `{ multiple: true }` | |
| 368 | ||
| 369 | Example: | |
| 370 | ||
| 371 | ```js | |
| 372 | import { command } from 'paparam' | |
| 373 | const cmd = command({ | |
| 374 | name: 'pear', | |
| 375 | summary: 'summary text', | |
| 376 | description: 'description text', | |
| 377 | header: 'header text', | |
| 378 | footer: 'footer text', | |
| 379 | 'flag --define flag': 'describe flag', | |
| 380 | 'flag --define multiflag': ['describe flag', { multiple: true }], | |
| 381 | 'flag --define hidden-flag': ['describe flag', { hide: true }], | |
| 382 | 'flag --define choices': ['describe flag', { choices: ['a', 'b', 'c'] }], | |
| 383 | 'arg <required>': 'required arg', | |
| 384 | 'hidden [arg]': ['hidden optional arg', { hide: true }], | |
| 385 | rest: '[...args]', | |
| 386 | command: { | |
| 387 | name: 'run', | |
| 388 | summary: 'a subcommand' | |
| 389 | } | |
| 390 | }) | |
| 391 | cmd.parse(['-h']) | |
| 392 | ``` | |
| 393 | ||
| 394 | This is a useful format for supporting JSON-based definitions of commands. | |
| 395 | ||
| 396 | ## License | |
| 397 | ||
| 398 | Apache-2.0 |
| … | … | @@ -0,0 +1,918 @@ |
| 1 | 'use strict' | |
| 2 | const EOL = '\n' | |
| 3 | const noop = () => {} | |
| 4 | module.exports = { | |
| 5 | command, | |
| 6 | hiddenCommand, | |
| 7 | header, | |
| 8 | footer, | |
| 9 | argv, | |
| 10 | flag, | |
| 11 | hiddenFlag, | |
| 12 | arg, | |
| 13 | rest, | |
| 14 | validate, | |
| 15 | summary, | |
| 16 | description, | |
| 17 | sloppy, | |
| 18 | bail | |
| 19 | } | |
| 20 | ||
| 21 | class Parser { | |
| 22 | constructor(argv) { | |
| 23 | this.argv = argv | |
| 24 | this.i = 0 | |
| 25 | this.multi = null | |
| 26 | } | |
| 27 | ||
| 28 | rest() { | |
| 29 | const rest = this.argv.slice(this.i) | |
| 30 | this.lasti = this.i | |
| 31 | this.i = this.argv.length | |
| 32 | return rest | |
| 33 | } | |
| 34 | ||
| 35 | next() { | |
| 36 | while (true) { | |
| 37 | const r = { | |
| 38 | flag: null, | |
| 39 | rest: null, | |
| 40 | arg: null | |
| 41 | } | |
| 42 | ||
| 43 | if (this.multi && this.multi.length) { | |
| 44 | const name = this.multi.pop() | |
| 45 | r.flag = { long: false, name, value: '' } | |
| 46 | return r | |
| 47 | } | |
| 48 | ||
| 49 | this.multi = null | |
| 50 | ||
| 51 | if (this.i >= this.argv.length) return null | |
| 52 | this.lasti = this.i | |
| 53 | const a = this.argv[this.i++] | |
| 54 | ||
| 55 | if (a === '--') { | |
| 56 | r.rest = this.rest() | |
| 57 | return r | |
| 58 | } | |
| 59 | ||
| 60 | if (a.length > 2 && a[0] === '-' && a[1] === '-') { | |
| 61 | let j = a.indexOf('=') | |
| 62 | if (j === -1) j = a.length | |
| 63 | const inverse = a.startsWith('--no-') | |
| 64 | const name = inverse ? a.slice(5, j) : a.slice(2, j) | |
| 65 | const value = a.slice(j + 1) | |
| 66 | r.flag = { long: true, name, value, inverse } | |
| 67 | return r | |
| 68 | } | |
| 69 | ||
| 70 | if (a.length > 1 && a[0] === '-') { | |
| 71 | this.multi = [] | |
| 72 | for (let i = 1; i < a.length; i++) this.multi.push(a[i]) | |
| 73 | this.multi.reverse() | |
| 74 | continue | |
| 75 | } | |
| 76 | ||
| 77 | r.arg = a | |
| 78 | return r | |
| 79 | } | |
| 80 | } | |
| 81 | } | |
| 82 | ||
| 83 | class Command { | |
| 84 | constructor(parent, name) { | |
| 85 | this.parent = parent | |
| 86 | this.name = name | |
| 87 | this.hidden = false | |
| 88 | this.description = '' | |
| 89 | this.summary = '' | |
| 90 | this.header = '' | |
| 91 | this.footer = '' | |
| 92 | ||
| 93 | this.flags = {} | |
| 94 | this.args = {} | |
| 95 | this.positionals = [] | |
| 96 | this.rest = null | |
| 97 | this.bailed = null | |
| 98 | this.indices = { flags: {}, args: {}, positionals: [], rest: undefined } | |
| 99 | ||
| 100 | this.running = null | |
| 101 | this.argv = null | |
| 102 | ||
| 103 | this._validators = [] | |
| 104 | this._runner = null | |
| 105 | this._onbail = null | |
| 106 | this._indent = '' | |
| 107 | this._strictFlags = true | |
| 108 | this._strictArgs = true | |
| 109 | this._labels = { | |
| 110 | arguments: 'Arguments:', | |
| 111 | flags: 'Flags:', | |
| 112 | commands: 'Commands:' | |
| 113 | } | |
| 114 | ||
| 115 | this._definedCommands = new Map() | |
| 116 | this._definedFlags = new Map() | |
| 117 | this._definedArgs = [] | |
| 118 | this._definedRest = null | |
| 119 | } | |
| 120 | ||
| 121 | hide() { | |
| 122 | this.hidden = true | |
| 123 | return this | |
| 124 | } | |
| 125 | ||
| 126 | bail(bail) { | |
| 127 | try { | |
| 128 | this.bailed = { output: this._bail(bail), bail } | |
| 129 | } catch (err) { | |
| 130 | this.bailed = { error: err, bail } | |
| 131 | throw err | |
| 132 | } | |
| 133 | return this.bailed | |
| 134 | } | |
| 135 | ||
| 136 | overview({ full = false } = {}) { | |
| 137 | let s = '' | |
| 138 | if (this.header) s += this.header + EOL + EOL | |
| 139 | ||
| 140 | for (const [name, command] of this._definedCommands) { | |
| 141 | if (command.hidden) continue | |
| 142 | if (full) { | |
| 143 | command._indent = this.header ? ' ' : '' | |
| 144 | s += command._indent + command._oneliner(false) + EOL + command._aligned() + EOL | |
| 145 | command._indent = '' | |
| 146 | } else s += ' ' + this.name + ' ' + name + ' ~ ' + command.summary + EOL | |
| 147 | } | |
| 148 | ||
| 149 | if (this.footer) { | |
| 150 | s += EOL + (Object.hasOwn(this.footer, 'overview') ? this.footer.overview : this.footer) + EOL | |
| 151 | } | |
| 152 | ||
| 153 | return s | |
| 154 | } | |
| 155 | ||
| 156 | help(...args) { | |
| 157 | let s = '' | |
| 158 | ||
| 159 | if (this.header) s += this.header + EOL + EOL | |
| 160 | this._indent = this.header ? ' ' : '' | |
| 161 | const output = this.usage(...args) | |
| 162 | s += typeof output === 'string' ? output : '' | |
| 163 | this._indent = '' | |
| 164 | ||
| 165 | if (this.footer) { | |
| 166 | s += EOL + (Object.hasOwn(this.footer, 'help') ? this.footer.help : this.footer) + EOL | |
| 167 | } | |
| 168 | ||
| 169 | return s | |
| 170 | } | |
| 171 | ||
| 172 | usage(subcommand, ...args) { | |
| 173 | if (subcommand) { | |
| 174 | const sub = this._definedCommands.get(subcommand) | |
| 175 | if (!sub) return this.bail(createBail(this, 'UNKNOWN_ARG', null, { value: subcommand })) | |
| 176 | sub.parent = this | |
| 177 | const str = sub.usage(...args) | |
| 178 | return str | |
| 179 | } | |
| 180 | let s = this._indent + this._oneliner(false) + EOL | |
| 181 | s += this._aligned() | |
| 182 | ||
| 183 | return s | |
| 184 | } | |
| 185 | ||
| 186 | parse(input = argv(), opts = { run: true }) { | |
| 187 | const { sync = false, bails = true } = opts | |
| 188 | const p = new Parser(input) | |
| 189 | ||
| 190 | let c = this._reset() | |
| 191 | let bail = null | |
| 192 | ||
| 193 | const visited = [c] | |
| 194 | ||
| 195 | while (bail === null) { | |
| 196 | if ( | |
| 197 | c._definedRest !== null && | |
| 198 | c.positionals.length === c._definedArgs.length && | |
| 199 | c._definedArgs.length > 0 | |
| 200 | ) { | |
| 201 | bail = c._onrest(p.rest(), p) | |
| 202 | break | |
| 203 | } | |
| 204 | ||
| 205 | const n = p.next() | |
| 206 | if (n === null) break | |
| 207 | ||
| 208 | if (n.flag) { | |
| 209 | bail = c._onflag(n.flag, p) | |
| 210 | continue | |
| 211 | } | |
| 212 | ||
| 213 | if (n.arg && c._definedArgs.length > 0) { | |
| 214 | bail = c._onarg(n.arg, p) | |
| 215 | continue | |
| 216 | } | |
| 217 | ||
| 218 | if (c._definedCommands.size > 0) { | |
| 219 | const cmd = c._getCommand(n.arg) | |
| 220 | ||
| 221 | if (cmd !== null) { | |
| 222 | c = cmd._reset() | |
| 223 | visited.push(c) | |
| 224 | continue | |
| 225 | } | |
| 226 | } | |
| 227 | ||
| 228 | p.i-- | |
| 229 | bail = c._onrest(p.rest(), p) | |
| 230 | } | |
| 231 | ||
| 232 | if (c !== this && !c.parent) c.parent = this | |
| 233 | if (c.parent) c.parent.current = c | |
| 234 | ||
| 235 | if (!bail) { | |
| 236 | const hasHelpFlag = 'help' in this.indices.flags | |
| 237 | const missing = this._definedArgs.filter( | |
| 238 | (arg) => !hasHelpFlag && !arg.optional && !(arg.name in c.args) | |
| 239 | ) | |
| 240 | ||
| 241 | if (missing.length > 0) { | |
| 242 | bail = createBail(this, 'MISSING_ARG', null, { value: missing[0].help }) | |
| 243 | } | |
| 244 | } | |
| 245 | ||
| 246 | if (!bail) { | |
| 247 | if (c.flags.help) { | |
| 248 | if (!opts.silent) console.log(c.help()) | |
| 249 | return null | |
| 250 | } | |
| 251 | for (const v of c._validators) { | |
| 252 | bail = runValidation(v, c) | |
| 253 | if (bail) { | |
| 254 | if (bails) c.bail(bail) | |
| 255 | return null | |
| 256 | } | |
| 257 | } | |
| 258 | if (c._runner !== null && opts.run !== false) { | |
| 259 | c.argv = input | |
| 260 | if (sync) runSync(c) | |
| 261 | else c.running = runAsync(c) | |
| 262 | } | |
| 263 | return c | |
| 264 | } | |
| 265 | ||
| 266 | if (bails) c.bail(bail) | |
| 267 | return null | |
| 268 | } | |
| 269 | ||
| 270 | _oneliner(short, relative) { | |
| 271 | const stack = [this] | |
| 272 | if (!relative) { | |
| 273 | while (stack[stack.length - 1].parent) stack.push(stack[stack.length - 1].parent) | |
| 274 | } | |
| 275 | ||
| 276 | const run = stack.reverse().map((c) => c.name || 'app') | |
| 277 | ||
| 278 | if (!short) { | |
| 279 | if (this._definedFlags.size > 0) run.push('[flags]') | |
| 280 | for (const arg of this._definedArgs) run.push(arg.help) | |
| 281 | if (this._definedCommands.size > 0) run.push('[command]') | |
| 282 | if (this._definedRest !== null) run.push(this._definedRest.help) | |
| 283 | } | |
| 284 | ||
| 285 | return run.join(' ') | |
| 286 | } | |
| 287 | ||
| 288 | _aligned(padding = 0, color = false) { | |
| 289 | const l = [] | |
| 290 | const visited = new Set() | |
| 291 | const indent = this._indent | |
| 292 | if (this.summary) { | |
| 293 | l.push(['', '']) | |
| 294 | l.push(...this.summary.split(EOL).map((line) => [this._indent + line, ''])) | |
| 295 | } | |
| 296 | if (this.description) { | |
| 297 | l.push(['', '']) | |
| 298 | l.push(...this.description.split(EOL).map((line) => [this._indent + line, ''])) | |
| 299 | } | |
| 300 | ||
| 301 | if (this._definedArgs.length > 0 || this._definedRest !== null) { | |
| 302 | l.push(['', '']) | |
| 303 | l.push([indent + this._labels.arguments, '']) | |
| 304 | for (const arg of this._definedArgs) { | |
| 305 | l.push([indent + ' ' + arg.help, arg.usage()]) | |
| 306 | } | |
| 307 | } | |
| 308 | ||
| 309 | if (this._definedRest) { | |
| 310 | l.push([indent + ' ' + this._definedRest.help, this._definedRest.description]) | |
| 311 | } | |
| 312 | ||
| 313 | if (this._definedFlags.size) { | |
| 314 | l.push(['', '']) | |
| 315 | l.push([indent + this._labels.flags, '']) | |
| 316 | for (const flag of this._definedFlags.values()) { | |
| 317 | if (visited.has(flag)) continue | |
| 318 | visited.add(flag) | |
| 319 | if (!flag.hidden) l.push([indent + ' ' + flag.help, flag.description]) | |
| 320 | } | |
| 321 | } | |
| 322 | ||
| 323 | if (this._definedCommands.size) { | |
| 324 | l.push(['', '']) | |
| 325 | l.push([indent + this._labels.commands, '']) | |
| 326 | for (const c of this._definedCommands.values()) { | |
| 327 | if (c.hidden) continue | |
| 328 | l.push([indent + ' ' + c._oneliner(true, true), c.summary || c.description]) | |
| 329 | } | |
| 330 | } | |
| 331 | ||
| 332 | if (padding === 0) { | |
| 333 | for (const [left, right] of l) { | |
| 334 | if (!right) continue | |
| 335 | if (left.length > padding) padding = left.length | |
| 336 | } | |
| 337 | } | |
| 338 | ||
| 339 | if (color === false) { | |
| 340 | return this._aligned(padding, true) | |
| 341 | } | |
| 342 | ||
| 343 | let s = '' | |
| 344 | for (const [left, right] of l) { | |
| 345 | s += (left.padEnd(padding, ' ') + ' ' + right).trimEnd() + EOL | |
| 346 | } | |
| 347 | ||
| 348 | s = s.trimEnd() | |
| 349 | ||
| 350 | return s ? s + EOL : '' | |
| 351 | } | |
| 352 | ||
| 353 | add(...args) { | |
| 354 | const c = this | |
| 355 | for (const a of args) { | |
| 356 | if (a instanceof Command) { | |
| 357 | c._addCommand(a) | |
| 358 | } else if (a instanceof Flag) { | |
| 359 | c._addFlag(a) | |
| 360 | } else if (a instanceof Arg) { | |
| 361 | c._addArg(a) | |
| 362 | } else if (a instanceof Rest) { | |
| 363 | c._addRest(a) | |
| 364 | } else if (a instanceof Data) { | |
| 365 | c._addData(a) | |
| 366 | } else if (a instanceof Validation) { | |
| 367 | c._addValidation(a) | |
| 368 | } else if (typeof a === 'function') { | |
| 369 | c._addRunner(a) | |
| 370 | } else if (a !== null && Array.isArray(a) === false && typeof a === 'object') { | |
| 371 | c._addDefinition(a) | |
| 372 | } else { | |
| 373 | throw new Error('Unknown arg: ' + a) | |
| 374 | } | |
| 375 | } | |
| 376 | c._addFlag(new Flag('--help|-h', 'Show help')) | |
| 377 | if (!c._runner) c._addRunner(noop) | |
| 378 | return c | |
| 379 | } | |
| 380 | ||
| 381 | _addDefinition(c) { | |
| 382 | for (const [def, dsc] of Object.entries(c)) { | |
| 383 | const [definer, ...definition] = def.split(' ') | |
| 384 | const define = module.exports[definer] | |
| 385 | if (typeof define === 'function') { | |
| 386 | const [description, modifiers = {}] = Array.isArray(dsc) ? dsc : [dsc] | |
| 387 | const mod = | |
| 388 | definition.length === 0 ? define(description) : define(definition.join(' '), description) | |
| 389 | for (const [modifier, value] of Object.entries(modifiers)) { | |
| 390 | if (typeof mod[modifier] === 'function') { | |
| 391 | if (value === true) mod[modifier]() | |
| 392 | else if (value !== false) mod[modifier](value) | |
| 393 | } | |
| 394 | } | |
| 395 | this.add(mod) | |
| 396 | } | |
| 397 | } | |
| 398 | } | |
| 399 | ||
| 400 | _addCommand(c) { | |
| 401 | if (!c.header) c.header = this.header | |
| 402 | if (!c.footer) c.footer = this.footer | |
| 403 | if (!c.parent) c.parent = this | |
| 404 | this._definedCommands.set(c.name, c) | |
| 405 | } | |
| 406 | ||
| 407 | _addFlag(f) { | |
| 408 | this._definedFlags.set(f.name, f) | |
| 409 | for (const alias of f.aliases) { | |
| 410 | if (alias !== f.name) { | |
| 411 | this._definedFlags.set(alias, f) | |
| 412 | } | |
| 413 | } | |
| 414 | } | |
| 415 | ||
| 416 | _addArg(a) { | |
| 417 | this._definedArgs.push(a) | |
| 418 | } | |
| 419 | ||
| 420 | _addRest(a) { | |
| 421 | this._definedRest = a | |
| 422 | } | |
| 423 | ||
| 424 | _addData(d) { | |
| 425 | switch (d.type) { | |
| 426 | case 'bail': { | |
| 427 | if (this._onbail !== null) throw new Error('onbail already set') | |
| 428 | this._onbail = d.value | |
| 429 | break | |
| 430 | } | |
| 431 | case 'name': { | |
| 432 | this.name = d.value | |
| 433 | break | |
| 434 | } | |
| 435 | case 'summary': { | |
| 436 | this.summary = d.value | |
| 437 | break | |
| 438 | } | |
| 439 | case 'description': { | |
| 440 | this.description = d.value | |
| 441 | break | |
| 442 | } | |
| 443 | case 'header': { | |
| 444 | this.header = unindent(d.value) | |
| 445 | break | |
| 446 | } | |
| 447 | case 'footer': { | |
| 448 | this.footer = | |
| 449 | d.value !== null && typeof d.value === 'object' | |
| 450 | ? { help: unindent(d.value.help || ''), overview: d.value.overview } | |
| 451 | : unindent(d.value) | |
| 452 | break | |
| 453 | } | |
| 454 | case 'sloppy': { | |
| 455 | this._strictFlags = !d.value?.flags | |
| 456 | this._strictArgs = !d.value?.args | |
| 457 | break | |
| 458 | } | |
| 459 | } | |
| 460 | } | |
| 461 | ||
| 462 | _addValidation(_validator) { | |
| 463 | this._validators.push(_validator) | |
| 464 | } | |
| 465 | ||
| 466 | _addRunner(_runner) { | |
| 467 | this._runner = _runner | |
| 468 | } | |
| 469 | ||
| 470 | _reset() { | |
| 471 | this.flags = {} | |
| 472 | this.args = {} | |
| 473 | this.positionals = [] | |
| 474 | this.rest = null | |
| 475 | this.indices = { flags: {}, args: {}, positionals: [], rest: undefined } | |
| 476 | this.running = null | |
| 477 | this.argv = null | |
| 478 | for (const [name, { value }] of this._definedFlags) { | |
| 479 | if (name === snakeToCamel(name)) { | |
| 480 | this.flags[name] = value | |
| 481 | } | |
| 482 | } | |
| 483 | return this | |
| 484 | } | |
| 485 | ||
| 486 | _getFlag(name, flag) { | |
| 487 | let f = this._definedFlags.get(name) | |
| 488 | if (f && flag.long && f.aliases.indexOf(name) > 0) f = undefined | |
| 489 | if (f === undefined && this._strictFlags === false) f = defaultFlag(name) | |
| 490 | return f || null | |
| 491 | } | |
| 492 | ||
| 493 | _getCommand(name) { | |
| 494 | return this._definedCommands.get(name) || null | |
| 495 | } | |
| 496 | ||
| 497 | _onflag(flag, parser) { | |
| 498 | const def = this._getFlag(flag.name, flag) | |
| 499 | if (def === null) return createBail(this, 'UNKNOWN_FLAG', flag, null) | |
| 500 | ||
| 501 | if (def.boolean === true) { | |
| 502 | if (flag.value) return createBail(this, 'INVALID_FLAG', flag, null) | |
| 503 | this.flags[def.name] = !flag.inverse | |
| 504 | if (def.aliases[1]) this.flags[def.aliases[1]] = this.flags[def.name] | |
| 505 | def.aliases.forEach((e) => { | |
| 506 | this.indices.flags[e] = parser.lasti | |
| 507 | }) | |
| 508 | if (!def.aliases.includes(def.name)) this.indices.flags[def.name] = parser.lasti | |
| 509 | return null | |
| 510 | } | |
| 511 | ||
| 512 | if (def.boolean === false) { | |
| 513 | if (flag.value) { | |
| 514 | if (def.valueChoices && !def.valueChoices.includes(flag.value)) { | |
| 515 | return createBail(this, 'INVALID_FLAG', flag, null) | |
| 516 | } | |
| 517 | ||
| 518 | const value = def.multi ? (this.flags[def.name] || []).concat(flag.value) : flag.value | |
| 519 | this.flags[def.name] = value | |
| 520 | if (def.aliases[1]) this.flags[def.aliases[1]] = value | |
| 521 | def.aliases.forEach((e) => { | |
| 522 | this.indices.flags[e] = parser.lasti | |
| 523 | }) | |
| 524 | if (!def.aliases.includes(def.name)) this.indices.flags[def.name] = parser.lasti | |
| 525 | return null | |
| 526 | } | |
| 527 | ||
| 528 | const next = parser.next() | |
| 529 | const argless = next === null || !next.arg | |
| 530 | if (def.valueRequired && argless && !def.hasDefault) { | |
| 531 | return createBail(this, 'INVALID_FLAG', flag, null) | |
| 532 | } | |
| 533 | ||
| 534 | const nextValue = !next?.arg && def.hasDefault ? def.value : next?.arg | |
| 535 | if (def.valueChoices && !def.valueChoices.includes(nextValue)) { | |
| 536 | return createBail(this, 'INVALID_FLAG', flag, null) | |
| 537 | } | |
| 538 | const value = def.multi ? (this.flags[def.name] || []).concat(nextValue) : nextValue | |
| 539 | this.flags[def.name] = value | |
| 540 | if (def.aliases[1]) this.flags[def.aliases[1]] = value | |
| 541 | if (def.multi) { | |
| 542 | def.aliases.forEach((e) => { | |
| 543 | this.indices.flags[e] = (this.indices.flags[e] || []).concat(parser.lasti) | |
| 544 | }) | |
| 545 | if (!def.aliases.includes(def.name)) { | |
| 546 | this.indices.flags[def.name] = (this.indices.flags[def.name] || []).concat(parser.lasti) | |
| 547 | } | |
| 548 | } else { | |
| 549 | def.aliases.forEach((e) => { | |
| 550 | this.indices.flags[e] = parser.lasti | |
| 551 | }) | |
| 552 | if (!def.aliases.includes(def.name)) this.indices.flags[def.name] = parser.lasti | |
| 553 | } | |
| 554 | } | |
| 555 | ||
| 556 | return null | |
| 557 | } | |
| 558 | ||
| 559 | _onarg(arg, parser) { | |
| 560 | const info = { index: this.positionals.length, value: arg } | |
| 561 | if (this._definedArgs.length <= this.positionals.length) { | |
| 562 | if (this._strictArgs === false) { | |
| 563 | this.positionals.push(arg) | |
| 564 | this.indices.positionals.push(parser.lasti) | |
| 565 | return null | |
| 566 | } | |
| 567 | ||
| 568 | return createBail(this, 'UNKNOWN_ARG', null, info) | |
| 569 | } | |
| 570 | ||
| 571 | const def = this._definedArgs[this.positionals.length] | |
| 572 | this.positionals.push(arg) | |
| 573 | this.indices.positionals.push(parser.lasti) | |
| 574 | this.args[def.name] = arg | |
| 575 | this.indices.args[def.name] = parser.lasti | |
| 576 | ||
| 577 | return null | |
| 578 | } | |
| 579 | ||
| 580 | _onrest(rest, parser) { | |
| 581 | if (this._definedRest === null && this._strictArgs) { | |
| 582 | return createBail(this, 'UNKNOWN_ARG', null, { | |
| 583 | index: this.positionals.length, | |
| 584 | value: rest.join(' ') | |
| 585 | }) | |
| 586 | } | |
| 587 | ||
| 588 | this.rest = rest | |
| 589 | this.indices.rest = parser.lasti | |
| 590 | return null | |
| 591 | } | |
| 592 | ||
| 593 | _bail(bail) { | |
| 594 | if (typeof this._onbail === 'function') return this._onbail(bail) | |
| 595 | if (this.parent) return this.parent._bail(bail) | |
| 596 | if (bail.flag) throw new Bail(bail.reason + ': ' + bail.flag.name) | |
| 597 | if (bail.arg) throw new Bail(bail.reason + ': ' + bail.arg.value) | |
| 598 | throw new Bail(bail.reason) | |
| 599 | } | |
| 600 | } | |
| 601 | ||
| 602 | class Flag { | |
| 603 | constructor(spec, description = '') { | |
| 604 | const { longName, shortName, aliases, boolean, help, value, valueRequired } = parseFlag(spec) | |
| 605 | this.name = snakeToCamel(longName || shortName) | |
| 606 | this.aliases = aliases | |
| 607 | this.boolean = boolean | |
| 608 | this.help = help | |
| 609 | this.description = description | |
| 610 | this.hidden = false | |
| 611 | this.multi = false | |
| 612 | this.valueChoices = undefined | |
| 613 | this.hasDefault = false | |
| 614 | this.value = value | |
| 615 | this.valueRequired = valueRequired | |
| 616 | } | |
| 617 | ||
| 618 | default(val) { | |
| 619 | this.hasDefault = true | |
| 620 | this.value = val | |
| 621 | this.description += ` (default: ${val})` | |
| 622 | return this | |
| 623 | } | |
| 624 | ||
| 625 | multiple() { | |
| 626 | this.multi = true | |
| 627 | return this | |
| 628 | } | |
| 629 | ||
| 630 | choices(valueChoices) { | |
| 631 | this.valueChoices = valueChoices | |
| 632 | this.description += ` (choices: ${valueChoices.join(', ')})` | |
| 633 | return this | |
| 634 | } | |
| 635 | ||
| 636 | hide() { | |
| 637 | this.hidden = true | |
| 638 | return this | |
| 639 | } | |
| 640 | } | |
| 641 | ||
| 642 | class Arg { | |
| 643 | constructor(help, description = '') { | |
| 644 | this.optional = help.startsWith('[') | |
| 645 | this.name = snakeToCamel(help) | |
| 646 | this.help = help | |
| 647 | this.description = description | |
| 648 | this.hidden = false | |
| 649 | } | |
| 650 | ||
| 651 | hide() { | |
| 652 | this.hidden = true | |
| 653 | return this | |
| 654 | } | |
| 655 | ||
| 656 | usage() { | |
| 657 | if (!this.optional) return this.description | |
| 658 | const first = this.description.slice(0, 1) | |
| 659 | return ( | |
| 660 | (this.description && first.toLowerCase() === first ? 'optional. ' : 'Optional. ') + | |
| 661 | this.description | |
| 662 | ) | |
| 663 | } | |
| 664 | } | |
| 665 | ||
| 666 | class Rest { | |
| 667 | constructor(help, description = '') { | |
| 668 | this.help = help | |
| 669 | this.description = description | |
| 670 | } | |
| 671 | } | |
| 672 | ||
| 673 | class Data { | |
| 674 | constructor(type, value) { | |
| 675 | this.type = type | |
| 676 | this.value = value | |
| 677 | } | |
| 678 | } | |
| 679 | ||
| 680 | class Validation { | |
| 681 | constructor(validator, description = 'invalid command') { | |
| 682 | if (typeof validator === 'string') [description, validator] = [validator, description] | |
| 683 | this.validator = validator | |
| 684 | this.description = description | |
| 685 | } | |
| 686 | } | |
| 687 | ||
| 688 | function argv() { | |
| 689 | return typeof process === 'undefined' ? global.Bare.argv.slice(2) : process.argv.slice(2) | |
| 690 | } | |
| 691 | ||
| 692 | function command(name, ...args) { | |
| 693 | if ( | |
| 694 | name !== null && | |
| 695 | typeof name === 'object' && | |
| 696 | Object.getPrototypeOf(name) === Object.prototype | |
| 697 | ) { | |
| 698 | const arg = name | |
| 699 | args.unshift(arg) | |
| 700 | name = arg.name | |
| 701 | } | |
| 702 | const c = new Command(null, name) | |
| 703 | c.add(...args) | |
| 704 | return c | |
| 705 | } | |
| 706 | ||
| 707 | // deprecated, removed soon, use.hide() | |
| 708 | function hiddenCommand(name, ...args) { | |
| 709 | return command(name, ...args).hide() | |
| 710 | } | |
| 711 | ||
| 712 | function bail(fn) { | |
| 713 | return new Data('bail', fn) | |
| 714 | } | |
| 715 | ||
| 716 | function sloppy(opts) { | |
| 717 | return new Data('sloppy', opts) | |
| 718 | } | |
| 719 | ||
| 720 | function summary(desc) { | |
| 721 | return new Data('summary', desc) | |
| 722 | } | |
| 723 | ||
| 724 | function description(desc, ...values) { | |
| 725 | if (Array.isArray(desc)) return description(dedent(desc, values)) | |
| 726 | return new Data('description', desc) | |
| 727 | } | |
| 728 | ||
| 729 | function header(desc) { | |
| 730 | return new Data('header', desc) | |
| 731 | } | |
| 732 | ||
| 733 | function footer(desc) { | |
| 734 | return new Data('footer', desc) | |
| 735 | } | |
| 736 | ||
| 737 | // deprecated, removed soon, use .hide() | |
| 738 | function hiddenFlag(help, description) { | |
| 739 | return flag(help, description).hide() | |
| 740 | } | |
| 741 | ||
| 742 | function flag(help, description) { | |
| 743 | return new Flag(help, description) | |
| 744 | } | |
| 745 | ||
| 746 | function rest(help, description) { | |
| 747 | return new Rest(help, description) | |
| 748 | } | |
| 749 | ||
| 750 | function arg(help, description) { | |
| 751 | return new Arg(help, description) | |
| 752 | } | |
| 753 | ||
| 754 | function validate(validator, description) { | |
| 755 | return new Validation(validator, description) | |
| 756 | } | |
| 757 | ||
| 758 | function unindent(info) { | |
| 759 | const lines = info.split('\n').filter(trimLine) | |
| 760 | if (!lines.length) return '' | |
| 761 | const indent = lines[0].match(/^(\s*)/)[1] | |
| 762 | ||
| 763 | let s = '' | |
| 764 | for (const line of lines) s += line.slice(indent.length).trimEnd() + '\n' | |
| 765 | return s.trimEnd() | |
| 766 | } | |
| 767 | ||
| 768 | function trimLine(line) { | |
| 769 | return line.trim() | |
| 770 | } | |
| 771 | ||
| 772 | function snakeToCamel(name) { | |
| 773 | const parts = name.match(/([a-zA-Z0-9-]+)/)[1].split(/-+/) | |
| 774 | for (let i = 1; i < parts.length; i++) { | |
| 775 | parts[i] = parts[i].slice(0, 1).toUpperCase() + parts[i].slice(1) | |
| 776 | } | |
| 777 | return parts.join('') | |
| 778 | } | |
| 779 | ||
| 780 | function parseFlag(help) { | |
| 781 | const parts = help.split(/[| ]/) | |
| 782 | const result = { | |
| 783 | longName: null, | |
| 784 | shortName: null, | |
| 785 | aliases: [], | |
| 786 | boolean: true, | |
| 787 | help, | |
| 788 | value: null, | |
| 789 | inverse: false, | |
| 790 | valueRequired: false | |
| 791 | } | |
| 792 | for (const p of parts) { | |
| 793 | result.inverse = p.startsWith('--no-') | |
| 794 | if (p.startsWith('--')) { | |
| 795 | const name = result.inverse ? trimFlag(p).slice(3) : trimFlag(p) | |
| 796 | if (result.longName === null) result.longName = name | |
| 797 | result.value = result.inverse | |
| 798 | result.aliases.push(name) | |
| 799 | continue | |
| 800 | } | |
| 801 | ||
| 802 | if (p.startsWith('-')) { | |
| 803 | const name = trimFlag(p) | |
| 804 | if (result.shortName === null) result.shortName = name | |
| 805 | result.value = false | |
| 806 | result.aliases.push(name) | |
| 807 | continue | |
| 808 | } | |
| 809 | ||
| 810 | const valueRequired = p.startsWith('<') | |
| 811 | const stringFlag = p.startsWith('[') || valueRequired | |
| 812 | if (stringFlag) { | |
| 813 | result.boolean = false | |
| 814 | result.value = undefined | |
| 815 | result.valueRequired = valueRequired | |
| 816 | } | |
| 817 | } | |
| 818 | ||
| 819 | return result | |
| 820 | } | |
| 821 | ||
| 822 | function trimFlag(s) { | |
| 823 | return s.replace(/(^[^0-9\w]+)|([^0-9\w]+$)/g, '') | |
| 824 | } | |
| 825 | ||
| 826 | function createBail(command, reason, flag, arg, err) { | |
| 827 | return { command, reason, flag, arg, err } | |
| 828 | } | |
| 829 | ||
| 830 | function defaultFlag(name) { | |
| 831 | return { | |
| 832 | name, | |
| 833 | aliases: [name], | |
| 834 | boolean: false, | |
| 835 | help: '', | |
| 836 | description: '', | |
| 837 | value: '' | |
| 838 | } | |
| 839 | } | |
| 840 | ||
| 841 | function dedent(strings, values) { | |
| 842 | const raw = String.raw(strings, ...values) | |
| 843 | const lines = raw.split('\n') | |
| 844 | ||
| 845 | let minIndent = Infinity | |
| 846 | for (const line of lines) { | |
| 847 | let i = 0 | |
| 848 | while (i < line.length && line[i] === ' ') i++ | |
| 849 | if (i < line.length) minIndent = Math.min(minIndent, i) | |
| 850 | } | |
| 851 | ||
| 852 | if (minIndent === Infinity) return raw.trim() | |
| 853 | ||
| 854 | let result = '' | |
| 855 | for (const line of lines) { | |
| 856 | result += line.length >= minIndent ? line.slice(minIndent) + '\n' : '\n' | |
| 857 | } | |
| 858 | ||
| 859 | return result.trim() | |
| 860 | } | |
| 861 | ||
| 862 | function runValidation(v, c) { | |
| 863 | try { | |
| 864 | const isValid = v.validator({ | |
| 865 | args: c.args, | |
| 866 | flags: c.flags, | |
| 867 | positionals: c.positionals, | |
| 868 | rest: c.rest, | |
| 869 | indices: c.indices, | |
| 870 | command: c | |
| 871 | }) | |
| 872 | if (!isValid) { | |
| 873 | const err = new Error(v.description) | |
| 874 | err.code = 'ERR_INVALID' | |
| 875 | err.bail = { reason: v.description } | |
| 876 | throw err | |
| 877 | } | |
| 878 | return null | |
| 879 | } catch (err) { | |
| 880 | return createBail(c, err.bail?.reason ?? err.stack, err.bail?.flag, err.bail?.arg, err) | |
| 881 | } | |
| 882 | } | |
| 883 | ||
| 884 | function runSync(c) { | |
| 885 | try { | |
| 886 | c._runner({ | |
| 887 | args: c.args, | |
| 888 | flags: c.flags, | |
| 889 | positionals: c.positionals, | |
| 890 | rest: c.rest, | |
| 891 | indices: c.indices, | |
| 892 | argv: c.argv, | |
| 893 | command: c | |
| 894 | }) | |
| 895 | } catch (err) { | |
| 896 | c.bail(createBail(c, err.stack, null, null, err)) | |
| 897 | } | |
| 898 | } | |
| 899 | ||
| 900 | async function runAsync(c) { | |
| 901 | try { | |
| 902 | await c._runner({ | |
| 903 | args: c.args, | |
| 904 | flags: c.flags, | |
| 905 | positionals: c.positionals, | |
| 906 | rest: c.rest, | |
| 907 | indices: c.indices, | |
| 908 | argv: c.argv, | |
| 909 | command: c | |
| 910 | }) | |
| 911 | } catch (err) { | |
| 912 | c.bail(createBail(c, err.stack, null, null, err)) | |
| 913 | } | |
| 914 | } | |
| 915 | ||
| 916 | class Bail extends Error { | |
| 917 | name = 'Bail' | |
| 918 | } |
| … | … | @@ -0,0 +1,32 @@ |
| 1 | { | |
| 2 | "name": "paparam", | |
| 3 | "version": "1.10.1", | |
| 4 | "description": "Strict and fair parameter papa", | |
| 5 | "main": "index.js", | |
| 6 | "scripts": { | |
| 7 | "format": "prettier --write .", | |
| 8 | "lint": "prettier --check . && lunte", | |
| 9 | "test": "npm run test:node && npm run test:bare", | |
| 10 | "test:node": "brittle-node test.js", | |
| 11 | "test:bare": "brittle-bare test.js" | |
| 12 | }, | |
| 13 | "devDependencies": { | |
| 14 | "brittle": "^3.5.0", | |
| 15 | "lunte": "^1.0.0", | |
| 16 | "prettier": "^3.6.2", | |
| 17 | "prettier-config-holepunch": "^2.0.0" | |
| 18 | }, | |
| 19 | "files": [ | |
| 20 | "index.js" | |
| 21 | ], | |
| 22 | "repository": { | |
| 23 | "type": "git", | |
| 24 | "url": "https://github.com/holepunchto/paparam.git" | |
| 25 | }, | |
| 26 | "author": "Holepunch Inc.", | |
| 27 | "license": "Apache-2.0", | |
| 28 | "bugs": { | |
| 29 | "url": "https://github.com/holepunchto/paparam/issues" | |
| 30 | }, | |
| 31 | "homepage": "https://github.com/holepunchto/paparam" | |
| 32 | } |
| … | … | @@ -0,0 +1,6 @@ |
| 1 | language: node_js | |
| 2 | node_js: | |
| 3 | - "9" | |
| 4 | - "8" | |
| 5 | - "6" | |
| 6 | - "4" |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2018 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,73 @@ |
| 1 | # protocol-buffers-encodings | |
| 2 | ||
| 3 | Base encodings for [protocol-buffers](https://github.com/mafintosh/protocol-buffers). | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install protocol-buffers-encodings | |
| 7 | ``` | |
| 8 | ||
| 9 | Note: use Node.js >= 16.15.0 to avoid a performance regression due to a slower `Buffer.subarray` function. | |
| 10 | ||
| 11 | [](https://travis-ci.org/mafintosh/protocol-buffers-encodings) | |
| 12 | ||
| 13 | Moved into its own module for lighter installs | |
| 14 | ||
| 15 | ## Usage | |
| 16 | ||
| 17 | ``` js | |
| 18 | var encodings = require('protocol-buffers-encodings') | |
| 19 | var buf = Buffer.alloc(4096) | |
| 20 | ||
| 21 | encodings.string('hi', buf, 0) | |
| 22 | console.log(encodings.string(buf, 0)) // prints 'hi' | |
| 23 | ``` | |
| 24 | ||
| 25 | ## API | |
| 26 | ||
| 27 | In general all encoders follow this API | |
| 28 | ||
| 29 | #### `buffer = enc.encode(value, buffer, offset)` | |
| 30 | ||
| 31 | Encode a value. `buffer` should be a buffer big enough to fit the value, `offset` should be the byte offset in the buffer where you want to write it. | |
| 32 | The buffer is returned for convenience. | |
| 33 | ||
| 34 | After a value has been encoded `enc.encode.bytes` contains the amount of bytes used in the buffer. | |
| 35 | ||
| 36 | #### `value = enc.decode(buffer, offset)` | |
| 37 | ||
| 38 | Decode a value. `buffer` should be an encoded value and `offset` should be the byte offset where you want to start decoding. | |
| 39 | ||
| 40 | After a value has been decoded `enc.decode.bytes` contains the amount of bytes that was consumed from the buffer. | |
| 41 | ||
| 42 | #### `var len = enc.encodingLength(value)` | |
| 43 | ||
| 44 | Use this method to calculate how much space is needed to encode a value. | |
| 45 | ||
| 46 | #### `enc.type` | |
| 47 | ||
| 48 | A number indicating the protobuf wire type for the encoding | |
| 49 | ||
| 50 | ## Encodings | |
| 51 | ||
| 52 | The following encodings are available | |
| 53 | ||
| 54 | * `encodings.bytes` - encode a buffer | |
| 55 | * `encodings.string` - encode a string | |
| 56 | * `encodings.bool` - encode a boolean | |
| 57 | * `encodings.uint64` - encode a uint64 to varint | |
| 58 | * `encodings.uint32` - encode a uint32 to varint | |
| 59 | * `encodings.sint64` - encode a signed int64 to a signed varint | |
| 60 | * `encodings.sint32` - encode a signed int32 to a signed varint | |
| 61 | * `encodings.int64` - encode a signed int64 to a varint | |
| 62 | * `encodings.int32` - encode a signed int32 to a varint | |
| 63 | * `encodings.fixed32` - encode a uint32 to a fixed 4 byte buffer | |
| 64 | * `encodings.sfixed32` - encode a signed int32 to a fixed 4 byte buffer | |
| 65 | * `encodings.fixed64` - encode a uint64 (represented as a buffer) to a fixed 8 byte buffer | |
| 66 | * `encodings.sfixed64` - encode a signed int64 (represented as a buffer) to a fixed 8 byte buffer | |
| 67 | * `encodings.double` - encode a double | |
| 68 | * `encodings.float` - encode a float | |
| 69 | * `encodings.enum` - encode a numeric enum as a varint | |
| 70 | ||
| 71 | ## License | |
| 72 | ||
| 73 | MIT |
| … | … | @@ -0,0 +1,287 @@ |
| 1 | var varint = require('varint') | |
| 2 | var svarint = require('signed-varint') | |
| 3 | var b4a = require('b4a') | |
| 4 | ||
| 5 | exports.make = encoder | |
| 6 | ||
| 7 | exports.name = function (enc) { | |
| 8 | var keys = Object.keys(exports) | |
| 9 | for (var i = 0; i < keys.length; i++) { | |
| 10 | if (exports[keys[i]] === enc) return keys[i] | |
| 11 | } | |
| 12 | return null | |
| 13 | } | |
| 14 | ||
| 15 | exports.skip = function (type, buffer, offset) { | |
| 16 | switch (type) { | |
| 17 | case 0: | |
| 18 | varint.decode(buffer, offset) | |
| 19 | return offset + varint.decode.bytes | |
| 20 | ||
| 21 | case 1: | |
| 22 | return offset + 8 | |
| 23 | ||
| 24 | case 2: | |
| 25 | var len = varint.decode(buffer, offset) | |
| 26 | return offset + varint.decode.bytes + len | |
| 27 | ||
| 28 | case 3: | |
| 29 | case 4: | |
| 30 | throw new Error('Groups are not supported') | |
| 31 | ||
| 32 | case 5: | |
| 33 | return offset + 4 | |
| 34 | } | |
| 35 | ||
| 36 | throw new Error('Unknown wire type: ' + type) | |
| 37 | } | |
| 38 | ||
| 39 | exports.bytes = encoder(2, | |
| 40 | function encode (val, buffer, offset) { | |
| 41 | var oldOffset = offset | |
| 42 | var len = bufferLength(val) | |
| 43 | ||
| 44 | varint.encode(len, buffer, offset) | |
| 45 | offset += varint.encode.bytes | |
| 46 | ||
| 47 | if (b4a.isBuffer(val)) b4a.copy(val, buffer, offset) | |
| 48 | else b4a.write(buffer, val, offset, len) | |
| 49 | offset += len | |
| 50 | ||
| 51 | encode.bytes = offset - oldOffset | |
| 52 | return buffer | |
| 53 | }, | |
| 54 | function decode (buffer, offset) { | |
| 55 | var oldOffset = offset | |
| 56 | ||
| 57 | var len = varint.decode(buffer, offset) | |
| 58 | offset += varint.decode.bytes | |
| 59 | ||
| 60 | var val = buffer.subarray(offset, offset + len) | |
| 61 | offset += val.length | |
| 62 | ||
| 63 | decode.bytes = offset - oldOffset | |
| 64 | return val | |
| 65 | }, | |
| 66 | function encodingLength (val) { | |
| 67 | var len = bufferLength(val) | |
| 68 | return varint.encodingLength(len) + len | |
| 69 | } | |
| 70 | ) | |
| 71 | ||
| 72 | exports.string = encoder(2, | |
| 73 | function encode (val, buffer, offset) { | |
| 74 | var oldOffset = offset | |
| 75 | var len = b4a.byteLength(val) | |
| 76 | ||
| 77 | varint.encode(len, buffer, offset, 'utf-8') | |
| 78 | offset += varint.encode.bytes | |
| 79 | ||
| 80 | b4a.write(buffer, val, offset, len) | |
| 81 | offset += len | |
| 82 | ||
| 83 | encode.bytes = offset - oldOffset | |
| 84 | return buffer | |
| 85 | }, | |
| 86 | function decode (buffer, offset) { | |
| 87 | var oldOffset = offset | |
| 88 | ||
| 89 | var len = varint.decode(buffer, offset) | |
| 90 | offset += varint.decode.bytes | |
| 91 | ||
| 92 | var val = b4a.toString(buffer, 'utf-8', offset, offset + len) | |
| 93 | offset += len | |
| 94 | ||
| 95 | decode.bytes = offset - oldOffset | |
| 96 | return val | |
| 97 | }, | |
| 98 | function encodingLength (val) { | |
| 99 | var len = b4a.byteLength(val) | |
| 100 | return varint.encodingLength(len) + len | |
| 101 | } | |
| 102 | ) | |
| 103 | ||
| 104 | exports.bool = encoder(0, | |
| 105 | function encode (val, buffer, offset) { | |
| 106 | buffer[offset] = val ? 1 : 0 | |
| 107 | encode.bytes = 1 | |
| 108 | return buffer | |
| 109 | }, | |
| 110 | function decode (buffer, offset) { | |
| 111 | var bool = buffer[offset] > 0 | |
| 112 | decode.bytes = 1 | |
| 113 | return bool | |
| 114 | }, | |
| 115 | function encodingLength () { | |
| 116 | return 1 | |
| 117 | } | |
| 118 | ) | |
| 119 | ||
| 120 | exports.int32 = encoder(0, | |
| 121 | function encode (val, buffer, offset) { | |
| 122 | varint.encode(val < 0 ? val + 4294967296 : val, buffer, offset) | |
| 123 | encode.bytes = varint.encode.bytes | |
| 124 | return buffer | |
| 125 | }, | |
| 126 | function decode (buffer, offset) { | |
| 127 | var val = varint.decode(buffer, offset) | |
| 128 | decode.bytes = varint.decode.bytes | |
| 129 | return val > 2147483647 ? val - 4294967296 : val | |
| 130 | }, | |
| 131 | function encodingLength (val) { | |
| 132 | return varint.encodingLength(val < 0 ? val + 4294967296 : val) | |
| 133 | } | |
| 134 | ) | |
| 135 | ||
| 136 | exports.int64 = encoder(0, | |
| 137 | function encode (val, buffer, offset) { | |
| 138 | if (val < 0) { | |
| 139 | var last = offset + 9 | |
| 140 | varint.encode(val * -1, buffer, offset) | |
| 141 | offset += varint.encode.bytes - 1 | |
| 142 | buffer[offset] = buffer[offset] | 0x80 | |
| 143 | while (offset < last - 1) { | |
| 144 | offset++ | |
| 145 | buffer[offset] = 0xff | |
| 146 | } | |
| 147 | buffer[last] = 0x01 | |
| 148 | encode.bytes = 10 | |
| 149 | } else { | |
| 150 | varint.encode(val, buffer, offset) | |
| 151 | encode.bytes = varint.encode.bytes | |
| 152 | } | |
| 153 | return buffer | |
| 154 | }, | |
| 155 | function decode (buffer, offset) { | |
| 156 | var val = varint.decode(buffer, offset) | |
| 157 | if (val >= Math.pow(2, 63)) { | |
| 158 | var limit = 9 | |
| 159 | while (buffer[offset + limit - 1] === 0xff) limit-- | |
| 160 | limit = limit || 9 | |
| 161 | var subset = b4a.allocUnsafe(limit) | |
| 162 | b4a.copy(buffer, subset, 0, offset, offset + limit) | |
| 163 | subset[limit - 1] = subset[limit - 1] & 0x7f | |
| 164 | val = -1 * varint.decode(subset, 0) | |
| 165 | decode.bytes = 10 | |
| 166 | } else { | |
| 167 | decode.bytes = varint.decode.bytes | |
| 168 | } | |
| 169 | return val | |
| 170 | }, | |
| 171 | function encodingLength (val) { | |
| 172 | return val < 0 ? 10 : varint.encodingLength(val) | |
| 173 | } | |
| 174 | ) | |
| 175 | ||
| 176 | exports.sint32 = | |
| 177 | exports.sint64 = encoder(0, | |
| 178 | svarint.encode, | |
| 179 | svarint.decode, | |
| 180 | svarint.encodingLength | |
| 181 | ) | |
| 182 | ||
| 183 | exports.uint32 = | |
| 184 | exports.uint64 = | |
| 185 | exports.enum = | |
| 186 | exports.varint = encoder(0, | |
| 187 | varint.encode, | |
| 188 | varint.decode, | |
| 189 | varint.encodingLength | |
| 190 | ) | |
| 191 | ||
| 192 | // we cannot represent these in javascript so we just use buffers | |
| 193 | exports.fixed64 = | |
| 194 | exports.sfixed64 = encoder(1, | |
| 195 | function encode (val, buffer, offset) { | |
| 196 | b4a.copy(val, buffer, offset) | |
| 197 | encode.bytes = 8 | |
| 198 | return buffer | |
| 199 | }, | |
| 200 | function decode (buffer, offset) { | |
| 201 | var val = buffer.subarray(offset, offset + 8) | |
| 202 | decode.bytes = 8 | |
| 203 | return val | |
| 204 | }, | |
| 205 | function encodingLength () { | |
| 206 | return 8 | |
| 207 | } | |
| 208 | ) | |
| 209 | ||
| 210 | exports.double = encoder(1, | |
| 211 | function encode (val, buffer, offset) { | |
| 212 | b4a.writeDoubleLE(buffer, val, offset) | |
| 213 | encode.bytes = 8 | |
| 214 | return buffer | |
| 215 | }, | |
| 216 | function decode (buffer, offset) { | |
| 217 | var val = b4a.readDoubleLE(buffer, offset) | |
| 218 | decode.bytes = 8 | |
| 219 | return val | |
| 220 | }, | |
| 221 | function encodingLength () { | |
| 222 | return 8 | |
| 223 | } | |
| 224 | ) | |
| 225 | ||
| 226 | exports.fixed32 = encoder(5, | |
| 227 | function encode (val, buffer, offset) { | |
| 228 | b4a.writeUInt32LE(buffer, val, offset) | |
| 229 | encode.bytes = 4 | |
| 230 | return buffer | |
| 231 | }, | |
| 232 | function decode (buffer, offset) { | |
| 233 | var val = b4a.readUInt32LE(buffer, offset) | |
| 234 | decode.bytes = 4 | |
| 235 | return val | |
| 236 | }, | |
| 237 | function encodingLength () { | |
| 238 | return 4 | |
| 239 | } | |
| 240 | ) | |
| 241 | ||
| 242 | exports.sfixed32 = encoder(5, | |
| 243 | function encode (val, buffer, offset) { | |
| 244 | b4a.writeInt32LE(buffer, val, offset) | |
| 245 | encode.bytes = 4 | |
| 246 | return buffer | |
| 247 | }, | |
| 248 | function decode (buffer, offset) { | |
| 249 | var val = b4a.readInt32LE(buffer, offset) | |
| 250 | decode.bytes = 4 | |
| 251 | return val | |
| 252 | }, | |
| 253 | function encodingLength () { | |
| 254 | return 4 | |
| 255 | } | |
| 256 | ) | |
| 257 | ||
| 258 | exports.float = encoder(5, | |
| 259 | function encode (val, buffer, offset) { | |
| 260 | b4a.writeFloatLE(buffer, val, offset) | |
| 261 | encode.bytes = 4 | |
| 262 | return buffer | |
| 263 | }, | |
| 264 | function decode (buffer, offset) { | |
| 265 | var val = b4a.readFloatLE(buffer, offset) | |
| 266 | decode.bytes = 4 | |
| 267 | return val | |
| 268 | }, | |
| 269 | function encodingLength () { | |
| 270 | return 4 | |
| 271 | } | |
| 272 | ) | |
| 273 | ||
| 274 | function encoder (type, encode, decode, encodingLength) { | |
| 275 | encode.bytes = decode.bytes = 0 | |
| 276 | ||
| 277 | return { | |
| 278 | type: type, | |
| 279 | encode: encode, | |
| 280 | decode: decode, | |
| 281 | encodingLength: encodingLength | |
| 282 | } | |
| 283 | } | |
| 284 | ||
| 285 | function bufferLength (val) { | |
| 286 | return b4a.isBuffer(val) ? val.length : b4a.byteLength(val) | |
| 287 | } |
| … | … | @@ -0,0 +1,28 @@ |
| 1 | { | |
| 2 | "name": "protocol-buffers-encodings", | |
| 3 | "version": "1.2.0", | |
| 4 | "description": "Base encodings for protocol-buffers", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "b4a": "^1.6.0", | |
| 8 | "signed-varint": "^2.0.1", | |
| 9 | "varint": "5.0.0" | |
| 10 | }, | |
| 11 | "devDependencies": { | |
| 12 | "standard": "^14.3.4", | |
| 13 | "tape": "^5.0.1" | |
| 14 | }, | |
| 15 | "scripts": { | |
| 16 | "test": "standard && tape test.js" | |
| 17 | }, | |
| 18 | "repository": { | |
| 19 | "type": "git", | |
| 20 | "url": "https://github.com/mafintosh/protocol-buffers-encodings.git" | |
| 21 | }, | |
| 22 | "author": "Mathias Buus (@mafintosh)", | |
| 23 | "license": "MIT", | |
| 24 | "bugs": { | |
| 25 | "url": "https://github.com/mafintosh/protocol-buffers-encodings/issues" | |
| 26 | }, | |
| 27 | "homepage": "https://github.com/mafintosh/protocol-buffers-encodings" | |
| 28 | } |
| … | … | @@ -0,0 +1,151 @@ |
| 1 | var tape = require('tape') | |
| 2 | var encodings = require('./') | |
| 3 | ||
| 4 | tape('name', function (t) { | |
| 5 | t.same(encodings.name(encodings.varint), 'varint') | |
| 6 | t.same(encodings.name(encodings.float), 'float') | |
| 7 | t.end() | |
| 8 | }) | |
| 9 | ||
| 10 | tape('varint', function (t) { | |
| 11 | test(t, encodings.varint, [42, 10, 0, 999999]) | |
| 12 | }) | |
| 13 | ||
| 14 | tape('string', function (t) { | |
| 15 | test(t, encodings.string, ['a', 'abefest', 'øøø']) | |
| 16 | }) | |
| 17 | ||
| 18 | tape('bytes (node style)', function (t) { | |
| 19 | test( | |
| 20 | t, | |
| 21 | encodings.bytes, | |
| 22 | [Buffer.alloc(4096), Buffer.from('hi')], | |
| 23 | [allocBuffer] | |
| 24 | ) | |
| 25 | }) | |
| 26 | ||
| 27 | tape('bytes (browser style)', function (t) { | |
| 28 | test( | |
| 29 | t, | |
| 30 | encodings.bytes, | |
| 31 | [new Uint8Array(4096), new Uint8Array([104, 105])], | |
| 32 | [allocUint8Array] | |
| 33 | ) | |
| 34 | }) | |
| 35 | ||
| 36 | tape('bool', function (t) { | |
| 37 | test(t, encodings.bool, [true, false]) | |
| 38 | }) | |
| 39 | ||
| 40 | tape('int32', function (t) { | |
| 41 | test(t, encodings.int32, [-1, 0, 42, 4242424]) | |
| 42 | }) | |
| 43 | ||
| 44 | tape('int64', function (t) { | |
| 45 | test(t, encodings.int64, [-1, 0, 1, 24242424244]) | |
| 46 | }) | |
| 47 | ||
| 48 | tape('sint64', function (t) { | |
| 49 | test(t, encodings.sint64, [-14, 0, 144, 4203595524]) | |
| 50 | }) | |
| 51 | ||
| 52 | tape('uint64', function (t) { | |
| 53 | test(t, encodings.uint64, [1, 0, 144, 424444, 4203595524]) | |
| 54 | }) | |
| 55 | ||
| 56 | tape('fixed64 (node style)', function (t) { | |
| 57 | test( | |
| 58 | t, | |
| 59 | encodings.fixed64, | |
| 60 | [Buffer.from([0, 0, 0, 0, 0, 0, 0, 1])], | |
| 61 | [allocBuffer] | |
| 62 | ) | |
| 63 | }) | |
| 64 | ||
| 65 | tape('fixed64 (browser style)', function (t) { | |
| 66 | test( | |
| 67 | t, | |
| 68 | encodings.fixed64, | |
| 69 | [new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1])], | |
| 70 | [allocUint8Array] | |
| 71 | ) | |
| 72 | }) | |
| 73 | ||
| 74 | tape('double', function (t) { | |
| 75 | test(t, encodings.double, [0, 2, 0.5, 0.4]) | |
| 76 | }) | |
| 77 | ||
| 78 | tape('float', function (t) { | |
| 79 | test(t, encodings.float, [0, 2, 0.5]) | |
| 80 | }) | |
| 81 | ||
| 82 | tape('fixed32', function (t) { | |
| 83 | test(t, encodings.fixed32, [4, 0, 10000]) | |
| 84 | }) | |
| 85 | ||
| 86 | tape('sfixed32', function (t) { | |
| 87 | test(t, encodings.sfixed32, [-100, 4, 0, 142425]) | |
| 88 | }) | |
| 89 | ||
| 90 | function test (t, enc, vals, allocFunctions = [allocBuffer, allocUint8Array]) { | |
| 91 | if (!Array.isArray(vals)) vals = [vals] | |
| 92 | ||
| 93 | for (const allocFunction of allocFunctions) { | |
| 94 | for (const val of vals) { | |
| 95 | let buf = allocFunction(enc.encodingLength(val)) | |
| 96 | ||
| 97 | enc.encode(val, buf, 0) | |
| 98 | ||
| 99 | t.same(enc.encode.bytes, buf.length) | |
| 100 | t.same(enc.encodingLength(val), buf.length) | |
| 101 | t.same(enc.decode(buf, 0), val) | |
| 102 | t.same(enc.decode.bytes, buf.length) | |
| 103 | ||
| 104 | const anotherBuf = allocFunction(enc.encodingLength(val) + 1000) | |
| 105 | ||
| 106 | buf = enc.encode(val, anotherBuf, 10) | |
| 107 | t.same(buf, anotherBuf) | |
| 108 | t.ok(enc.encode.bytes < anotherBuf.length) | |
| 109 | t.same(enc.decode(buf, 10, 10 + enc.encodingLength(val)), val) | |
| 110 | t.ok(enc.decode.bytes < anotherBuf.length) | |
| 111 | } | |
| 112 | } | |
| 113 | ||
| 114 | t.end() | |
| 115 | } | |
| 116 | ||
| 117 | tape('test browser-style buffer', function (t) { | |
| 118 | const enc = encodings.string | |
| 119 | const val = 'value' | |
| 120 | let buf = new Uint8Array(enc.encodingLength(val)) | |
| 121 | enc.encode(val, buf, 0) | |
| 122 | ||
| 123 | // First elem is the length (5). Others are the encoded 'value' | |
| 124 | const expectedEncodedVal = new Uint8Array([5, 118, 97, 108, 117, 101]) | |
| 125 | t.same(buf, expectedEncodedVal) | |
| 126 | ||
| 127 | t.same(enc.encode.bytes, buf.length) | |
| 128 | t.same(enc.encodingLength(val), buf.length) | |
| 129 | t.same(enc.decode(buf, 0), val) | |
| 130 | t.same(enc.decode.bytes, buf.length) | |
| 131 | ||
| 132 | const anotherBuf = new Uint8Array(enc.encodingLength(val) + 1000) | |
| 133 | ||
| 134 | buf = enc.encode(val, anotherBuf, 10) | |
| 135 | t.same(buf, anotherBuf) | |
| 136 | t.ok(enc.encode.bytes < anotherBuf.length) | |
| 137 | t.same(enc.decode(buf, 10, 10 + enc.encodingLength(val)), val) | |
| 138 | t.ok(enc.decode.bytes < anotherBuf.length) | |
| 139 | ||
| 140 | t.end() | |
| 141 | }) | |
| 142 | ||
| 143 | function allocBuffer (length) { | |
| 144 | // Node style | |
| 145 | return Buffer.alloc(length) | |
| 146 | } | |
| 147 | ||
| 148 | function allocUint8Array (length) { | |
| 149 | // Browser style | |
| 150 | return new Uint8Array(length) | |
| 151 | } |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2021 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,183 @@ |
| 1 | # protomux | |
| 2 | ||
| 3 | Multiplex multiple message oriented protocols over a stream | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install protomux | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const Protomux = require('protomux') | |
| 13 | const c = require('compact-encoding') | |
| 14 | ||
| 15 | // By framed stream, it has be a stream that preserves the messages, ie something that length prefixes | |
| 16 | // like @hyperswarm/secret-stream | |
| 17 | ||
| 18 | const mux = new Protomux(aStreamThatFrames) | |
| 19 | ||
| 20 | // Now add some protocol channels | |
| 21 | ||
| 22 | const cool = mux.createChannel({ | |
| 23 | protocol: 'cool-protocol', | |
| 24 | id: Buffer.from('optional binary id'), | |
| 25 | onopen () { | |
| 26 | console.log('the other side opened this protocol!') | |
| 27 | }, | |
| 28 | onclose () { | |
| 29 | console.log('either side closed the protocol') | |
| 30 | } | |
| 31 | }) | |
| 32 | ||
| 33 | // And add some messages | |
| 34 | ||
| 35 | const one = cool.addMessage({ | |
| 36 | encoding: c.string, | |
| 37 | onmessage (m) { | |
| 38 | console.log('recv message (1)', m) | |
| 39 | } | |
| 40 | }) | |
| 41 | ||
| 42 | const two = cool.addMessage({ | |
| 43 | encoding: c.bool, | |
| 44 | onmessage (m) { | |
| 45 | console.log('recv message (2)', m) | |
| 46 | } | |
| 47 | }) | |
| 48 | ||
| 49 | // open the channel | |
| 50 | ||
| 51 | cool.open() | |
| 52 | ||
| 53 | // And send some data | |
| 54 | ||
| 55 | one.send('a string') | |
| 56 | two.send(true) | |
| 57 | ``` | |
| 58 | ||
| 59 | ## API | |
| 60 | ||
| 61 | #### `mux = new Protomux(stream, [options])` | |
| 62 | ||
| 63 | Make a new instance. `stream` should be a framed stream, preserving the messages written. | |
| 64 | ||
| 65 | Options include: | |
| 66 | ||
| 67 | ``` js | |
| 68 | { | |
| 69 | // Called when the muxer wants to allocate a message that is written, defaults to Buffer.allocUnsafe. | |
| 70 | alloc (size) {} | |
| 71 | } | |
| 72 | ``` | |
| 73 | ||
| 74 | #### `mux = Protomux.from(stream | muxer, [options])` | |
| 75 | ||
| 76 | Helper to accept either an existing muxer instance or a stream (which creates a new one). | |
| 77 | ||
| 78 | #### `const channel = mux.createChannel(opts)` | |
| 79 | ||
| 80 | Add a new protocol channel. | |
| 81 | ||
| 82 | Options include: | |
| 83 | ||
| 84 | ``` js | |
| 85 | { | |
| 86 | // Used to match the protocol | |
| 87 | protocol: 'name of the protocol', | |
| 88 | // Optional additional binary id to identify this channel | |
| 89 | id: buffer, | |
| 90 | // Optional encoding for a handshake | |
| 91 | handshake: encoding, | |
| 92 | // Optional array of messages types you want to send/receive. | |
| 93 | messages: [], | |
| 94 | // Called when the remote side adds this protocol. | |
| 95 | // Errors here are caught and forwared to stream.destroy | |
| 96 | async onopen (handshake) {}, | |
| 97 | // Called when the channel closes - ie the remote side closes or rejects this protocol or we closed it. | |
| 98 | // Errors here are caught and forwared to stream.destroy | |
| 99 | async onclose () {}, | |
| 100 | // Called after onclose when all pending promises has resolved. | |
| 101 | async ondestroy () {} | |
| 102 | } | |
| 103 | ``` | |
| 104 | ||
| 105 | Sessions are paired based on a queue, so the first remote channel with the same `protocol` and `id`. | |
| 106 | ||
| 107 | __NOTE__: `mux.createChannel` returns `null` if the channel should not be opened, ie it's a duplicate channel or the remote has already closed this one. | |
| 108 | ||
| 109 | If you want multiple sessions with the same `protocol` and `id`, set `unique: false` as an option. | |
| 110 | ||
| 111 | #### `const opened = mux.opened({ protocol, id })` | |
| 112 | ||
| 113 | Boolean that indicates if the channel is opened. | |
| 114 | ||
| 115 | #### `mux.pair({ protocol, id }, callback)` | |
| 116 | ||
| 117 | Register a callback to be called everytime a new channel is requested. | |
| 118 | ||
| 119 | #### `mux.unpair({ protocol, id })` | |
| 120 | ||
| 121 | Unregisters the pair callback. | |
| 122 | ||
| 123 | #### `channel.open([handshake])` | |
| 124 | ||
| 125 | Open the channel. | |
| 126 | ||
| 127 | #### `const m = channel.addMessage(opts)` | |
| 128 | ||
| 129 | Add/register a message type for a certain encoding. Options include: | |
| 130 | ||
| 131 | ``` js | |
| 132 | { | |
| 133 | // compact-encoding specifying how to encode/decode this message | |
| 134 | encoding: c.binary, | |
| 135 | // Called when the remote side sends a message. | |
| 136 | // Errors here are caught and forwared to stream.destroy | |
| 137 | async onmessage (message) { } | |
| 138 | } | |
| 139 | ``` | |
| 140 | ||
| 141 | #### `m.send(data)` | |
| 142 | ||
| 143 | Send a message. | |
| 144 | ||
| 145 | #### `m.onmessage` | |
| 146 | ||
| 147 | Function that is called when a message arrives. | |
| 148 | ||
| 149 | #### `m.encoding` | |
| 150 | ||
| 151 | The encoding for this message. | |
| 152 | ||
| 153 | #### `channel.close()` | |
| 154 | ||
| 155 | Closes the protocol channel. | |
| 156 | ||
| 157 | #### `channel.cork()` | |
| 158 | ||
| 159 | Corking the protocol channel, makes it buffer messages and send them all in a batch when it uncorks. | |
| 160 | ||
| 161 | #### `channel.uncork()` | |
| 162 | ||
| 163 | Uncork and send the batch. | |
| 164 | ||
| 165 | #### `mux.cork()` | |
| 166 | ||
| 167 | Same as `channel.cork` but on the muxer instance. | |
| 168 | ||
| 169 | #### `mux.uncork()` | |
| 170 | ||
| 171 | Same as `channel.uncork` but on the muxer instance. | |
| 172 | ||
| 173 | #### `for (const channel of muxer) { ... }` | |
| 174 | ||
| 175 | The muxer instance is iterable, so you can iterate over all the channels. | |
| 176 | ||
| 177 | #### `mux.isIdle()` | |
| 178 | ||
| 179 | Convenience method that returns true if the number of channels is currently 0. | |
| 180 | ||
| 181 | ## License | |
| 182 | ||
| 183 | MIT |
| … | … | @@ -0,0 +1,767 @@ |
| 1 | const b4a = require('b4a') | |
| 2 | const c = require('compact-encoding') | |
| 3 | const queueTick = require('queue-tick') | |
| 4 | const safetyCatch = require('safety-catch') | |
| 5 | const unslab = require('unslab') | |
| 6 | ||
| 7 | const MAX_BUFFERED = 32768 | |
| 8 | const MAX_BACKLOG = Infinity // TODO: impl "open" backpressure | |
| 9 | const MAX_BATCH = 8 * 1024 * 1024 | |
| 10 | ||
| 11 | class Channel { | |
| 12 | constructor (mux, info, userData, protocol, aliases, id, handshake, messages, onopen, onclose, ondestroy, ondrain) { | |
| 13 | this.userData = userData | |
| 14 | this.protocol = protocol | |
| 15 | this.aliases = aliases | |
| 16 | this.id = id | |
| 17 | this.handshake = null | |
| 18 | this.messages = [] | |
| 19 | ||
| 20 | this.opened = false | |
| 21 | this.closed = false | |
| 22 | this.destroyed = false | |
| 23 | ||
| 24 | this.onopen = onopen | |
| 25 | this.onclose = onclose | |
| 26 | this.ondestroy = ondestroy | |
| 27 | this.ondrain = ondrain | |
| 28 | ||
| 29 | this._handshake = handshake | |
| 30 | this._mux = mux | |
| 31 | this._info = info | |
| 32 | this._localId = 0 | |
| 33 | this._remoteId = 0 | |
| 34 | this._active = 0 | |
| 35 | this._extensions = null | |
| 36 | ||
| 37 | this._decBound = this._dec.bind(this) | |
| 38 | this._decAndDestroyBound = this._decAndDestroy.bind(this) | |
| 39 | ||
| 40 | this._openedPromise = null | |
| 41 | this._openedResolve = null | |
| 42 | ||
| 43 | this._destroyedPromise = null | |
| 44 | this._destroyedResolve = null | |
| 45 | ||
| 46 | for (const m of messages) this.addMessage(m) | |
| 47 | } | |
| 48 | ||
| 49 | get drained () { | |
| 50 | return this._mux.drained | |
| 51 | } | |
| 52 | ||
| 53 | fullyOpened () { | |
| 54 | if (this.opened) return Promise.resolve(true) | |
| 55 | if (this.closed) return Promise.resolve(false) | |
| 56 | if (this._openedPromise) return this._openedPromise | |
| 57 | ||
| 58 | this._openedPromise = new Promise((resolve) => { this._openedResolve = resolve }) | |
| 59 | return this._openedPromise | |
| 60 | } | |
| 61 | ||
| 62 | fullyClosed () { | |
| 63 | if (this.destroyed) return Promise.resolve() | |
| 64 | if (this._destroyedPromise) return this._destroyedPromise | |
| 65 | ||
| 66 | this._destroyedPromise = new Promise((resolve) => { this._destroyedResolve = resolve }) | |
| 67 | return this._destroyedPromise | |
| 68 | } | |
| 69 | ||
| 70 | open (handshake) { | |
| 71 | const id = this._mux._free.length > 0 | |
| 72 | ? this._mux._free.pop() | |
| 73 | : this._mux._local.push(null) - 1 | |
| 74 | ||
| 75 | this._info.opened++ | |
| 76 | this._info.lastChannel = this | |
| 77 | this._localId = id + 1 | |
| 78 | this._mux._local[id] = this | |
| 79 | ||
| 80 | if (this._remoteId === 0) { | |
| 81 | this._info.outgoing.push(this._localId) | |
| 82 | } | |
| 83 | ||
| 84 | const state = { buffer: null, start: 2, end: 2 } | |
| 85 | ||
| 86 | c.uint.preencode(state, this._localId) | |
| 87 | c.string.preencode(state, this.protocol) | |
| 88 | c.buffer.preencode(state, this.id) | |
| 89 | if (this._handshake) this._handshake.preencode(state, handshake) | |
| 90 | ||
| 91 | state.buffer = this._mux._alloc(state.end) | |
| 92 | ||
| 93 | state.buffer[0] = 0 | |
| 94 | state.buffer[1] = 1 | |
| 95 | c.uint.encode(state, this._localId) | |
| 96 | c.string.encode(state, this.protocol) | |
| 97 | c.buffer.encode(state, this.id) | |
| 98 | if (this._handshake) this._handshake.encode(state, handshake) | |
| 99 | ||
| 100 | this._mux._write0(state.buffer) | |
| 101 | } | |
| 102 | ||
| 103 | _dec () { | |
| 104 | if (--this._active === 0 && this.closed === true) this._destroy() | |
| 105 | } | |
| 106 | ||
| 107 | _decAndDestroy (err) { | |
| 108 | this._dec() | |
| 109 | this._mux._safeDestroy(err) | |
| 110 | } | |
| 111 | ||
| 112 | _fullyOpenSoon () { | |
| 113 | this._mux._remote[this._remoteId - 1].session = this | |
| 114 | queueTick(this._fullyOpen.bind(this)) | |
| 115 | } | |
| 116 | ||
| 117 | _fullyOpen () { | |
| 118 | if (this.opened === true || this.closed === true) return | |
| 119 | ||
| 120 | const remote = this._mux._remote[this._remoteId - 1] | |
| 121 | ||
| 122 | this.opened = true | |
| 123 | this.handshake = this._handshake ? this._handshake.decode(remote.state) : null | |
| 124 | this._track(this.onopen(this.handshake, this)) | |
| 125 | ||
| 126 | remote.session = this | |
| 127 | remote.state = null | |
| 128 | if (remote.pending !== null) this._drain(remote) | |
| 129 | ||
| 130 | this._resolveOpen(true) | |
| 131 | } | |
| 132 | ||
| 133 | _resolveOpen (opened) { | |
| 134 | if (this._openedResolve !== null) { | |
| 135 | this._openedResolve(opened) | |
| 136 | this._openedResolve = this._openedPromise = null | |
| 137 | } | |
| 138 | } | |
| 139 | ||
| 140 | _resolveDestroyed () { | |
| 141 | if (this._destroyedResolve !== null) { | |
| 142 | this._destroyedResolve() | |
| 143 | this._destroyedResolve = this._destroyedPromise = null | |
| 144 | } | |
| 145 | } | |
| 146 | ||
| 147 | _drain (remote) { | |
| 148 | for (let i = 0; i < remote.pending.length; i++) { | |
| 149 | const p = remote.pending[i] | |
| 150 | this._mux._buffered -= byteSize(p.state) | |
| 151 | this._recv(p.type, p.state) | |
| 152 | } | |
| 153 | ||
| 154 | remote.pending = null | |
| 155 | this._mux._resumeMaybe() | |
| 156 | } | |
| 157 | ||
| 158 | _track (p) { | |
| 159 | if (isPromise(p) === true) { | |
| 160 | this._active++ | |
| 161 | return p.then(this._decBound, this._decAndDestroyBound) | |
| 162 | } | |
| 163 | ||
| 164 | return null | |
| 165 | } | |
| 166 | ||
| 167 | _close (isRemote) { | |
| 168 | if (this.closed === true) return | |
| 169 | this.closed = true | |
| 170 | ||
| 171 | this._info.opened-- | |
| 172 | if (this._info.lastChannel === this) this._info.lastChannel = null | |
| 173 | ||
| 174 | if (this._remoteId > 0) { | |
| 175 | this._mux._remote[this._remoteId - 1] = null | |
| 176 | this._remoteId = 0 | |
| 177 | // If remote has acked, we can reuse the local id now | |
| 178 | // otherwise, we need to wait for the "ack" to arrive | |
| 179 | this._mux._free.push(this._localId - 1) | |
| 180 | } | |
| 181 | ||
| 182 | this._mux._local[this._localId - 1] = null | |
| 183 | this._localId = 0 | |
| 184 | ||
| 185 | this._mux._gc(this._info) | |
| 186 | this._track(this.onclose(isRemote, this)) | |
| 187 | ||
| 188 | if (this._active === 0) this._destroy() | |
| 189 | ||
| 190 | this._resolveOpen(false) | |
| 191 | } | |
| 192 | ||
| 193 | _destroy () { | |
| 194 | if (this.destroyed === true) return | |
| 195 | this.destroyed = true | |
| 196 | this._track(this.ondestroy(this)) | |
| 197 | this._resolveDestroyed() | |
| 198 | } | |
| 199 | ||
| 200 | _recv (type, state) { | |
| 201 | if (type < this.messages.length) { | |
| 202 | const m = this.messages[type] | |
| 203 | const p = m.recv(state, this) | |
| 204 | if (m.autoBatch === true) return p | |
| 205 | } | |
| 206 | return null | |
| 207 | } | |
| 208 | ||
| 209 | cork () { | |
| 210 | this._mux.cork() | |
| 211 | } | |
| 212 | ||
| 213 | uncork () { | |
| 214 | this._mux.uncork() | |
| 215 | } | |
| 216 | ||
| 217 | close () { | |
| 218 | if (this.closed === true) return | |
| 219 | ||
| 220 | const state = { buffer: null, start: 2, end: 2 } | |
| 221 | ||
| 222 | c.uint.preencode(state, this._localId) | |
| 223 | ||
| 224 | state.buffer = this._mux._alloc(state.end) | |
| 225 | ||
| 226 | state.buffer[0] = 0 | |
| 227 | state.buffer[1] = 3 | |
| 228 | c.uint.encode(state, this._localId) | |
| 229 | ||
| 230 | this._close(false) | |
| 231 | this._mux._write0(state.buffer) | |
| 232 | } | |
| 233 | ||
| 234 | addMessage (opts) { | |
| 235 | if (!opts) return this._skipMessage() | |
| 236 | ||
| 237 | const type = this.messages.length | |
| 238 | const autoBatch = opts.autoBatch !== false | |
| 239 | const encoding = opts.encoding || c.raw | |
| 240 | const onmessage = opts.onmessage || noop | |
| 241 | ||
| 242 | const s = this | |
| 243 | const typeLen = encodingLength(c.uint, type) | |
| 244 | ||
| 245 | const m = { | |
| 246 | type, | |
| 247 | autoBatch, | |
| 248 | encoding, | |
| 249 | onmessage, | |
| 250 | recv (state, session) { | |
| 251 | return session._track(m.onmessage(encoding.decode(state), session)) | |
| 252 | }, | |
| 253 | send (m, session = s) { | |
| 254 | if (session.closed === true) return false | |
| 255 | ||
| 256 | const mux = session._mux | |
| 257 | const state = { buffer: null, start: 0, end: typeLen } | |
| 258 | ||
| 259 | if (mux._batch !== null) { | |
| 260 | encoding.preencode(state, m) | |
| 261 | state.buffer = mux._alloc(state.end) | |
| 262 | ||
| 263 | c.uint.encode(state, type) | |
| 264 | encoding.encode(state, m) | |
| 265 | ||
| 266 | mux._pushBatch(session._localId, state.buffer) | |
| 267 | return true | |
| 268 | } | |
| 269 | ||
| 270 | c.uint.preencode(state, session._localId) | |
| 271 | encoding.preencode(state, m) | |
| 272 | ||
| 273 | state.buffer = mux._alloc(state.end) | |
| 274 | ||
| 275 | c.uint.encode(state, session._localId) | |
| 276 | c.uint.encode(state, type) | |
| 277 | encoding.encode(state, m) | |
| 278 | ||
| 279 | mux.drained = mux.stream.write(state.buffer) | |
| 280 | ||
| 281 | return mux.drained | |
| 282 | } | |
| 283 | } | |
| 284 | ||
| 285 | this.messages.push(m) | |
| 286 | ||
| 287 | return m | |
| 288 | } | |
| 289 | ||
| 290 | _skipMessage () { | |
| 291 | const type = this.messages.length | |
| 292 | const m = { | |
| 293 | type, | |
| 294 | encoding: c.raw, | |
| 295 | onmessage: noop, | |
| 296 | recv (state, session) {}, | |
| 297 | send (m, session) {} | |
| 298 | } | |
| 299 | ||
| 300 | this.messages.push(m) | |
| 301 | return m | |
| 302 | } | |
| 303 | } | |
| 304 | ||
| 305 | module.exports = class Protomux { | |
| 306 | constructor (stream, { alloc } = {}) { | |
| 307 | if (stream.userData === null) stream.userData = this | |
| 308 | ||
| 309 | this.isProtomux = true | |
| 310 | this.stream = stream | |
| 311 | this.corked = 0 | |
| 312 | this.drained = true | |
| 313 | ||
| 314 | this._alloc = alloc || (typeof stream.alloc === 'function' ? stream.alloc.bind(stream) : b4a.allocUnsafe) | |
| 315 | this._safeDestroyBound = this._safeDestroy.bind(this) | |
| 316 | this._uncorkBound = this.uncork.bind(this) | |
| 317 | ||
| 318 | this._remoteBacklog = 0 | |
| 319 | this._buffered = 0 | |
| 320 | this._paused = false | |
| 321 | this._remote = [] | |
| 322 | this._local = [] | |
| 323 | this._free = [] | |
| 324 | this._batch = null | |
| 325 | this._batchState = null | |
| 326 | ||
| 327 | this._infos = new Map() | |
| 328 | this._notify = new Map() | |
| 329 | ||
| 330 | this.stream.on('data', this._ondata.bind(this)) | |
| 331 | this.stream.on('drain', this._ondrain.bind(this)) | |
| 332 | this.stream.on('end', this._onend.bind(this)) | |
| 333 | this.stream.on('error', noop) // we handle this in "close" | |
| 334 | this.stream.on('close', this._shutdown.bind(this)) | |
| 335 | } | |
| 336 | ||
| 337 | static from (stream, opts) { | |
| 338 | if (stream.userData && stream.userData.isProtomux) return stream.userData | |
| 339 | if (stream.isProtomux) return stream | |
| 340 | return new this(stream, opts) | |
| 341 | } | |
| 342 | ||
| 343 | static isProtomux (mux) { | |
| 344 | return typeof mux === 'object' && mux.isProtomux === true | |
| 345 | } | |
| 346 | ||
| 347 | * [Symbol.iterator] () { | |
| 348 | for (const session of this._local) { | |
| 349 | if (session !== null) yield session | |
| 350 | } | |
| 351 | } | |
| 352 | ||
| 353 | isIdle () { | |
| 354 | return this._local.length === this._free.length | |
| 355 | } | |
| 356 | ||
| 357 | cork () { | |
| 358 | if (++this.corked === 1) { | |
| 359 | this._batch = [] | |
| 360 | this._batchState = { buffer: null, start: 0, end: 1 } | |
| 361 | } | |
| 362 | } | |
| 363 | ||
| 364 | uncork () { | |
| 365 | if (--this.corked === 0) { | |
| 366 | this._sendBatch(this._batch, this._batchState) | |
| 367 | this._batch = null | |
| 368 | this._batchState = null | |
| 369 | } | |
| 370 | } | |
| 371 | ||
| 372 | getLastChannel ({ protocol, id = null }) { | |
| 373 | const key = toKey(protocol, id) | |
| 374 | const info = this._infos.get(key) | |
| 375 | if (info) return info.lastChannel | |
| 376 | return null | |
| 377 | } | |
| 378 | ||
| 379 | pair ({ protocol, id = null }, notify) { | |
| 380 | this._notify.set(toKey(protocol, id), notify) | |
| 381 | } | |
| 382 | ||
| 383 | unpair ({ protocol, id = null }) { | |
| 384 | this._notify.delete(toKey(protocol, id)) | |
| 385 | } | |
| 386 | ||
| 387 | opened ({ protocol, id = null }) { | |
| 388 | const key = toKey(protocol, id) | |
| 389 | const info = this._infos.get(key) | |
| 390 | return info ? info.opened > 0 : false | |
| 391 | } | |
| 392 | ||
| 393 | createChannel ({ userData = null, protocol, aliases = [], id = null, unique = true, handshake = null, messages = [], onopen = noop, onclose = noop, ondestroy = noop, ondrain = noop }) { | |
| 394 | if (this.stream.destroyed) return null | |
| 395 | ||
| 396 | const info = this._get(protocol, id, aliases) | |
| 397 | if (unique && info.opened > 0) return null | |
| 398 | ||
| 399 | if (info.incoming.length === 0) { | |
| 400 | return new Channel(this, info, userData, protocol, aliases, id, handshake, messages, onopen, onclose, ondestroy, ondrain) | |
| 401 | } | |
| 402 | ||
| 403 | this._remoteBacklog-- | |
| 404 | ||
| 405 | const remoteId = info.incoming.shift() | |
| 406 | const r = this._remote[remoteId - 1] | |
| 407 | if (r === null) return null | |
| 408 | ||
| 409 | const session = new Channel(this, info, userData, protocol, aliases, id, handshake, messages, onopen, onclose, ondestroy, ondrain) | |
| 410 | ||
| 411 | session._remoteId = remoteId | |
| 412 | session._fullyOpenSoon() | |
| 413 | ||
| 414 | return session | |
| 415 | } | |
| 416 | ||
| 417 | _pushBatch (localId, buffer) { | |
| 418 | if (this._batchState.end >= MAX_BATCH) { | |
| 419 | this._sendBatch(this._batch, this._batchState) | |
| 420 | this._batch = [] | |
| 421 | this._batchState = { buffer: null, start: 0, end: 1 } | |
| 422 | } | |
| 423 | ||
| 424 | if (this._batch.length === 0 || this._batch[this._batch.length - 1].localId !== localId) { | |
| 425 | this._batchState.end++ | |
| 426 | c.uint.preencode(this._batchState, localId) | |
| 427 | } | |
| 428 | c.buffer.preencode(this._batchState, buffer) | |
| 429 | this._batch.push({ localId, buffer }) | |
| 430 | } | |
| 431 | ||
| 432 | _sendBatch (batch, state) { | |
| 433 | if (batch.length === 0) return | |
| 434 | ||
| 435 | let prev = batch[0].localId | |
| 436 | ||
| 437 | state.buffer = this._alloc(state.end) | |
| 438 | state.buffer[state.start++] = 0 | |
| 439 | state.buffer[state.start++] = 0 | |
| 440 | ||
| 441 | c.uint.encode(state, prev) | |
| 442 | ||
| 443 | for (let i = 0; i < batch.length; i++) { | |
| 444 | const b = batch[i] | |
| 445 | if (prev !== b.localId) { | |
| 446 | state.buffer[state.start++] = 0 | |
| 447 | c.uint.encode(state, (prev = b.localId)) | |
| 448 | } | |
| 449 | c.buffer.encode(state, b.buffer) | |
| 450 | } | |
| 451 | ||
| 452 | this.drained = this.stream.write(state.buffer) | |
| 453 | } | |
| 454 | ||
| 455 | _get (protocol, id, aliases = []) { | |
| 456 | const key = toKey(protocol, id) | |
| 457 | ||
| 458 | let info = this._infos.get(key) | |
| 459 | if (info) return info | |
| 460 | ||
| 461 | info = { key, protocol, aliases: [], id, pairing: 0, opened: 0, incoming: [], outgoing: [], lastChannel: null } | |
| 462 | this._infos.set(key, info) | |
| 463 | ||
| 464 | for (const alias of aliases) { | |
| 465 | const key = toKey(alias, id) | |
| 466 | info.aliases.push(key) | |
| 467 | ||
| 468 | this._infos.set(key, info) | |
| 469 | } | |
| 470 | ||
| 471 | return info | |
| 472 | } | |
| 473 | ||
| 474 | _gc (info) { | |
| 475 | if (info.opened === 0 && info.outgoing.length === 0 && info.incoming.length === 0) { | |
| 476 | this._infos.delete(info.key) | |
| 477 | ||
| 478 | for (const alias of info.aliases) this._infos.delete(alias) | |
| 479 | } | |
| 480 | } | |
| 481 | ||
| 482 | _ondata (buffer) { | |
| 483 | if (buffer.byteLength === 0) return // ignore empty frames... | |
| 484 | try { | |
| 485 | const state = { buffer, start: 0, end: buffer.byteLength } | |
| 486 | this._decode(c.uint.decode(state), state) | |
| 487 | } catch (err) { | |
| 488 | this._safeDestroy(err) | |
| 489 | } | |
| 490 | } | |
| 491 | ||
| 492 | _ondrain () { | |
| 493 | this.drained = true | |
| 494 | ||
| 495 | for (const s of this._local) { | |
| 496 | if (s !== null) s._track(s.ondrain(s)) | |
| 497 | } | |
| 498 | } | |
| 499 | ||
| 500 | _onend () { // TODO: support half open mode for the users who wants that here | |
| 501 | this.stream.end() | |
| 502 | } | |
| 503 | ||
| 504 | _decode (remoteId, state) { | |
| 505 | const type = c.uint.decode(state) | |
| 506 | ||
| 507 | if (remoteId === 0) { | |
| 508 | return this._oncontrolsession(type, state) | |
| 509 | } | |
| 510 | ||
| 511 | const r = remoteId <= this._remote.length ? this._remote[remoteId - 1] : null | |
| 512 | ||
| 513 | // if the channel is closed ignore - could just be a pipeline message... | |
| 514 | if (r === null) return null | |
| 515 | ||
| 516 | if (r.pending !== null) { | |
| 517 | this._bufferMessage(r, type, state) | |
| 518 | return null | |
| 519 | } | |
| 520 | ||
| 521 | return r.session._recv(type, state) | |
| 522 | } | |
| 523 | ||
| 524 | _oncontrolsession (type, state) { | |
| 525 | switch (type) { | |
| 526 | case 0: | |
| 527 | this._onbatch(state) | |
| 528 | break | |
| 529 | ||
| 530 | case 1: | |
| 531 | // return the promise back up as this has sideeffects so we can batch reply | |
| 532 | return this._onopensession(state) | |
| 533 | ||
| 534 | case 2: | |
| 535 | this._onrejectsession(state) | |
| 536 | break | |
| 537 | ||
| 538 | case 3: | |
| 539 | this._onclosesession(state) | |
| 540 | break | |
| 541 | } | |
| 542 | ||
| 543 | return null | |
| 544 | } | |
| 545 | ||
| 546 | _bufferMessage (r, type, { buffer, start, end }) { | |
| 547 | const state = { buffer, start, end } // copy | |
| 548 | r.pending.push({ type, state }) | |
| 549 | this._buffered += byteSize(state) | |
| 550 | this._pauseMaybe() | |
| 551 | } | |
| 552 | ||
| 553 | _pauseMaybe () { | |
| 554 | if (this._paused === true || this._buffered <= MAX_BUFFERED) return | |
| 555 | this._paused = true | |
| 556 | this.stream.pause() | |
| 557 | } | |
| 558 | ||
| 559 | _resumeMaybe () { | |
| 560 | if (this._paused === false || this._buffered > MAX_BUFFERED) return | |
| 561 | this._paused = false | |
| 562 | this.stream.resume() | |
| 563 | } | |
| 564 | ||
| 565 | _onbatch (state) { | |
| 566 | const end = state.end | |
| 567 | let remoteId = c.uint.decode(state) | |
| 568 | ||
| 569 | let waiting = null | |
| 570 | ||
| 571 | while (state.end > state.start) { | |
| 572 | const len = c.uint.decode(state) | |
| 573 | if (len === 0) { | |
| 574 | remoteId = c.uint.decode(state) | |
| 575 | continue | |
| 576 | } | |
| 577 | state.end = state.start + len | |
| 578 | // if batch contains more than one message, cork it so we reply back with a batch | |
| 579 | if (end !== state.end && waiting === null) { | |
| 580 | waiting = [] | |
| 581 | this.cork() | |
| 582 | } | |
| 583 | const p = this._decode(remoteId, state) | |
| 584 | if (waiting !== null && p !== null) waiting.push(p) | |
| 585 | state.start = state.end | |
| 586 | state.end = end | |
| 587 | } | |
| 588 | ||
| 589 | if (waiting !== null) { | |
| 590 | // the waiting promises are not allowed to throw but we destroy the stream in case we are wrong | |
| 591 | Promise.all(waiting).then(this._uncorkBound, this._safeDestroyBound) | |
| 592 | } | |
| 593 | } | |
| 594 | ||
| 595 | _onopensession (state) { | |
| 596 | const remoteId = c.uint.decode(state) | |
| 597 | const protocol = c.string.decode(state) | |
| 598 | const id = unslab(c.buffer.decode(state)) | |
| 599 | ||
| 600 | // remote tried to open the control session - auto reject for now | |
| 601 | // as we can use as an explicit control protocol declaration if we need to | |
| 602 | if (remoteId === 0) { | |
| 603 | this._rejectSession(0) | |
| 604 | return null | |
| 605 | } | |
| 606 | ||
| 607 | const rid = remoteId - 1 | |
| 608 | const info = this._get(protocol, id) | |
| 609 | ||
| 610 | // allow the remote to grow the ids by one | |
| 611 | if (this._remote.length === rid) { | |
| 612 | this._remote.push(null) | |
| 613 | } | |
| 614 | ||
| 615 | if (rid >= this._remote.length || this._remote[rid] !== null) { | |
| 616 | throw new Error('Invalid open message') | |
| 617 | } | |
| 618 | ||
| 619 | if (info.outgoing.length > 0) { | |
| 620 | const localId = info.outgoing.shift() | |
| 621 | const session = this._local[localId - 1] | |
| 622 | ||
| 623 | if (session === null) { // we already closed the channel - ignore | |
| 624 | this._free.push(localId - 1) | |
| 625 | return null | |
| 626 | } | |
| 627 | ||
| 628 | this._remote[rid] = { state, pending: null, session: null } | |
| 629 | ||
| 630 | session._remoteId = remoteId | |
| 631 | session._fullyOpen() | |
| 632 | return null | |
| 633 | } | |
| 634 | ||
| 635 | const copyState = { buffer: state.buffer, start: state.start, end: state.end } | |
| 636 | this._remote[rid] = { state: copyState, pending: [], session: null } | |
| 637 | ||
| 638 | if (++this._remoteBacklog > MAX_BACKLOG) { | |
| 639 | throw new Error('Remote exceeded backlog') | |
| 640 | } | |
| 641 | ||
| 642 | info.pairing++ | |
| 643 | info.incoming.push(remoteId) | |
| 644 | ||
| 645 | return this._requestSession(protocol, id, info).catch(this._safeDestroyBound) | |
| 646 | } | |
| 647 | ||
| 648 | _onrejectsession (state) { | |
| 649 | const localId = c.uint.decode(state) | |
| 650 | ||
| 651 | // TODO: can be done smarter... | |
| 652 | for (const info of this._infos.values()) { | |
| 653 | const i = info.outgoing.indexOf(localId) | |
| 654 | if (i === -1) continue | |
| 655 | ||
| 656 | info.outgoing.splice(i, 1) | |
| 657 | ||
| 658 | const session = this._local[localId - 1] | |
| 659 | ||
| 660 | this._free.push(localId - 1) | |
| 661 | if (session !== null) session._close(true) | |
| 662 | ||
| 663 | this._gc(info) | |
| 664 | return | |
| 665 | } | |
| 666 | ||
| 667 | throw new Error('Invalid reject message') | |
| 668 | } | |
| 669 | ||
| 670 | _onclosesession (state) { | |
| 671 | const remoteId = c.uint.decode(state) | |
| 672 | ||
| 673 | if (remoteId === 0) return // ignore | |
| 674 | ||
| 675 | const rid = remoteId - 1 | |
| 676 | const r = rid < this._remote.length ? this._remote[rid] : null | |
| 677 | ||
| 678 | if (r === null) return | |
| 679 | ||
| 680 | if (r.session !== null) r.session._close(true) | |
| 681 | } | |
| 682 | ||
| 683 | async _requestSession (protocol, id, info) { | |
| 684 | const notify = this._notify.get(toKey(protocol, id)) || this._notify.get(toKey(protocol, null)) | |
| 685 | ||
| 686 | if (notify) await notify(id) | |
| 687 | ||
| 688 | if (--info.pairing > 0) return | |
| 689 | ||
| 690 | while (info.incoming.length > 0) { | |
| 691 | this._rejectSession(info, info.incoming.shift()) | |
| 692 | } | |
| 693 | ||
| 694 | this._gc(info) | |
| 695 | } | |
| 696 | ||
| 697 | _rejectSession (info, remoteId) { | |
| 698 | if (remoteId > 0) { | |
| 699 | const r = this._remote[remoteId - 1] | |
| 700 | ||
| 701 | if (r.pending !== null) { | |
| 702 | for (let i = 0; i < r.pending.length; i++) { | |
| 703 | this._buffered -= byteSize(r.pending[i].state) | |
| 704 | } | |
| 705 | } | |
| 706 | ||
| 707 | this._remote[remoteId - 1] = null | |
| 708 | this._resumeMaybe() | |
| 709 | } | |
| 710 | ||
| 711 | const state = { buffer: null, start: 2, end: 2 } | |
| 712 | ||
| 713 | c.uint.preencode(state, remoteId) | |
| 714 | ||
| 715 | state.buffer = this._alloc(state.end) | |
| 716 | ||
| 717 | state.buffer[0] = 0 | |
| 718 | state.buffer[1] = 2 | |
| 719 | c.uint.encode(state, remoteId) | |
| 720 | ||
| 721 | this._write0(state.buffer) | |
| 722 | } | |
| 723 | ||
| 724 | _write0 (buffer) { | |
| 725 | if (this._batch !== null) { | |
| 726 | this._pushBatch(0, buffer.subarray(1)) | |
| 727 | return | |
| 728 | } | |
| 729 | ||
| 730 | this.drained = this.stream.write(buffer) | |
| 731 | } | |
| 732 | ||
| 733 | destroy (err) { | |
| 734 | this.stream.destroy(err) | |
| 735 | } | |
| 736 | ||
| 737 | _safeDestroy (err) { | |
| 738 | safetyCatch(err) | |
| 739 | this.stream.destroy(err) | |
| 740 | } | |
| 741 | ||
| 742 | _shutdown () { | |
| 743 | for (const s of this._local) { | |
| 744 | if (s !== null) s._close(true) | |
| 745 | } | |
| 746 | } | |
| 747 | } | |
| 748 | ||
| 749 | function noop () {} | |
| 750 | ||
| 751 | function toKey (protocol, id) { | |
| 752 | return protocol + '##' + (id ? b4a.toString(id, 'hex') : '') | |
| 753 | } | |
| 754 | ||
| 755 | function byteSize (state) { | |
| 756 | return 512 + (state.end - state.start) | |
| 757 | } | |
| 758 | ||
| 759 | function isPromise (p) { | |
| 760 | return !!(p && typeof p.then === 'function') | |
| 761 | } | |
| 762 | ||
| 763 | function encodingLength (enc, val) { | |
| 764 | const state = { buffer: null, start: 0, end: 0 } | |
| 765 | enc.preencode(state, val) | |
| 766 | return state.end | |
| 767 | } |
| … | … | @@ -0,0 +1,34 @@ |
| 1 | { | |
| 2 | "name": "protomux", | |
| 3 | "version": "3.10.1", | |
| 4 | "description": "Multiplex multiple message oriented protocols over a stream", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js" | |
| 8 | ], | |
| 9 | "dependencies": { | |
| 10 | "b4a": "^1.3.1", | |
| 11 | "compact-encoding": "^2.5.1", | |
| 12 | "queue-tick": "^1.0.0", | |
| 13 | "safety-catch": "^1.0.1", | |
| 14 | "unslab": "^1.3.0" | |
| 15 | }, | |
| 16 | "devDependencies": { | |
| 17 | "@hyperswarm/secret-stream": "^6.0.0", | |
| 18 | "brittle": "^3.0.0", | |
| 19 | "standard": "^16.0.4" | |
| 20 | }, | |
| 21 | "scripts": { | |
| 22 | "test": "standard && brittle test.js" | |
| 23 | }, | |
| 24 | "repository": { | |
| 25 | "type": "git", | |
| 26 | "url": "https://github.com/mafintosh/protomux.git" | |
| 27 | }, | |
| 28 | "author": "Mathias Buus (@mafintosh)", | |
| 29 | "license": "MIT", | |
| 30 | "bugs": { | |
| 31 | "url": "https://github.com/mafintosh/protomux/issues" | |
| 32 | }, | |
| 33 | "homepage": "https://github.com/mafintosh/protomux" | |
| 34 | } |
| … | … | @@ -0,0 +1,24 @@ |
| 1 | name: Build Status | |
| 2 | ||
| 3 | on: | |
| 4 | push: | |
| 5 | branches: | |
| 6 | - master | |
| 7 | pull_request: | |
| 8 | branches: | |
| 9 | - master | |
| 10 | jobs: | |
| 11 | build: | |
| 12 | strategy: | |
| 13 | matrix: | |
| 14 | node-version: [14.x] | |
| 15 | os: [ubuntu-16.04, macos-latest, windows-latest] | |
| 16 | runs-on: ${{ matrix.os }} | |
| 17 | steps: | |
| 18 | - uses: actions/checkout@v2 | |
| 19 | - name: Use Node.js ${{ matrix.node-version }} | |
| 20 | uses: actions/setup-node@v1 | |
| 21 | with: | |
| 22 | node-version: ${{ matrix.node-version }} | |
| 23 | - run: npm install | |
| 24 | - run: npm test |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2021 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,20 @@ |
| 1 | # queue-tick | |
| 2 | ||
| 3 | Next tick shim that prefers process.nextTick over queueMicrotask for compat | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install queue-tick | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const queueTick = require('queue-tick') | |
| 13 | ||
| 14 | // in Node it uses process.nextTick, in browsers it uses queueMicrotask | |
| 15 | queueTick(() => console.log('laters')) | |
| 16 | ``` | |
| 17 | ||
| 18 | ## License | |
| 19 | ||
| 20 | MIT |
| … | … | @@ -0,0 +1,25 @@ |
| 1 | { | |
| 2 | "name": "queue-tick", | |
| 3 | "version": "1.0.1", | |
| 4 | "description": "Next tick shim that prefers process.nextTick over queueMicrotask for compat", | |
| 5 | "main": "./process-next-tick.js", | |
| 6 | "browser": "./queue-microtask.js", | |
| 7 | "dependencies": {}, | |
| 8 | "devDependencies": { | |
| 9 | "standard": "^16.0.3", | |
| 10 | "tape": "^5.3.1" | |
| 11 | }, | |
| 12 | "scripts": { | |
| 13 | "test": "standard && tape test.js" | |
| 14 | }, | |
| 15 | "repository": { | |
| 16 | "type": "git", | |
| 17 | "url": "https://github.com/mafintosh/queue-tick.git" | |
| 18 | }, | |
| 19 | "author": "Mathias Buus (@mafintosh)", | |
| 20 | "license": "MIT", | |
| 21 | "bugs": { | |
| 22 | "url": "https://github.com/mafintosh/queue-tick/issues" | |
| 23 | }, | |
| 24 | "homepage": "https://github.com/mafintosh/queue-tick" | |
| 25 | } |
| … | … | @@ -0,0 +1,3 @@ |
| 1 | module.exports = (typeof process !== 'undefined' && typeof process.nextTick === 'function') | |
| 2 | ? process.nextTick.bind(process) | |
| 3 | : require('./queue-microtask') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = typeof queueMicrotask === 'function' ? queueMicrotask : (fn) => Promise.resolve().then(fn) |
| … | … | @@ -0,0 +1,10 @@ |
| 1 | const tape = require('tape') | |
| 2 | const queueTick = require('./') | |
| 3 | const js = require('./queue-microtask') | |
| 4 | ||
| 5 | tape('basic', function (t) { | |
| 6 | t.plan(2) | |
| 7 | ||
| 8 | queueTick(() => t.pass('tick')) | |
| 9 | js(() => t.pass('tock')) | |
| 10 | }) |
| … | … | @@ -0,0 +1,70 @@ |
| 1 | cmake_minimum_required(VERSION 3.31) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) | |
| 5 | find_package(cmake-napi REQUIRED PATHS node_modules/cmake-napi) | |
| 6 | ||
| 7 | project(quickbit_native C CXX) | |
| 8 | ||
| 9 | bare_target(target) | |
| 10 | ||
| 11 | if(target MATCHES "win32") | |
| 12 | add_compile_options(/MT$<$<CONFIG:Debug>:d>) | |
| 13 | endif() | |
| 14 | ||
| 15 | fetch_package("github:holepunchto/libquickbit#5ecf908") | |
| 16 | fetch_package("github:holepunchto/libjstl#d08e05c") | |
| 17 | ||
| 18 | add_bare_module(quickbit_native_bare) | |
| 19 | ||
| 20 | target_sources( | |
| 21 | ${quickbit_native_bare} | |
| 22 | PRIVATE | |
| 23 | binding.cc | |
| 24 | ) | |
| 25 | ||
| 26 | target_link_libraries( | |
| 27 | ${quickbit_native_bare} | |
| 28 | PRIVATE | |
| 29 | $<TARGET_OBJECTS:quickbit> | |
| 30 | $<TARGET_OBJECTS:simdle> | |
| 31 | PUBLIC | |
| 32 | quickbit | |
| 33 | simdle | |
| 34 | PRIVATE | |
| 35 | jstl | |
| 36 | ) | |
| 37 | ||
| 38 | add_napi_module(quickbit_native_node) | |
| 39 | ||
| 40 | target_sources( | |
| 41 | ${quickbit_native_node} | |
| 42 | PRIVATE | |
| 43 | binding.cc | |
| 44 | ) | |
| 45 | ||
| 46 | target_compile_definitions( | |
| 47 | ${quickbit_native_node} | |
| 48 | PRIVATE | |
| 49 | NAPI_VERSION=9 | |
| 50 | ) | |
| 51 | ||
| 52 | target_link_libraries( | |
| 53 | ${quickbit_native_node} | |
| 54 | PRIVATE | |
| 55 | $<TARGET_OBJECTS:quickbit> | |
| 56 | $<TARGET_OBJECTS:simdle> | |
| 57 | PUBLIC | |
| 58 | quickbit | |
| 59 | simdle | |
| 60 | PRIVATE | |
| 61 | jstl | |
| 62 | ) | |
| 63 | ||
| 64 | resolve_node_module(bare-compat-napi compat) | |
| 65 | ||
| 66 | target_include_directories( | |
| 67 | ${quickbit_native_node} | |
| 68 | PRIVATE | |
| 69 | "${compat}/include" | |
| 70 | ) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,72 @@ |
| 1 | # quickbit-native | |
| 2 | ||
| 3 | https://github.com/holepunchto/libquickbit JavaScript bindings for Node.js. | |
| 4 | ||
| 5 | ```sh | |
| 6 | npm install quickbit-native | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const { get, set, findFirst } = require('quickbit-native') | |
| 13 | ||
| 14 | const field = Buffer.alloc(256) // 2048 bits | |
| 15 | ||
| 16 | const changed = set(field, 1000) | |
| 17 | // true | |
| 18 | ||
| 19 | get(field, 1000) | |
| 20 | // true | |
| 21 | ||
| 22 | findFirst(field, true) | |
| 23 | // 1000 | |
| 24 | ``` | |
| 25 | ||
| 26 | ## API | |
| 27 | ||
| 28 | #### `const b = get(field, bit)` | |
| 29 | ||
| 30 | Get the given bit, which will either be `true` (set) or `false` (unset). | |
| 31 | ||
| 32 | #### `const changed = set(field, bit[, value])` | |
| 33 | ||
| 34 | Set the given bit to `value`, which defaults to `true`. Returns `true` if the bit changed, otherwise `false`. | |
| 35 | ||
| 36 | #### `field = fill(field, value[, start[, end]])` | |
| 37 | ||
| 38 | Fill the given bit range with `value`. `start` defaults to `0` and `end` defaults to the bit length of the field. Returns the modified field. | |
| 39 | ||
| 40 | #### `clear(field, chunk)` | |
| 41 | ||
| 42 | Clear the bits from `field` that are set in the `{ field, offset }` chunk. | |
| 43 | ||
| 44 | #### `const i = findFirst(field, value[, position])` | |
| 45 | ||
| 46 | Return the index of the first occurrence of `value`, or `-1` if not found. If `position` is given, return the first index that is greater than or equal to `position`. | |
| 47 | ||
| 48 | #### `const i = findLast(field, value[, position])` | |
| 49 | ||
| 50 | Return the index of the last occurrence of `value`, or `-1` if not found. If `position` is given, return the last index that is less than or equal to `position`. | |
| 51 | ||
| 52 | ### Indexing | |
| 53 | ||
| 54 | #### `const index = Index.from(fieldOrChunks)` | |
| 55 | ||
| 56 | Construct an index of the bits in a bit field or an array of sparse `{ field, offset }` chunks. | |
| 57 | ||
| 58 | #### `const changed = index.update(bit)` | |
| 59 | ||
| 60 | Update the given bit in the index. | |
| 61 | ||
| 62 | #### `const position = index.skipFirst(value[, position])` | |
| 63 | ||
| 64 | Skip the first parts of the index that contain nothing but `value`. If the index contains _only_ `value`, the last index of the field is returned. | |
| 65 | ||
| 66 | #### `const position = index.skipLast(value[, position])` | |
| 67 | ||
| 68 | Skip the last parts of the index that contain nothing but `value`. If the index contains _only_ `value`, the first index of the field is returned. | |
| 69 | ||
| 70 | ## License | |
| 71 | ||
| 72 | Apache-2.0 |
| … | … | @@ -0,0 +1,221 @@ |
| 1 | #include <bare.h> | |
| 2 | #include <js.h> | |
| 3 | #include <jstl.h> | |
| 4 | #include <quickbit.h> | |
| 5 | ||
| 6 | template <> | |
| 7 | struct js_type_info_t<quickbit_chunk_t> { | |
| 8 | using type = js_value_t *; | |
| 9 | ||
| 10 | static constexpr auto signature = js_object; | |
| 11 | ||
| 12 | template <js_type_options_t options> | |
| 13 | static auto | |
| 14 | unmarshall(js_env_t *env, js_value_t *value, quickbit_chunk_t &chunk) { | |
| 15 | int err; | |
| 16 | ||
| 17 | js_typedarray_span_t<uint8_t> field; | |
| 18 | err = js_get_property(env, value, "field", field); | |
| 19 | if (err < 0) return err; | |
| 20 | ||
| 21 | uint32_t offset; | |
| 22 | err = js_get_property(env, value, "offset", offset); | |
| 23 | if (err < 0) return err; | |
| 24 | ||
| 25 | chunk = { | |
| 26 | .field = field.data(), | |
| 27 | .len = field.size(), | |
| 28 | .offset = offset, | |
| 29 | }; | |
| 30 | ||
| 31 | return 0; | |
| 32 | } | |
| 33 | }; | |
| 34 | ||
| 35 | static inline uint32_t | |
| 36 | quickbit_napi_get( | |
| 37 | js_env_t *env, | |
| 38 | js_receiver_t, | |
| 39 | js_typedarray_span_t<uint8_t> field, | |
| 40 | int32_t bit | |
| 41 | ) { | |
| 42 | return quickbit_get(field.data(), field.size(), bit); | |
| 43 | } | |
| 44 | ||
| 45 | static inline uint32_t | |
| 46 | quickbit_napi_set( | |
| 47 | js_env_t *env, | |
| 48 | js_receiver_t, | |
| 49 | js_typedarray_span_t<uint8_t> field, | |
| 50 | int32_t bit, | |
| 51 | uint32_t value | |
| 52 | ) { | |
| 53 | return quickbit_set(field.data(), field.size(), bit, value); | |
| 54 | } | |
| 55 | ||
| 56 | static inline void | |
| 57 | quickbit_napi_fill( | |
| 58 | js_env_t *env, | |
| 59 | js_receiver_t, | |
| 60 | js_typedarray_span_t<uint8_t> field, | |
| 61 | uint32_t value, | |
| 62 | int32_t start, | |
| 63 | int32_t end | |
| 64 | ) { | |
| 65 | quickbit_fill(field.data(), field.size(), value, start, end); | |
| 66 | } | |
| 67 | ||
| 68 | static inline void | |
| 69 | quickbit_napi_clear( | |
| 70 | js_env_t *env, | |
| 71 | js_receiver_t, | |
| 72 | js_typedarray_span_t<uint8_t> field, | |
| 73 | std::vector<quickbit_chunk_t> chunks | |
| 74 | ) { | |
| 75 | for (auto &chunk : chunks) { | |
| 76 | quickbit_clear(field.data(), field.size(), &chunk); | |
| 77 | } | |
| 78 | } | |
| 79 | ||
| 80 | static inline int32_t | |
| 81 | quickbit_napi_find_first( | |
| 82 | js_env_t *env, | |
| 83 | js_receiver_t, | |
| 84 | js_typedarray_span_t<uint8_t> field, | |
| 85 | uint32_t value, | |
| 86 | int32_t position | |
| 87 | ) { | |
| 88 | return quickbit_find_first(field.data(), field.size(), value, position); | |
| 89 | } | |
| 90 | ||
| 91 | static inline int32_t | |
| 92 | quickbit_napi_find_last( | |
| 93 | js_env_t *env, | |
| 94 | js_receiver_t, | |
| 95 | js_typedarray_span_t<uint8_t> field, | |
| 96 | uint32_t value, | |
| 97 | int32_t position | |
| 98 | ) { | |
| 99 | return quickbit_find_last(field.data(), field.size(), value, position); | |
| 100 | } | |
| 101 | ||
| 102 | static inline void | |
| 103 | quickbit_napi_index_init( | |
| 104 | js_env_t *env, | |
| 105 | js_receiver_t, | |
| 106 | js_typedarray_span_t<uint8_t> index, | |
| 107 | js_typedarray_span_t<uint8_t> field | |
| 108 | ) { | |
| 109 | quickbit_index_init(index.data(), field.data(), field.size()); | |
| 110 | } | |
| 111 | ||
| 112 | static inline void | |
| 113 | quickbit_napi_index_init_sparse( | |
| 114 | js_env_t *env, | |
| 115 | js_receiver_t, | |
| 116 | js_typedarray_span_t<uint8_t> index, | |
| 117 | std::vector<quickbit_chunk_t> chunks | |
| 118 | ) { | |
| 119 | quickbit_index_init_sparse(index.data(), chunks.data(), chunks.size()); | |
| 120 | } | |
| 121 | ||
| 122 | static inline uint32_t | |
| 123 | quickbit_napi_index_update( | |
| 124 | js_env_t *env, | |
| 125 | js_receiver_t, | |
| 126 | js_typedarray_span_t<uint8_t> index, | |
| 127 | js_typedarray_span_t<uint8_t> field, | |
| 128 | int32_t bit | |
| 129 | ) { | |
| 130 | return quickbit_index_update(index.data(), field.data(), field.size(), bit); | |
| 131 | } | |
| 132 | ||
| 133 | static inline uint32_t | |
| 134 | quickbit_napi_index_update_sparse( | |
| 135 | js_env_t *env, | |
| 136 | js_receiver_t, | |
| 137 | js_typedarray_span_t<uint8_t> index, | |
| 138 | js_typedarray_span_t<uint8_t> field, | |
| 139 | uint32_t offset, | |
| 140 | int32_t bit | |
| 141 | ) { | |
| 142 | quickbit_chunk_t chunk = { | |
| 143 | .field = field.data(), | |
| 144 | .len = field.size(), | |
| 145 | .offset = offset, | |
| 146 | }; | |
| 147 | ||
| 148 | return quickbit_index_update_sparse(index.data(), &chunk, 1, bit); | |
| 149 | } | |
| 150 | ||
| 151 | static inline uint32_t | |
| 152 | quickbit_napi_skip_first( | |
| 153 | js_env_t *env, | |
| 154 | js_receiver_t, | |
| 155 | js_typedarray_span_t<uint8_t> index, | |
| 156 | uint32_t len, | |
| 157 | uint32_t value, | |
| 158 | int32_t position | |
| 159 | ) { | |
| 160 | return quickbit_skip_first(index.data(), len, value, position); | |
| 161 | } | |
| 162 | ||
| 163 | static inline uint32_t | |
| 164 | quickbit_napi_skip_last( | |
| 165 | js_env_t *env, | |
| 166 | js_receiver_t, | |
| 167 | js_typedarray_span_t<uint8_t> index, | |
| 168 | uint32_t len, | |
| 169 | uint32_t value, | |
| 170 | int32_t position | |
| 171 | ) { | |
| 172 | return quickbit_skip_last(index.data(), len, value, position); | |
| 173 | } | |
| 174 | ||
| 175 | static js_value_t * | |
| 176 | quickbit_napi_exports(js_env_t *env, js_value_t *exports) { | |
| 177 | int err; | |
| 178 | ||
| 179 | err = js_set_property(env, exports, "sizeof_quickbit_index_t", uint32_t(sizeof(quickbit_index_t))); | |
| 180 | assert(err == 0); | |
| 181 | ||
| 182 | err = js_set_property<quickbit_napi_get>(env, exports, "quickbit_napi_get"); | |
| 183 | assert(err == 0); | |
| 184 | ||
| 185 | err = js_set_property<quickbit_napi_set>(env, exports, "quickbit_napi_set"); | |
| 186 | assert(err == 0); | |
| 187 | ||
| 188 | err = js_set_property<quickbit_napi_fill>(env, exports, "quickbit_napi_fill"); | |
| 189 | assert(err == 0); | |
| 190 | ||
| 191 | err = js_set_property<quickbit_napi_clear>(env, exports, "quickbit_napi_clear"); | |
| 192 | assert(err == 0); | |
| 193 | ||
| 194 | err = js_set_property<quickbit_napi_find_first>(env, exports, "quickbit_napi_find_first"); | |
| 195 | assert(err == 0); | |
| 196 | ||
| 197 | err = js_set_property<quickbit_napi_find_last>(env, exports, "quickbit_napi_find_last"); | |
| 198 | assert(err == 0); | |
| 199 | ||
| 200 | err = js_set_property<quickbit_napi_index_init>(env, exports, "quickbit_napi_index_init"); | |
| 201 | assert(err == 0); | |
| 202 | ||
| 203 | err = js_set_property<quickbit_napi_index_init_sparse>(env, exports, "quickbit_napi_index_init_sparse"); | |
| 204 | assert(err == 0); | |
| 205 | ||
| 206 | err = js_set_property<quickbit_napi_index_update>(env, exports, "quickbit_napi_index_update"); | |
| 207 | assert(err == 0); | |
| 208 | ||
| 209 | err = js_set_property<quickbit_napi_index_update_sparse>(env, exports, "quickbit_napi_index_update_sparse"); | |
| 210 | assert(err == 0); | |
| 211 | ||
| 212 | err = js_set_property<quickbit_napi_skip_first>(env, exports, "quickbit_napi_skip_first"); | |
| 213 | assert(err == 0); | |
| 214 | ||
| 215 | err = js_set_property<quickbit_napi_skip_last>(env, exports, "quickbit_napi_skip_last"); | |
| 216 | assert(err == 0); | |
| 217 | ||
| 218 | return exports; | |
| 219 | } | |
| 220 | ||
| 221 | BARE_MODULE(quickbit_napi, quickbit_napi_exports) |
| … | … | @@ -0,0 +1,3 @@ |
| 1 | require.addon = require('require-addon') | |
| 2 | ||
| 3 | module.exports = require.addon('.', __filename) |
| … | … | @@ -0,0 +1,217 @@ |
| 1 | const binding = require('./binding') | |
| 2 | ||
| 3 | exports.get = function get(field, bit) { | |
| 4 | const n = field.byteLength * 8 | |
| 5 | ||
| 6 | if (bit < 0) bit += n | |
| 7 | if (bit < 0 || bit >= n) return false | |
| 8 | ||
| 9 | return binding.quickbit_napi_get(toBuffer(field), bit) !== 0 | |
| 10 | } | |
| 11 | ||
| 12 | exports.set = function set(field, bit, value = true) { | |
| 13 | const n = field.byteLength * 8 | |
| 14 | ||
| 15 | if (bit < 0) bit += n | |
| 16 | if (bit < 0 || bit >= n) return false | |
| 17 | ||
| 18 | return binding.quickbit_napi_set(toBuffer(field), bit, value ? 1 : 0) !== 0 | |
| 19 | } | |
| 20 | ||
| 21 | exports.fill = function fill( | |
| 22 | field, | |
| 23 | value, | |
| 24 | start = 0, | |
| 25 | end = field.byteLength * 8 | |
| 26 | ) { | |
| 27 | const n = field.byteLength * 8 | |
| 28 | ||
| 29 | if (start < 0) start += n | |
| 30 | if (end < 0) end += n | |
| 31 | if (start < 0 || start >= field.byteLength * 8 || start >= end) return field | |
| 32 | ||
| 33 | binding.quickbit_napi_fill(toBuffer(field), value ? 1 : 0, start, end) | |
| 34 | return field | |
| 35 | } | |
| 36 | ||
| 37 | exports.clear = function clear(field, ...chunks) { | |
| 38 | binding.quickbit_napi_clear(toBuffer(field), chunks.map(toBufferChunk)) | |
| 39 | } | |
| 40 | ||
| 41 | exports.findFirst = function findFirst(field, value, position = 0) { | |
| 42 | const n = field.byteLength * 8 | |
| 43 | ||
| 44 | if (position < 0) position += n | |
| 45 | if (position < 0) position = 0 | |
| 46 | if (position >= n) return -1 | |
| 47 | ||
| 48 | return binding.quickbit_napi_find_first( | |
| 49 | toBuffer(field), | |
| 50 | value ? 1 : 0, | |
| 51 | position | |
| 52 | ) | |
| 53 | } | |
| 54 | ||
| 55 | exports.findLast = function findLast( | |
| 56 | field, | |
| 57 | value, | |
| 58 | position = field.byteLength * 8 - 1 | |
| 59 | ) { | |
| 60 | const n = field.byteLength * 8 | |
| 61 | ||
| 62 | if (position < 0) position += n | |
| 63 | if (position < 0) return -1 | |
| 64 | if (position >= n) position = n - 1 | |
| 65 | ||
| 66 | return binding.quickbit_napi_find_last( | |
| 67 | toBuffer(field), | |
| 68 | value ? 1 : 0, | |
| 69 | position | |
| 70 | ) | |
| 71 | } | |
| 72 | ||
| 73 | function toBuffer(field) { | |
| 74 | if (field.BYTES_PER_ELEMENT === 1) return field | |
| 75 | return new Uint8Array(field.buffer, field.byteOffset, field.byteLength) | |
| 76 | } | |
| 77 | ||
| 78 | function toBufferChunk(chunk) { | |
| 79 | return { field: toBuffer(chunk.field), offset: chunk.offset } | |
| 80 | } | |
| 81 | ||
| 82 | class Index { | |
| 83 | static from(fieldOrChunks, byteLength = -1) { | |
| 84 | if (Array.isArray(fieldOrChunks)) { | |
| 85 | return new SparseIndex(fieldOrChunks, byteLength) | |
| 86 | } else { | |
| 87 | return new DenseIndex(fieldOrChunks, byteLength) | |
| 88 | } | |
| 89 | } | |
| 90 | ||
| 91 | constructor(byteLength) { | |
| 92 | this._byteLength = byteLength | |
| 93 | this.handle = Buffer.allocUnsafe(binding.sizeof_quickbit_index_t) | |
| 94 | } | |
| 95 | ||
| 96 | get byteLength() { | |
| 97 | return this._byteLength | |
| 98 | } | |
| 99 | ||
| 100 | skipFirst(value, position = 0) { | |
| 101 | const n = this.byteLength * 8 | |
| 102 | ||
| 103 | if (position < 0) position += n | |
| 104 | if (position < 0) position = 0 | |
| 105 | if (position >= n) return n - 1 | |
| 106 | ||
| 107 | return binding.quickbit_napi_skip_first( | |
| 108 | this.handle, | |
| 109 | this.byteLength, | |
| 110 | value ? 1 : 0, | |
| 111 | position | |
| 112 | ) | |
| 113 | } | |
| 114 | ||
| 115 | skipLast(value, position = this.byteLength * 8 - 1) { | |
| 116 | const n = this.byteLength * 8 | |
| 117 | ||
| 118 | if (position < 0) position += n | |
| 119 | if (position < 0) return 0 | |
| 120 | if (position >= n) position = n - 1 | |
| 121 | ||
| 122 | return binding.quickbit_napi_skip_last( | |
| 123 | this.handle, | |
| 124 | this.byteLength, | |
| 125 | value ? 1 : 0, | |
| 126 | position | |
| 127 | ) | |
| 128 | } | |
| 129 | } | |
| 130 | ||
| 131 | exports.Index = Index | |
| 132 | ||
| 133 | class DenseIndex extends Index { | |
| 134 | constructor(field, byteLength) { | |
| 135 | super(byteLength) | |
| 136 | this.field = field | |
| 137 | ||
| 138 | binding.quickbit_napi_index_init(this.handle, toBuffer(this.field)) | |
| 139 | } | |
| 140 | ||
| 141 | get byteLength() { | |
| 142 | if (this._byteLength !== -1) return this._byteLength | |
| 143 | return this.field.byteLength | |
| 144 | } | |
| 145 | ||
| 146 | update(bit) { | |
| 147 | const n = this.byteLength * 8 | |
| 148 | ||
| 149 | if (bit < 0) bit += n | |
| 150 | if (bit < 0 || bit >= n) return false | |
| 151 | ||
| 152 | return ( | |
| 153 | binding.quickbit_napi_index_update( | |
| 154 | this.handle, | |
| 155 | toBuffer(this.field), | |
| 156 | bit | |
| 157 | ) !== 0 | |
| 158 | ) | |
| 159 | } | |
| 160 | } | |
| 161 | ||
| 162 | function selectChunk(chunks, offset) { | |
| 163 | for (let i = 0; i < chunks.length; i++) { | |
| 164 | const next = chunks[i] | |
| 165 | ||
| 166 | const start = next.offset | |
| 167 | const end = next.offset + next.field.byteLength | |
| 168 | ||
| 169 | if (offset >= start && offset + 16 <= end) { | |
| 170 | return next | |
| 171 | } | |
| 172 | } | |
| 173 | ||
| 174 | return null | |
| 175 | } | |
| 176 | ||
| 177 | class SparseIndex extends Index { | |
| 178 | constructor(chunks, byteLength) { | |
| 179 | super(byteLength) | |
| 180 | this.chunks = chunks | |
| 181 | ||
| 182 | binding.quickbit_napi_index_init_sparse( | |
| 183 | this.handle, | |
| 184 | this.chunks.map(toBufferChunk) | |
| 185 | ) | |
| 186 | } | |
| 187 | ||
| 188 | get byteLength() { | |
| 189 | if (this._byteLength !== -1) return this._byteLength | |
| 190 | const last = this.chunks[this.chunks.length - 1] | |
| 191 | return last ? last.offset + last.field.byteLength : 0 | |
| 192 | } | |
| 193 | ||
| 194 | update(bit) { | |
| 195 | const n = this.byteLength * 8 | |
| 196 | ||
| 197 | if (bit < 0) bit += n | |
| 198 | if (bit < 0 || bit >= n) return false | |
| 199 | ||
| 200 | const j = Math.floor(bit / 128) | |
| 201 | ||
| 202 | const offset = j * 16 | |
| 203 | ||
| 204 | const chunk = selectChunk(this.chunks, offset) | |
| 205 | ||
| 206 | if (chunk === null) return false | |
| 207 | ||
| 208 | return ( | |
| 209 | binding.quickbit_napi_index_update_sparse( | |
| 210 | this.handle, | |
| 211 | toBuffer(chunk.field), | |
| 212 | chunk.offset, | |
| 213 | bit | |
| 214 | ) !== 0 | |
| 215 | ) | |
| 216 | } | |
| 217 | } |
| … | … | @@ -0,0 +1,44 @@ |
| 1 | { | |
| 2 | "name": "quickbit-native", | |
| 3 | "version": "2.4.8", | |
| 4 | "description": "libquickbit JavaScript bindings for Node.js", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js", | |
| 8 | "macros.h", | |
| 9 | "binding.cc", | |
| 10 | "binding.js", | |
| 11 | "CMakeLists.txt", | |
| 12 | "prebuilds" | |
| 13 | ], | |
| 14 | "addon": true, | |
| 15 | "scripts": { | |
| 16 | "test": "npm run lint && npm run test:bare && npm run test:node", | |
| 17 | "test:bare": "bare test.mjs", | |
| 18 | "test:node": "node test.mjs", | |
| 19 | "lint": "prettier . --check" | |
| 20 | }, | |
| 21 | "repository": { | |
| 22 | "type": "git", | |
| 23 | "url": "git+https://github.com/holepunchto/quickbit-native.git" | |
| 24 | }, | |
| 25 | "author": "Holepunch", | |
| 26 | "license": "Apache-2.0", | |
| 27 | "bugs": { | |
| 28 | "url": "https://github.com/holepunchto/quickbit-native/issues" | |
| 29 | }, | |
| 30 | "homepage": "https://github.com/holepunchto/quickbit-native#readme", | |
| 31 | "dependencies": { | |
| 32 | "require-addon": "^1.1.0" | |
| 33 | }, | |
| 34 | "devDependencies": { | |
| 35 | "b4a": "^1.6.7", | |
| 36 | "bare-compat-napi": "^1.3.2", | |
| 37 | "brittle": "^3.1.0", | |
| 38 | "cmake-bare": "^1.1.7", | |
| 39 | "cmake-fetch": "^1.1.0", | |
| 40 | "cmake-napi": "^1.0.2", | |
| 41 | "prettier": "^3.5.3", | |
| 42 | "prettier-config-standard": "^7.0.0" | |
| 43 | } | |
| 44 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,72 @@ |
| 1 | # quickbit-universal | |
| 2 | ||
| 3 | Universal wrapper for https://github.com/holepunchto/libquickbit with a JavaScript fallback. | |
| 4 | ||
| 5 | ```sh | |
| 6 | npm install quickbit-universal | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const { get, set, findFirst } = require('quickbit-native') | |
| 13 | ||
| 14 | const field = Buffer.alloc(256) // 2048 bits | |
| 15 | ||
| 16 | const changed = set(field, 1000) | |
| 17 | // true | |
| 18 | ||
| 19 | get(field, 1000) | |
| 20 | // true | |
| 21 | ||
| 22 | findFirst(field, true) | |
| 23 | // 1000 | |
| 24 | ``` | |
| 25 | ||
| 26 | ## API | |
| 27 | ||
| 28 | #### `const b = get(field, bit)` | |
| 29 | ||
| 30 | Get the given bit, which will either be `true` (set) or `false` (unset). | |
| 31 | ||
| 32 | #### `const changed = set(field, bit[, value])` | |
| 33 | ||
| 34 | Set the given bit to `value`, which defaults to `true`. Returns `true` if the bit changed, otherwise `false`. | |
| 35 | ||
| 36 | #### `field = fill(field, value[, start[, end]])` | |
| 37 | ||
| 38 | Fill the given bit range with `value`. `start` defaults to `0` and `end` defaults to the bit length of the field. Returns the modified field. | |
| 39 | ||
| 40 | #### `clear(field, chunk)` | |
| 41 | ||
| 42 | Clear the bits from `field` that are set in the `{ field, offset }` chunk. | |
| 43 | ||
| 44 | #### `const i = findFirst(field, value[, position])` | |
| 45 | ||
| 46 | Return the index of the first occurrence of `value`, or `-1` if not found. If `position` is given, return the first index that is greater than or equal to `position`. | |
| 47 | ||
| 48 | #### `const i = findLast(field, value[, position])` | |
| 49 | ||
| 50 | Return the index of the last occurrence of `value`, or `-1` if not found. If `position` is given, return the last index that is less than or equal to `position`. | |
| 51 | ||
| 52 | ### Indexing | |
| 53 | ||
| 54 | #### `const index = Index.from(fieldOrChunks)` | |
| 55 | ||
| 56 | Construct an index of the bits in a bit field or an array of sparse `{ field, offset }` chunks. | |
| 57 | ||
| 58 | #### `const changed = index.update(bit)` | |
| 59 | ||
| 60 | Update the given bit in the index. | |
| 61 | ||
| 62 | #### `const position = index.skipFirst(value[, position])` | |
| 63 | ||
| 64 | Skip the first parts of the index that contain nothing but `value`. If the index contains *only* `value`, the last index of the field is returned. | |
| 65 | ||
| 66 | #### `const position = index.skipLast(value[, position])` | |
| 67 | ||
| 68 | Skip the last parts of the index that contain nothing but `value`. If the index contains *only* `value`, the first index of the field is returned. | |
| 69 | ||
| 70 | ## License | |
| 71 | ||
| 72 | Apache 2.0 |
| … | … | @@ -0,0 +1,442 @@ |
| 1 | const simdle = require('simdle-universal') | |
| 2 | ||
| 3 | const INDEX_LEN = (16 /* root */ + 128 * 16 /* children */) * 2 | |
| 4 | ||
| 5 | const get = exports.get = function get (field, bit) { | |
| 6 | const n = field.byteLength * 8 | |
| 7 | ||
| 8 | if (bit < 0) bit += n | |
| 9 | if (bit < 0 || bit >= n) return false | |
| 10 | ||
| 11 | const m = field.BYTES_PER_ELEMENT * 8 | |
| 12 | ||
| 13 | const offset = bit & (m - 1) | |
| 14 | const i = (bit - offset) / m | |
| 15 | ||
| 16 | return (field[i] & (1 << offset)) !== 0 | |
| 17 | } | |
| 18 | ||
| 19 | const set = exports.set = function set (field, bit, value = true) { | |
| 20 | const n = field.byteLength * 8 | |
| 21 | ||
| 22 | if (bit < 0) bit += n | |
| 23 | if (bit < 0 || bit >= n) return false | |
| 24 | ||
| 25 | const m = field.BYTES_PER_ELEMENT * 8 | |
| 26 | ||
| 27 | const offset = bit & (m - 1) | |
| 28 | const i = (bit - offset) / m | |
| 29 | const mask = 1 << offset | |
| 30 | ||
| 31 | if (value) { | |
| 32 | if ((field[i] & mask) !== 0) return false | |
| 33 | } else { | |
| 34 | if ((field[i] & mask) === 0) return false | |
| 35 | } | |
| 36 | ||
| 37 | field[i] ^= mask | |
| 38 | ||
| 39 | return true | |
| 40 | } | |
| 41 | ||
| 42 | exports.fill = function fill (field, value, start = 0, end = field.byteLength * 8) { | |
| 43 | const n = field.byteLength * 8 | |
| 44 | ||
| 45 | if (start < 0) start += n | |
| 46 | if (end < 0) end += n | |
| 47 | if (start < 0 || start >= field.byteLength * 8 || start >= end) return field | |
| 48 | ||
| 49 | const m = field.BYTES_PER_ELEMENT * 8 | |
| 50 | ||
| 51 | let i, j | |
| 52 | ||
| 53 | { | |
| 54 | const offset = start & (m - 1) | |
| 55 | i = (start - offset) / m | |
| 56 | ||
| 57 | if (offset !== 0) { | |
| 58 | let shift = m - offset | |
| 59 | if (end - start < shift) shift = end - start | |
| 60 | ||
| 61 | const mask = ((1 << shift) - 1) << offset | |
| 62 | ||
| 63 | if (value) field[i] |= mask | |
| 64 | else field[i] &= ~mask | |
| 65 | ||
| 66 | i++ | |
| 67 | } | |
| 68 | } | |
| 69 | ||
| 70 | { | |
| 71 | const offset = end & (m - 1) | |
| 72 | j = (end - offset) / m | |
| 73 | ||
| 74 | if (offset !== 0 && j >= i) { | |
| 75 | const mask = (1 << offset) - 1 | |
| 76 | ||
| 77 | if (value) field[j] |= mask | |
| 78 | else field[j] &= ~mask | |
| 79 | } | |
| 80 | } | |
| 81 | ||
| 82 | if (i < j) field.fill(value ? (2 ** m) - 1 : 0, i, j) | |
| 83 | ||
| 84 | return field | |
| 85 | } | |
| 86 | ||
| 87 | exports.clear = function clear (field, ...chunks) { | |
| 88 | const n = field.byteLength | |
| 89 | ||
| 90 | for (const chunk of chunks) { | |
| 91 | if (chunk.offset >= n) continue | |
| 92 | ||
| 93 | const m = chunk.field.byteLength | |
| 94 | ||
| 95 | let i = chunk.offset | |
| 96 | let j = 0 | |
| 97 | ||
| 98 | while (((i & 15) !== 0 || (j & 15) !== 0) && i < n && j < m) { | |
| 99 | field[i] = field[i] & ~chunk.field[j] | |
| 100 | i++ | |
| 101 | j++ | |
| 102 | } | |
| 103 | ||
| 104 | if (i + 15 < n && j + 15 < m) { | |
| 105 | const len = Math.min(n - (n & 15) - i, m - (m & 15) - j) | |
| 106 | ||
| 107 | simdle.clear(field.subarray(i, i + len), chunk.field.subarray(j, j + len), field.subarray(i, i + len)) | |
| 108 | } | |
| 109 | ||
| 110 | while (i < n && j < m) { | |
| 111 | field[i] = field[i] & ~chunk.field[j] | |
| 112 | i++ | |
| 113 | j++ | |
| 114 | } | |
| 115 | } | |
| 116 | } | |
| 117 | ||
| 118 | function bitOffset (bit, offset) { | |
| 119 | return !bit ? offset : (INDEX_LEN * 8 / 2) + offset | |
| 120 | } | |
| 121 | ||
| 122 | function byteOffset (bit, offset) { | |
| 123 | return !bit ? offset : (INDEX_LEN / 2) + offset | |
| 124 | } | |
| 125 | ||
| 126 | exports.findFirst = function findFirst (field, value, position = 0) { | |
| 127 | const n = field.byteLength * 8 | |
| 128 | ||
| 129 | if (position < 0) position += n | |
| 130 | if (position < 0) position = 0 | |
| 131 | if (position >= n) return -1 | |
| 132 | ||
| 133 | value = !!value | |
| 134 | ||
| 135 | for (let i = position; i < n; i++) { | |
| 136 | if (get(field, i) === value) return i | |
| 137 | } | |
| 138 | ||
| 139 | return -1 | |
| 140 | } | |
| 141 | ||
| 142 | exports.findLast = function findLast (field, value, position = field.byteLength * 8 - 1) { | |
| 143 | const n = field.byteLength * 8 | |
| 144 | ||
| 145 | if (position < 0) position += n | |
| 146 | if (position < 0) return -1 | |
| 147 | if (position >= n) position = n - 1 | |
| 148 | ||
| 149 | value = !!value | |
| 150 | ||
| 151 | for (let i = position; i >= 0; i--) { | |
| 152 | if (get(field, i) === value) return i | |
| 153 | } | |
| 154 | ||
| 155 | return -1 | |
| 156 | } | |
| 157 | ||
| 158 | const Index = exports.Index = class Index { | |
| 159 | static from (fieldOrChunks, byteLength = -1) { | |
| 160 | if (Array.isArray(fieldOrChunks)) { | |
| 161 | return new SparseIndex(fieldOrChunks, byteLength) | |
| 162 | } else { | |
| 163 | return new DenseIndex(fieldOrChunks, byteLength) | |
| 164 | } | |
| 165 | } | |
| 166 | ||
| 167 | constructor (byteLength) { | |
| 168 | this._byteLength = byteLength | |
| 169 | this.handle = new Uint32Array(INDEX_LEN / 4) | |
| 170 | } | |
| 171 | ||
| 172 | get byteLength () { | |
| 173 | return this._byteLength | |
| 174 | } | |
| 175 | ||
| 176 | skipFirst (value, position = 0) { | |
| 177 | const n = this.byteLength * 8 | |
| 178 | ||
| 179 | if (position < 0) position += n | |
| 180 | if (position < 0) position = 0 | |
| 181 | if (position >= n) return n - 1 | |
| 182 | ||
| 183 | let i = Math.floor(position / 16384) | |
| 184 | ||
| 185 | if (i > 127) return position | |
| 186 | ||
| 187 | while (i <= 127 && get(this.handle, bitOffset(value, i))) { | |
| 188 | i++ | |
| 189 | } | |
| 190 | ||
| 191 | if (i === 128) return n - 1 | |
| 192 | ||
| 193 | let k = i * 16384 | |
| 194 | let j = 0 | |
| 195 | ||
| 196 | if (position > k) j = Math.floor((position - k) / 128) | |
| 197 | ||
| 198 | while (j <= 127 && get(this.handle, bitOffset(value, i * 128 + j + 128))) { | |
| 199 | j++ | |
| 200 | k += 128 | |
| 201 | } | |
| 202 | ||
| 203 | if (j === 128 && i !== 127) return this.skipFirst(value, (i + 1) * 16384) | |
| 204 | ||
| 205 | if (k > position) position = k | |
| 206 | ||
| 207 | return position < n ? position : n - 1 | |
| 208 | } | |
| 209 | ||
| 210 | skipLast (value, position = this.byteLength * 8 - 1) { | |
| 211 | const n = this.byteLength * 8 | |
| 212 | ||
| 213 | if (position < 0) position += n | |
| 214 | if (position < 0) return 0 | |
| 215 | if (position >= n) position = n - 1 | |
| 216 | ||
| 217 | let i = Math.floor(position / 16384) | |
| 218 | ||
| 219 | if (i > 127) return position | |
| 220 | ||
| 221 | while (i >= 0 && get(this.handle, bitOffset(value, i))) { | |
| 222 | i-- | |
| 223 | } | |
| 224 | ||
| 225 | if (i === -1) return 0 | |
| 226 | ||
| 227 | let k = ((i + 1) * 16384) - 1 | |
| 228 | let j = 127 | |
| 229 | ||
| 230 | if (position < k) j = 128 - Math.ceil((k - position) / 128) | |
| 231 | ||
| 232 | while (j >= 0 && get(this.handle, bitOffset(value, i * 128 + j + 128))) { | |
| 233 | j-- | |
| 234 | k -= 128 | |
| 235 | } | |
| 236 | ||
| 237 | if (j === -1 && i !== 0) return this.skipLast(value, i * 16384 - 1) | |
| 238 | ||
| 239 | if (k < position) position = k | |
| 240 | ||
| 241 | return position | |
| 242 | } | |
| 243 | } | |
| 244 | ||
| 245 | class DenseIndex extends Index { | |
| 246 | constructor (field, byteLength) { | |
| 247 | super(byteLength) | |
| 248 | this.field = field | |
| 249 | ||
| 250 | const m = field.BYTES_PER_ELEMENT | |
| 251 | ||
| 252 | for (let i = 0; i < 128; i++) { | |
| 253 | for (let j = 0; j < 128; j++) { | |
| 254 | const offset = (i * 128 + j) * 16 | |
| 255 | let allz = true | |
| 256 | let allo = false | |
| 257 | ||
| 258 | if (offset + 16 <= this.field.byteLength) { | |
| 259 | const vec = this.field.subarray(offset / m, (offset + 16) / m) | |
| 260 | ||
| 261 | allz = simdle.allz(vec) | |
| 262 | allo = simdle.allo(vec) | |
| 263 | } | |
| 264 | ||
| 265 | const k = i * 128 + 128 + j | |
| 266 | ||
| 267 | set(this.handle, bitOffset(false, k), allz) | |
| 268 | set(this.handle, bitOffset(true, k), allo) | |
| 269 | } | |
| 270 | ||
| 271 | { | |
| 272 | const offset = byteOffset(false, i * 16 + 16) / 4 | |
| 273 | const allo = simdle.allo(this.handle.subarray(offset, offset + 4)) | |
| 274 | ||
| 275 | set(this.handle, bitOffset(false, i), allo) | |
| 276 | } | |
| 277 | ||
| 278 | { | |
| 279 | const offset = byteOffset(true, i * 16 + 16) / 4 | |
| 280 | const allo = simdle.allo(this.handle.subarray(offset, offset + 4)) | |
| 281 | ||
| 282 | set(this.handle, bitOffset(true, i), allo) | |
| 283 | } | |
| 284 | } | |
| 285 | } | |
| 286 | ||
| 287 | get byteLength () { | |
| 288 | if (this._byteLength !== -1) return this._byteLength | |
| 289 | return this.field.byteLength | |
| 290 | } | |
| 291 | ||
| 292 | update (bit) { | |
| 293 | const n = this.byteLength * 8 | |
| 294 | ||
| 295 | if (bit < 0) bit += n | |
| 296 | if (bit < 0 || bit >= n) return false | |
| 297 | ||
| 298 | const m = this.field.BYTES_PER_ELEMENT | |
| 299 | ||
| 300 | const i = Math.floor(bit / 16384) | |
| 301 | const j = Math.floor(bit / 128) | |
| 302 | ||
| 303 | const offset = (j * 16) / m | |
| 304 | const vec = this.field.subarray(offset, offset + (16 / m)) | |
| 305 | ||
| 306 | const allz = simdle.allz(vec) | |
| 307 | const allo = simdle.allo(vec) | |
| 308 | ||
| 309 | let changed = false | |
| 310 | ||
| 311 | if (set(this.handle, bitOffset(false, 128 + j), allz)) { | |
| 312 | changed = true | |
| 313 | ||
| 314 | const offset = byteOffset(false, i * 16 + 16) / 4 | |
| 315 | const allo = simdle.allo(this.handle.subarray(offset, offset + 4)) | |
| 316 | ||
| 317 | set(this.handle, bitOffset(false, i), allo) | |
| 318 | } | |
| 319 | ||
| 320 | if (set(this.handle, bitOffset(true, 128 + j), allo)) { | |
| 321 | changed = true | |
| 322 | ||
| 323 | const offset = byteOffset(true, i * 16 + 16) / 4 | |
| 324 | const allo = simdle.allo(this.handle.subarray(offset, offset + 4)) | |
| 325 | ||
| 326 | set(this.handle, bitOffset(true, i), allo) | |
| 327 | } | |
| 328 | ||
| 329 | return changed | |
| 330 | } | |
| 331 | } | |
| 332 | ||
| 333 | function selectChunk (chunks, offset) { | |
| 334 | for (let i = 0; i < chunks.length; i++) { | |
| 335 | const next = chunks[i] | |
| 336 | ||
| 337 | const start = next.offset | |
| 338 | const end = next.offset + next.field.byteLength | |
| 339 | ||
| 340 | if (offset >= start && offset + 16 <= end) { | |
| 341 | return next | |
| 342 | } | |
| 343 | } | |
| 344 | ||
| 345 | return null | |
| 346 | } | |
| 347 | ||
| 348 | class SparseIndex extends Index { | |
| 349 | constructor (chunks, byteLength) { | |
| 350 | super(byteLength) | |
| 351 | this.chunks = chunks | |
| 352 | ||
| 353 | for (let i = 0; i < 128; i++) { | |
| 354 | for (let j = 0; j < 128; j++) { | |
| 355 | const offset = (i * 128 + j) * 16 | |
| 356 | let allz = true | |
| 357 | let allo = false | |
| 358 | ||
| 359 | const chunk = selectChunk(this.chunks, offset) | |
| 360 | ||
| 361 | if (chunk !== null) { | |
| 362 | const m = chunk.field.BYTES_PER_ELEMENT | |
| 363 | ||
| 364 | const vec = chunk.field.subarray((offset - chunk.offset) / m, (offset - chunk.offset + 16) / m) | |
| 365 | ||
| 366 | allz = simdle.allz(vec) | |
| 367 | allo = simdle.allo(vec) | |
| 368 | } | |
| 369 | ||
| 370 | const k = i * 128 + 128 + j | |
| 371 | ||
| 372 | set(this.handle, bitOffset(false, k), allz) | |
| 373 | set(this.handle, bitOffset(true, k), allo) | |
| 374 | } | |
| 375 | ||
| 376 | { | |
| 377 | const offset = byteOffset(false, i * 16 + 16) / 4 | |
| 378 | const allo = simdle.allo(this.handle.subarray(offset, offset + 4)) | |
| 379 | ||
| 380 | set(this.handle, bitOffset(false, i), allo) | |
| 381 | } | |
| 382 | ||
| 383 | { | |
| 384 | const offset = byteOffset(true, i * 16 + 16) / 4 | |
| 385 | const allo = simdle.allo(this.handle.subarray(offset, offset + 4)) | |
| 386 | ||
| 387 | set(this.handle, bitOffset(true, i), allo) | |
| 388 | } | |
| 389 | } | |
| 390 | } | |
| 391 | ||
| 392 | get byteLength () { | |
| 393 | if (this._byteLength !== -1) return this._byteLength | |
| 394 | const last = this.chunks[this.chunks.length - 1] | |
| 395 | return last ? last.offset + last.field.byteLength : 0 | |
| 396 | } | |
| 397 | ||
| 398 | update (bit) { | |
| 399 | const n = this.byteLength * 8 | |
| 400 | ||
| 401 | if (bit < 0) bit += n | |
| 402 | if (bit < 0 || bit >= n) return false | |
| 403 | ||
| 404 | const i = Math.floor(bit / 16384) | |
| 405 | const j = Math.floor(bit / 128) | |
| 406 | ||
| 407 | const offset = j * 16 | |
| 408 | ||
| 409 | const chunk = selectChunk(this.chunks, offset) | |
| 410 | ||
| 411 | if (chunk === null) return false | |
| 412 | ||
| 413 | const m = chunk.field.BYTES_PER_ELEMENT | |
| 414 | ||
| 415 | const vec = chunk.field.subarray((offset - chunk.offset) / m, (offset - chunk.offset + 16) / m) | |
| 416 | ||
| 417 | const allz = simdle.allz(vec) | |
| 418 | const allo = simdle.allo(vec) | |
| 419 | ||
| 420 | let changed = false | |
| 421 | ||
| 422 | if (set(this.handle, bitOffset(false, 128 + j), allz)) { | |
| 423 | changed = true | |
| 424 | ||
| 425 | const offset = byteOffset(false, i * 16 + 16) / 4 | |
| 426 | const allo = simdle.allo(this.handle.subarray(offset, offset + 4)) | |
| 427 | ||
| 428 | set(this.handle, bitOffset(false, i), allo) | |
| 429 | } | |
| 430 | ||
| 431 | if (set(this.handle, bitOffset(true, 128 + j), allo)) { | |
| 432 | changed = true | |
| 433 | ||
| 434 | const offset = byteOffset(true, i * 16 + 16) / 4 | |
| 435 | const allo = simdle.allo(this.handle.subarray(offset, offset + 4)) | |
| 436 | ||
| 437 | set(this.handle, bitOffset(true, i), allo) | |
| 438 | } | |
| 439 | ||
| 440 | return changed | |
| 441 | } | |
| 442 | } |
| … | … | @@ -0,0 +1,18 @@ |
| 1 | const fallback = require('./fallback') | |
| 2 | ||
| 3 | try { | |
| 4 | const native = require('quickbit-native') | |
| 5 | ||
| 6 | // These functions are faster in JavaScript | |
| 7 | exports.get = fallback.get | |
| 8 | exports.set = fallback.set | |
| 9 | exports.fill = fallback.fill | |
| 10 | ||
| 11 | // These functions are faster in C | |
| 12 | exports.clear = native.clear | |
| 13 | exports.findFirst = native.findFirst | |
| 14 | exports.findLast = native.findLast | |
| 15 | exports.Index = native.Index | |
| 16 | } catch { | |
| 17 | module.exports = fallback | |
| 18 | } |
| … | … | @@ -0,0 +1,37 @@ |
| 1 | { | |
| 2 | "name": "quickbit-universal", | |
| 3 | "version": "2.2.0", | |
| 4 | "description": "Universal wrapper for libquickbit with a JavaScript fallback", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "fallback.js", | |
| 8 | "index.js" | |
| 9 | ], | |
| 10 | "browser": { | |
| 11 | "./index.js": "./fallback.js" | |
| 12 | }, | |
| 13 | "scripts": { | |
| 14 | "test": "standard && brittle test.mjs" | |
| 15 | }, | |
| 16 | "repository": { | |
| 17 | "type": "git", | |
| 18 | "url": "git+https://github.com/holepunchto/quickbit-universal.git" | |
| 19 | }, | |
| 20 | "author": "Kasper Isager Dalsgarð <kasper@funktionel.co>", | |
| 21 | "license": "ISC", | |
| 22 | "bugs": { | |
| 23 | "url": "https://github.com/holepunchto/quickbit-universal/issues" | |
| 24 | }, | |
| 25 | "homepage": "https://github.com/holepunchto/quickbit-universal#readme", | |
| 26 | "dependencies": { | |
| 27 | "b4a": "^1.6.0", | |
| 28 | "simdle-universal": "^1.1.0" | |
| 29 | }, | |
| 30 | "optionalDependencies": { | |
| 31 | "quickbit-native": "^2.2.0" | |
| 32 | }, | |
| 33 | "devDependencies": { | |
| 34 | "brittle": "^3.1.0", | |
| 35 | "standard": "^17.0.0" | |
| 36 | } | |
| 37 | } |
| … | … | @@ -0,0 +1,57 @@ |
| 1 | cmake_minimum_required(VERSION 4.0) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) | |
| 5 | find_package(cmake-napi REQUIRED PATHS node_modules/cmake-napi) | |
| 6 | ||
| 7 | project(rabin_native C) | |
| 8 | ||
| 9 | bare_target(target) | |
| 10 | ||
| 11 | fetch_package("github:holepunchto/librabin#2e4f70b") | |
| 12 | ||
| 13 | add_bare_module(rabin_native_bare) | |
| 14 | ||
| 15 | target_sources( | |
| 16 | ${rabin_native_bare} | |
| 17 | PRIVATE | |
| 18 | binding.c | |
| 19 | ) | |
| 20 | ||
| 21 | target_link_libraries( | |
| 22 | ${rabin_native_bare} | |
| 23 | PRIVATE | |
| 24 | $<TARGET_OBJECTS:rabin> | |
| 25 | PUBLIC | |
| 26 | rabin | |
| 27 | ) | |
| 28 | ||
| 29 | add_napi_module(rabin_native_node) | |
| 30 | ||
| 31 | target_sources( | |
| 32 | ${rabin_native_node} | |
| 33 | PRIVATE | |
| 34 | binding.c | |
| 35 | ) | |
| 36 | ||
| 37 | target_link_libraries( | |
| 38 | ${rabin_native_node} | |
| 39 | PRIVATE | |
| 40 | $<TARGET_OBJECTS:rabin> | |
| 41 | PUBLIC | |
| 42 | rabin | |
| 43 | ) | |
| 44 | ||
| 45 | target_compile_definitions( | |
| 46 | ${rabin_native_node} | |
| 47 | PRIVATE | |
| 48 | NAPI_VERSION=9 | |
| 49 | ) | |
| 50 | ||
| 51 | resolve_node_module(bare-compat-napi compat) | |
| 52 | ||
| 53 | target_include_directories( | |
| 54 | ${rabin_native_node} | |
| 55 | PRIVATE | |
| 56 | "${compat}/include" | |
| 57 | ) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,62 @@ |
| 1 | # rabin-native | |
| 2 | ||
| 3 | <https://github.com/holepunchto/librabin> bindings for JavaScript. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i rabin-native | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const rabin = require('rabin-native') | |
| 13 | ||
| 14 | const chunker = new rabin.Chunker() | |
| 15 | const chunks = [] | |
| 16 | ||
| 17 | for (const chunk of chunker.push(shakespeare)) { | |
| 18 | chunks.push(chunk) | |
| 19 | } | |
| 20 | ||
| 21 | const chunk = chunker.end() | |
| 22 | ||
| 23 | if (chunk) chunks.push(chunk) | |
| 24 | ``` | |
| 25 | ||
| 26 | ## API | |
| 27 | ||
| 28 | #### `const chunker = new Chunker([options])` | |
| 29 | ||
| 30 | Construct a new chunker. | |
| 31 | ||
| 32 | Options include: | |
| 33 | ||
| 34 | ```js | |
| 35 | options = { | |
| 36 | minSize: 512 * 1024, // 512 KiB | |
| 37 | maxSize: 8 * 1024 * 1024 // 8 MiB | |
| 38 | } | |
| 39 | ``` | |
| 40 | ||
| 41 | #### `const iterator = chunker.push(data)` | |
| 42 | ||
| 43 | Push data to the chunker, returning an iterator over the identified chunks. | |
| 44 | ||
| 45 | Each chunk has the following shape: | |
| 46 | ||
| 47 | ```js | |
| 48 | chunk = { | |
| 49 | // Size of the chunk in bytes | |
| 50 | length, | |
| 51 | // Offset of the chunk within the stream | |
| 52 | offset | |
| 53 | } | |
| 54 | ``` | |
| 55 | ||
| 56 | #### `const chunk = chunker.end()` | |
| 57 | ||
| 58 | Finish the chunker, returning the trailing chunk or `null`. | |
| 59 | ||
| 60 | ## License | |
| 61 | ||
| 62 | Apache-2.0 |
| … | … | @@ -0,0 +1,165 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <bare.h> | |
| 3 | #include <js.h> | |
| 4 | #include <rabin.h> | |
| 5 | #include <uv.h> | |
| 6 | ||
| 7 | typedef struct { | |
| 8 | rabin_t context; | |
| 9 | } rabin_native_t; | |
| 10 | ||
| 11 | static js_value_t * | |
| 12 | rabin_native_init(js_env_t *env, js_callback_info_t *info) { | |
| 13 | int err; | |
| 14 | ||
| 15 | size_t argc = 2; | |
| 16 | js_value_t *argv[2]; | |
| 17 | ||
| 18 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 19 | assert(err == 0); | |
| 20 | ||
| 21 | assert(argc == 2); | |
| 22 | ||
| 23 | uint32_t min_size; | |
| 24 | err = js_get_value_uint32(env, argv[0], &min_size); | |
| 25 | assert(err == 0); | |
| 26 | ||
| 27 | uint32_t max_size; | |
| 28 | err = js_get_value_uint32(env, argv[1], &max_size); | |
| 29 | assert(err == 0); | |
| 30 | ||
| 31 | js_value_t *handle; | |
| 32 | ||
| 33 | rabin_native_t *chunker; | |
| 34 | err = js_create_arraybuffer(env, sizeof(rabin_native_t), (void **) &chunker, &handle); | |
| 35 | assert(err == 0); | |
| 36 | ||
| 37 | rabin_init(&chunker->context); | |
| 38 | ||
| 39 | chunker->context.chunk_min = min_size; | |
| 40 | chunker->context.chunk_max = max_size; | |
| 41 | ||
| 42 | return handle; | |
| 43 | } | |
| 44 | ||
| 45 | static js_value_t * | |
| 46 | rabin_native_push(js_env_t *env, js_callback_info_t *info) { | |
| 47 | int err; | |
| 48 | ||
| 49 | size_t argc = 4; | |
| 50 | js_value_t *argv[4]; | |
| 51 | ||
| 52 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 53 | assert(err == 0); | |
| 54 | ||
| 55 | assert(argc == 4); | |
| 56 | ||
| 57 | rabin_native_t *chunker; | |
| 58 | err = js_get_arraybuffer_info(env, argv[0], (void **) &chunker, NULL); | |
| 59 | assert(err == 0); | |
| 60 | ||
| 61 | void *data; | |
| 62 | err = js_get_arraybuffer_info(env, argv[1], &data, NULL); | |
| 63 | assert(err == 0); | |
| 64 | ||
| 65 | int64_t offset; | |
| 66 | err = js_get_value_int64(env, argv[2], &offset); | |
| 67 | assert(err == 0); | |
| 68 | ||
| 69 | int64_t len; | |
| 70 | err = js_get_value_int64(env, argv[3], &len); | |
| 71 | assert(err == 0); | |
| 72 | ||
| 73 | len = rabin_push(&chunker->context, &data[offset], len); | |
| 74 | ||
| 75 | js_value_t *result; | |
| 76 | err = js_create_int64(env, len, &result); | |
| 77 | assert(err == 0); | |
| 78 | ||
| 79 | return result; | |
| 80 | } | |
| 81 | ||
| 82 | static js_value_t * | |
| 83 | rabin_native_end(js_env_t *env, js_callback_info_t *info) { | |
| 84 | int err; | |
| 85 | ||
| 86 | size_t argc = 1; | |
| 87 | js_value_t *argv[1]; | |
| 88 | ||
| 89 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 90 | assert(err == 0); | |
| 91 | ||
| 92 | assert(argc == 1); | |
| 93 | ||
| 94 | rabin_native_t *chunker; | |
| 95 | err = js_get_arraybuffer_info(env, argv[0], (void **) &chunker, NULL); | |
| 96 | assert(err == 0); | |
| 97 | ||
| 98 | int64_t len = rabin_end(&chunker->context); | |
| 99 | ||
| 100 | js_value_t *result; | |
| 101 | err = js_create_int64(env, len, &result); | |
| 102 | assert(err == 0); | |
| 103 | ||
| 104 | return result; | |
| 105 | } | |
| 106 | ||
| 107 | static js_value_t * | |
| 108 | rabin_native_last_chunk(js_env_t *env, js_callback_info_t *info) { | |
| 109 | int err; | |
| 110 | ||
| 111 | size_t argc = 1; | |
| 112 | js_value_t *argv[1]; | |
| 113 | ||
| 114 | err = js_get_callback_info(env, info, &argc, argv, NULL, NULL); | |
| 115 | assert(err == 0); | |
| 116 | ||
| 117 | assert(argc == 1); | |
| 118 | ||
| 119 | rabin_native_t *chunker; | |
| 120 | err = js_get_arraybuffer_info(env, argv[0], (void **) &chunker, NULL); | |
| 121 | assert(err == 0); | |
| 122 | ||
| 123 | js_value_t *result; | |
| 124 | err = js_create_object(env, &result); | |
| 125 | assert(err == 0); | |
| 126 | ||
| 127 | #define V(name, property) \ | |
| 128 | { \ | |
| 129 | js_value_t *val; \ | |
| 130 | err = js_create_int64(env, chunker->context.last_chunk.property, &val); \ | |
| 131 | assert(err == 0); \ | |
| 132 | err = js_set_named_property(env, result, name, val); \ | |
| 133 | assert(err == 0); \ | |
| 134 | } | |
| 135 | ||
| 136 | V("length", length) | |
| 137 | V("offset", offset) | |
| 138 | #undef V | |
| 139 | ||
| 140 | return result; | |
| 141 | } | |
| 142 | ||
| 143 | static js_value_t * | |
| 144 | rabin_native_exports(js_env_t *env, js_value_t *exports) { | |
| 145 | int err; | |
| 146 | ||
| 147 | #define V(name, fn) \ | |
| 148 | { \ | |
| 149 | js_value_t *val; \ | |
| 150 | err = js_create_function(env, name, -1, fn, NULL, &val); \ | |
| 151 | assert(err == 0); \ | |
| 152 | err = js_set_named_property(env, exports, name, val); \ | |
| 153 | assert(err == 0); \ | |
| 154 | } | |
| 155 | ||
| 156 | V("init", rabin_native_init) | |
| 157 | V("push", rabin_native_push) | |
| 158 | V("end", rabin_native_end) | |
| 159 | V("lastChunk", rabin_native_last_chunk) | |
| 160 | #undef V | |
| 161 | ||
| 162 | return exports; | |
| 163 | } | |
| 164 | ||
| 165 | BARE_MODULE(rabin_native, rabin_native_exports) |
| … | … | @@ -0,0 +1,3 @@ |
| 1 | require.addon = require('require-addon') | |
| 2 | ||
| 3 | module.exports = require.addon('.', __filename) |
| … | … | @@ -0,0 +1,34 @@ |
| 1 | const binding = require('./binding') | |
| 2 | ||
| 3 | class RabinChunker { | |
| 4 | constructor(opts = {}) { | |
| 5 | const { | |
| 6 | minSize = 512 * 1024, // 512 KiB | |
| 7 | maxSize = 8 * 1024 * 1024 // 8 MiB | |
| 8 | } = opts | |
| 9 | ||
| 10 | this._handle = binding.init(minSize, maxSize) | |
| 11 | } | |
| 12 | ||
| 13 | *push(data) { | |
| 14 | while (true) { | |
| 15 | const length = binding.push(this._handle, data.buffer, data.byteOffset, data.byteLength) | |
| 16 | ||
| 17 | if (length === 0) return | |
| 18 | ||
| 19 | data = data.subarray(length) | |
| 20 | ||
| 21 | yield binding.lastChunk(this._handle) | |
| 22 | } | |
| 23 | } | |
| 24 | ||
| 25 | end() { | |
| 26 | const length = binding.end(this._handle) | |
| 27 | ||
| 28 | if (length === 0) return null | |
| 29 | ||
| 30 | return binding.lastChunk(this._handle) | |
| 31 | } | |
| 32 | } | |
| 33 | ||
| 34 | exports.Chunker = RabinChunker |
| … | … | @@ -0,0 +1,60 @@ |
| 1 | { | |
| 2 | "name": "rabin-native", | |
| 3 | "version": "2.0.0", | |
| 4 | "description": "librabin bindings for JavaScript", | |
| 5 | "exports": { | |
| 6 | "./package": "./package.json", | |
| 7 | ".": "./index.js" | |
| 8 | }, | |
| 9 | "imports": { | |
| 10 | "fs": { | |
| 11 | "bare": "bare-fs", | |
| 12 | "default": "fs" | |
| 13 | }, | |
| 14 | "path": { | |
| 15 | "bare": "bare-path", | |
| 16 | "default": "path" | |
| 17 | } | |
| 18 | }, | |
| 19 | "files": [ | |
| 20 | "index.js", | |
| 21 | "binding.c", | |
| 22 | "binding.js", | |
| 23 | "CMakeLists.txt", | |
| 24 | "prebuilds" | |
| 25 | ], | |
| 26 | "addon": true, | |
| 27 | "scripts": { | |
| 28 | "format": "prettier --write .", | |
| 29 | "lint": "prettier --check . && lunte", | |
| 30 | "test": "npm run test:node && npm run test:bare", | |
| 31 | "test:bare": "brittle-bare --coverage test.js", | |
| 32 | "test:node": "brittle-node --coverage test.js" | |
| 33 | }, | |
| 34 | "repository": { | |
| 35 | "type": "git", | |
| 36 | "url": "git+https://github.com/holepunchto/rabin-native.git" | |
| 37 | }, | |
| 38 | "author": "Holepunch", | |
| 39 | "license": "Apache-2.0", | |
| 40 | "bugs": { | |
| 41 | "url": "https://github.com/holepunchto/rabin-native/issues" | |
| 42 | }, | |
| 43 | "homepage": "https://github.com/holepunchto/rabin-native#readme", | |
| 44 | "dependencies": { | |
| 45 | "require-addon": "^1.1.0" | |
| 46 | }, | |
| 47 | "devDependencies": { | |
| 48 | "bare-compat-napi": "^1.3.9", | |
| 49 | "bare-fs": "^4.5.3", | |
| 50 | "bare-path": "^3.0.0", | |
| 51 | "brittle": "^3.1.0", | |
| 52 | "cmake-bare": "^1.1.7", | |
| 53 | "cmake-fetch": "^1.1.0", | |
| 54 | "cmake-napi": "^1.0.2", | |
| 55 | "cmake-npm": "^1.1.0", | |
| 56 | "lunte": "^1.3.0", | |
| 57 | "prettier": "^3.6.2", | |
| 58 | "prettier-config-holepunch": "^2.0.0" | |
| 59 | } | |
| 60 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,31 @@ |
| 1 | # rabin-stream | |
| 2 | ||
| 3 | Streaming Rabin chunker. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i rabin-stream | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const RabinStream = require('rabin-stream') | |
| 13 | ||
| 14 | const r = new RabinStream() | |
| 15 | ||
| 16 | r.on('data', function (data) { | |
| 17 | console.log('Chunk', data) | |
| 18 | }) | |
| 19 | ||
| 20 | someStream.pipe(r) | |
| 21 | ``` | |
| 22 | ||
| 23 | ## API | |
| 24 | ||
| 25 | #### `const stream = new RabinStream([options])` | |
| 26 | ||
| 27 | Construct a new chunker stream. Options supported by <https://github.com/holepunchto/rabin-native> may be specified. | |
| 28 | ||
| 29 | ## License | |
| 30 | ||
| 31 | Apache-2.0 |
| … | … | @@ -0,0 +1,49 @@ |
| 1 | const { Transform } = require('streamx') | |
| 2 | const rabin = require('rabin-native') | |
| 3 | ||
| 4 | module.exports = class RabinStream extends Transform { | |
| 5 | constructor(opts = {}) { | |
| 6 | super() | |
| 7 | ||
| 8 | this._rabin = new rabin.Chunker(opts) | |
| 9 | this._buffer = [] | |
| 10 | } | |
| 11 | ||
| 12 | _transform(data, cb) { | |
| 13 | if (typeof data === 'string') data = Buffer.from(data) | |
| 14 | ||
| 15 | this._buffer.push(data) | |
| 16 | ||
| 17 | const chunks = this._rabin.push(data) | |
| 18 | ||
| 19 | for (const chunk of chunks) { | |
| 20 | let data = this._buffer[0] | |
| 21 | ||
| 22 | if (data.byteLength < chunk.length) { | |
| 23 | data = Buffer.concat(this._buffer) | |
| 24 | ||
| 25 | this._buffer = [] | |
| 26 | } else { | |
| 27 | this._buffer.pop() | |
| 28 | } | |
| 29 | ||
| 30 | this.push(data.subarray(0, chunk.length)) | |
| 31 | ||
| 32 | data = data.subarray(chunk.length) | |
| 33 | ||
| 34 | if (data.byteLength) this._buffer.unshift(data) | |
| 35 | } | |
| 36 | ||
| 37 | cb(null) | |
| 38 | } | |
| 39 | ||
| 40 | _flush(cb) { | |
| 41 | if (this._buffer.length) { | |
| 42 | this.push(Buffer.concat(this._buffer)) | |
| 43 | } | |
| 44 | ||
| 45 | this._buffer = [] | |
| 46 | ||
| 47 | cb(null) | |
| 48 | } | |
| 49 | } |
| … | … | @@ -0,0 +1,51 @@ |
| 1 | { | |
| 2 | "name": "rabin-stream", | |
| 3 | "version": "2.0.0", | |
| 4 | "description": "Streaming Rabin chunker", | |
| 5 | "exports": { | |
| 6 | "./package": "./package.json", | |
| 7 | ".": "./index.js" | |
| 8 | }, | |
| 9 | "imports": { | |
| 10 | "fs": { | |
| 11 | "bare": "bare-fs", | |
| 12 | "default": "fs" | |
| 13 | }, | |
| 14 | "path": { | |
| 15 | "bare": "bare-path", | |
| 16 | "default": "path" | |
| 17 | } | |
| 18 | }, | |
| 19 | "files": [ | |
| 20 | "index.js" | |
| 21 | ], | |
| 22 | "scripts": { | |
| 23 | "format": "prettier --write .", | |
| 24 | "lint": "prettier --check . && lunte", | |
| 25 | "test": "npm run test:node && npm run test:bare", | |
| 26 | "test:bare": "brittle-bare --coverage test.js", | |
| 27 | "test:node": "brittle-node --coverage test.js" | |
| 28 | }, | |
| 29 | "repository": { | |
| 30 | "type": "git", | |
| 31 | "url": "git+https://github.com/holepunchto/rabin-stream.git" | |
| 32 | }, | |
| 33 | "author": "Holepunch", | |
| 34 | "license": "Apache-2.0", | |
| 35 | "bugs": { | |
| 36 | "url": "https://github.com/holepunchto/rabin-stream/issues" | |
| 37 | }, | |
| 38 | "homepage": "https://github.com/holepunchto/rabin-stream#readme", | |
| 39 | "dependencies": { | |
| 40 | "rabin-native": "^2.0.0", | |
| 41 | "streamx": "^2.23.0" | |
| 42 | }, | |
| 43 | "devDependencies": { | |
| 44 | "bare-fs": "^4.5.4", | |
| 45 | "bare-path": "^3.0.0", | |
| 46 | "brittle": "^3.1.0", | |
| 47 | "lunte": "^1.3.0", | |
| 48 | "prettier": "^3.6.2", | |
| 49 | "prettier-config-holepunch": "^2.0.0" | |
| 50 | } | |
| 51 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,105 @@ |
| 1 | # Rache | |
| 2 | ||
| 3 | A random-eviction cache which imposes a global cache-size limit on all the caches derived from it. | |
| 4 | ||
| 5 | Useful when you have many caches and want to limit their memory usage as a whole, instead of per cache. | |
| 6 | ||
| 7 | The `get` and `set` operations as well as `delete` (and cache eviction) take constant time. | |
| 8 | ||
| 9 | ## Install | |
| 10 | ||
| 11 | ``` | |
| 12 | npm i rache | |
| 13 | ``` | |
| 14 | ||
| 15 | ## Usage | |
| 16 | ||
| 17 | ``` | |
| 18 | const Rache = require('rache') | |
| 19 | ||
| 20 | const cache = new Rache({ maxSize: 3 }) | |
| 21 | const cache2 = cache.sub() | |
| 22 | const cache3 = cache.sub() | |
| 23 | ||
| 24 | cache.set('key', 'value') | |
| 25 | cache2.set('key', 'otherValue') | |
| 26 | cache3.set('some', 'thing') | |
| 27 | ||
| 28 | // cache 1 is a separate cache from cache2 and cache3 | |
| 29 | console.log('cached:', cache.get('key')) // 'value' | |
| 30 | ||
| 31 | // But they share the same global size | |
| 32 | console.log(cache.globalSize, 'of', cache.maxSize) // 3 of 3 | |
| 33 | ||
| 34 | cache.set('key2', 'another value') | |
| 35 | // The cache was full, so one of the existing 3 entries got evicted | |
| 36 | ||
| 37 | console.log(cache.globalSize, 'of', cache.maxSize) // 3 of 3 | |
| 38 | ``` | |
| 39 | ||
| 40 | ## API | |
| 41 | ||
| 42 | #### `const cache = new Rache({ maxSize=65536 })` | |
| 43 | ||
| 44 | Create a new cache. | |
| 45 | ||
| 46 | `maxSize` is the maximum amount of entries globally, across this cache and all derived caches (derived with `cache.sub()` or `Rache.from(cache)`). | |
| 47 | ||
| 48 | #### `const subCache = cache.sub()` | |
| 49 | ||
| 50 | Create a new cache which shares the global memory limit with the original cache. | |
| 51 | ||
| 52 | #### `const aCache = Rache.from(cache?)` | |
| 53 | ||
| 54 | Create a new rache instance. | |
| 55 | ||
| 56 | If an existing cache is passed in, it will create a sub-cache (equivalent to `aCache = cache.sub()`). | |
| 57 | ||
| 58 | Otherwise (if no `cache` or a falsy value is passed in), it will create a new cache (equivalent to `aCache = new Rache()`). | |
| 59 | ||
| 60 | ||
| 61 | #### `cache.globalSize` | |
| 62 | ||
| 63 | The current amount of entries across all caches. | |
| 64 | ||
| 65 | #### `cache.maxSize` | |
| 66 | ||
| 67 | The maximum amount of entries across all caches. | |
| 68 | ||
| 69 | #### `cache.size` | |
| 70 | ||
| 71 | The amount of entries in this specific cache. | |
| 72 | ||
| 73 | #### `cache.set(key, value)` | |
| 74 | ||
| 75 | Set the key to the given value. | |
| 76 | ||
| 77 | If the global cache size was at the limit, a random old entry is evicted from the cache. | |
| 78 | ||
| 79 | #### `cache.delete(key)` | |
| 80 | ||
| 81 | Delete the entry corresponding to `key`, if any. | |
| 82 | ||
| 83 | Returns `true` if an entry was deleted, `false` otherwise. | |
| 84 | ||
| 85 | #### `cache.get(key)` | |
| 86 | ||
| 87 | Returns the value corresponding to the given key, or `undefined` if there is none. | |
| 88 | ||
| 89 | #### `cache.keys()` | |
| 90 | ||
| 91 | Returns an iterator over the keys of this particular cache. | |
| 92 | ||
| 93 | #### `cache.values()` | |
| 94 | ||
| 95 | Returns an iterator over the values of this particular cache. | |
| 96 | ||
| 97 | #### `cache.clear()` | |
| 98 | ||
| 99 | Clear all entries from this particular cache. | |
| 100 | ||
| 101 | After clearing, new entries can again be added to the cache. | |
| 102 | ||
| 103 | #### `cache.destroy()` | |
| 104 | ||
| 105 | Destroys the cache. The cache should no longer be used after it has been destroyed. |
| … | … | @@ -0,0 +1,119 @@ |
| 1 | class CacheEntry { | |
| 2 | constructor (key, index, map) { | |
| 3 | this.key = key | |
| 4 | this.index = index | |
| 5 | this.map = map | |
| 6 | } | |
| 7 | } | |
| 8 | ||
| 9 | class CacheValue { | |
| 10 | constructor (entry, value) { | |
| 11 | this.entry = entry | |
| 12 | this.value = value | |
| 13 | } | |
| 14 | } | |
| 15 | ||
| 16 | class Rache { | |
| 17 | constructor ({ maxSize = 65536, parent = null } = {}) { | |
| 18 | this.maxSize = parent?.maxSize || maxSize | |
| 19 | ||
| 20 | this._array = parent?._array || [] | |
| 21 | this._map = new Map() | |
| 22 | } | |
| 23 | ||
| 24 | static from (cache) { | |
| 25 | return cache ? new this({ parent: cache }) : new this() | |
| 26 | } | |
| 27 | ||
| 28 | get globalSize () { | |
| 29 | return this._array.length | |
| 30 | } | |
| 31 | ||
| 32 | get size () { | |
| 33 | return this._map.size | |
| 34 | } | |
| 35 | ||
| 36 | sub () { | |
| 37 | return new Rache({ parent: this }) | |
| 38 | } | |
| 39 | ||
| 40 | set (key, value) { // ~constant time | |
| 41 | const existing = this._map.get(key) | |
| 42 | if (existing !== undefined) { | |
| 43 | existing.value = value | |
| 44 | return | |
| 45 | } | |
| 46 | ||
| 47 | if (this._array.length >= this.maxSize) this._gc() | |
| 48 | ||
| 49 | const entry = new CacheEntry(key, this._array.length, this._map) | |
| 50 | this._array.push(entry) | |
| 51 | const cacheValue = new CacheValue(entry, value) | |
| 52 | this._map.set(key, cacheValue) | |
| 53 | } | |
| 54 | ||
| 55 | delete (key) { | |
| 56 | const existing = this._map.get(key) | |
| 57 | if (existing === undefined) return false | |
| 58 | ||
| 59 | this._delete(existing.entry.index) | |
| 60 | return true | |
| 61 | } | |
| 62 | ||
| 63 | get (key) { | |
| 64 | const existing = this._map.get(key) | |
| 65 | return existing === undefined ? undefined : existing.value | |
| 66 | } | |
| 67 | ||
| 68 | * [Symbol.iterator] () { | |
| 69 | for (const [key, { value }] of this._map) { | |
| 70 | yield [key, value] | |
| 71 | } | |
| 72 | } | |
| 73 | ||
| 74 | keys () { | |
| 75 | return this._map.keys() | |
| 76 | } | |
| 77 | ||
| 78 | * values () { | |
| 79 | for (const { value } of this._map.values()) { | |
| 80 | yield value | |
| 81 | } | |
| 82 | } | |
| 83 | ||
| 84 | clear () { | |
| 85 | // The entries in map linger on in _array, | |
| 86 | // so on top of clearing the map, we also kill the ref, | |
| 87 | // so that any gc running later on the old map won't interfere | |
| 88 | // (in case a new entry was added with the same key as a cleared entry) | |
| 89 | ||
| 90 | this._map.clear() | |
| 91 | this._map = new Map() | |
| 92 | } | |
| 93 | ||
| 94 | destroy () { | |
| 95 | this._map = null | |
| 96 | this._array = null | |
| 97 | } | |
| 98 | ||
| 99 | _gc () { | |
| 100 | this._delete(Math.floor(Math.random() * this._array.length)) | |
| 101 | } | |
| 102 | ||
| 103 | _delete (index) { // ~constant time | |
| 104 | if (index >= this._array.length) throw new Error('Cannot delete unused index (logic bug?)') | |
| 105 | ||
| 106 | const head = this._array.pop() | |
| 107 | let removed = head | |
| 108 | ||
| 109 | if (index < this._array.length) { | |
| 110 | removed = this._array[index] | |
| 111 | head.index = index | |
| 112 | this._array[index] = head | |
| 113 | } | |
| 114 | ||
| 115 | removed.map.delete(removed.key) | |
| 116 | } | |
| 117 | } | |
| 118 | ||
| 119 | module.exports = Rache |
| … | … | @@ -0,0 +1,27 @@ |
| 1 | { | |
| 2 | "name": "rache", | |
| 3 | "version": "1.0.0", | |
| 4 | "description": "Random-eviction cache", | |
| 5 | "main": "index.js", | |
| 6 | "scripts": { | |
| 7 | "test": "standard && brittle test.js --coverage" | |
| 8 | }, | |
| 9 | "repository": { | |
| 10 | "type": "git", | |
| 11 | "url": "git+https://github.com/holepunchto/rache.git" | |
| 12 | }, | |
| 13 | "author": "Holepunch", | |
| 14 | "license": "Apache-2.0", | |
| 15 | "bugs": { | |
| 16 | "url": "https://github.com/holepunchto/rache/issues" | |
| 17 | }, | |
| 18 | "homepage": "https://github.com/holepunchto/rache#readme", | |
| 19 | "files": [ | |
| 20 | "index.js" | |
| 21 | ], | |
| 22 | "dependencies": {}, | |
| 23 | "devDependencies": { | |
| 24 | "brittle": "^3.5.2", | |
| 25 | "standard": "^17.1.0" | |
| 26 | } | |
| 27 | } |
| … | … | @@ -0,0 +1,97 @@ |
| 1 | # random-access-file | |
| 2 | ||
| 3 | Continuous reading or writing to a file using random offsets and lengths | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install random-access-file | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Why? | |
| 10 | ||
| 11 | If you are receiving a file in multiple pieces in a distributed system it can be useful to write these pieces to disk one by one in various places throughout the file without having to open and close a file descriptor all the time. | |
| 12 | ||
| 13 | random-access-file allows you to do just this. | |
| 14 | ||
| 15 | ## Usage | |
| 16 | ||
| 17 | ``` js | |
| 18 | const RandomAccessFile = require('random-access-file') | |
| 19 | ||
| 20 | const file = new RandomAccessFile('my-file.txt') | |
| 21 | ||
| 22 | file.write(10, Buffer.from('hello'), function(err) { | |
| 23 | // write a buffer to offset 10 | |
| 24 | file.read(10, 5, function(err, buffer) { | |
| 25 | console.log(buffer) // read 5 bytes from offset 10 | |
| 26 | file.close(function() { | |
| 27 | console.log('file is closed') | |
| 28 | }) | |
| 29 | }) | |
| 30 | }) | |
| 31 | ``` | |
| 32 | ||
| 33 | file will use an open file descriptor. When you are done with the file you should call `file.close()`. | |
| 34 | ||
| 35 | ## API | |
| 36 | ||
| 37 | #### `const file = new RandomAccessFile(filename, [options])` | |
| 38 | ||
| 39 | Create a new file. Options include: | |
| 40 | ||
| 41 | ``` js | |
| 42 | { | |
| 43 | truncate: false, // truncate the file before reading / writing | |
| 44 | size: someSize, // truncate the file to this size first | |
| 45 | readable: true, // should the file be opened as readable? | |
| 46 | writable: true, // should the file be opened as writable? | |
| 47 | lock: false, // lock the file | |
| 48 | sparse: false // mark the file as sparse | |
| 49 | } | |
| 50 | ``` | |
| 51 | ||
| 52 | #### `file.write(offset, buffer, [callback])` | |
| 53 | ||
| 54 | Write a buffer at a specific offset. | |
| 55 | ||
| 56 | #### `file.read(offset, length, callback)` | |
| 57 | ||
| 58 | Read a buffer at a specific offset. Callback is called with the buffer read. | |
| 59 | ||
| 60 | #### `file.del(offset, length, callback)` | |
| 61 | ||
| 62 | Delete a portion of the file. Any partial file blocks in the deleted portion are zeroed and, if the file is sparse, the remaining file blocks unlinked in-place. | |
| 63 | ||
| 64 | #### `file.truncate(offset, callback)` | |
| 65 | ||
| 66 | Truncate the file length to this offset. | |
| 67 | ||
| 68 | #### `file.stat(callback)` | |
| 69 | ||
| 70 | Stat the storage. Should return an object with useful information about the underlying storage, including: | |
| 71 | ||
| 72 | ```js | |
| 73 | { | |
| 74 | size: number // how many bytes of data is stored? | |
| 75 | } | |
| 76 | ``` | |
| 77 | ||
| 78 | #### `file.close([callback])` | |
| 79 | ||
| 80 | Close the underlying file descriptor. | |
| 81 | ||
| 82 | #### `file.unlink([callback])` | |
| 83 | ||
| 84 | Unlink the underlying file. | |
| 85 | ||
| 86 | #### `file.on('open')` | |
| 87 | ||
| 88 | Emitted when the file descriptor has been opened. You can access the fd using `file.fd`. | |
| 89 | You do not need to wait for this event before doing any reads/writes. | |
| 90 | ||
| 91 | #### `file.on('close')` | |
| 92 | ||
| 93 | Emitted when the file has been closed. | |
| 94 | ||
| 95 | ## License | |
| 96 | ||
| 97 | MIT |
| … | … | @@ -0,0 +1,3 @@ |
| 1 | module.exports = function () { | |
| 2 | throw new Error('random-access-file is not supported in the browser') | |
| 3 | } |
| … | … | @@ -0,0 +1,255 @@ |
| 1 | const RandomAccessStorage = require('random-access-storage') | |
| 2 | const fs = require('fs') | |
| 3 | const path = require('path') | |
| 4 | const constants = fs.constants | |
| 5 | ||
| 6 | let fsext = null | |
| 7 | try { | |
| 8 | fsext = require('fs-native-extensions') | |
| 9 | } catch {} | |
| 10 | ||
| 11 | const RDWR = constants.O_RDWR | |
| 12 | const RDONLY = constants.O_RDONLY | |
| 13 | const WRONLY = constants.O_WRONLY | |
| 14 | const CREAT = constants.O_CREAT | |
| 15 | ||
| 16 | class Pool { | |
| 17 | constructor (maxSize) { | |
| 18 | this.maxSize = maxSize | |
| 19 | this.active = [] | |
| 20 | } | |
| 21 | ||
| 22 | _onactive (file) { | |
| 23 | // suspend a random one when the pool | |
| 24 | if (this.active.length >= this.maxSize) { | |
| 25 | const r = Math.floor(Math.random() * this.active.length) | |
| 26 | this.active[r].suspend() | |
| 27 | } | |
| 28 | ||
| 29 | file._pi = this.active.push(file) - 1 | |
| 30 | } | |
| 31 | ||
| 32 | _oninactive (file) { | |
| 33 | const head = this.active.pop() | |
| 34 | if (head !== file) { | |
| 35 | head._pi = file._pi | |
| 36 | this.active[head._pi] = head | |
| 37 | } | |
| 38 | } | |
| 39 | } | |
| 40 | ||
| 41 | module.exports = class RandomAccessFile extends RandomAccessStorage { | |
| 42 | constructor (filename, opts = {}) { | |
| 43 | const size = opts.size || (opts.truncate ? 0 : -1) | |
| 44 | ||
| 45 | super() | |
| 46 | ||
| 47 | if (opts.directory) filename = path.join(opts.directory, path.resolve('/', filename).replace(/^\w+:\\/, '')) | |
| 48 | ||
| 49 | this.directory = opts.directory || null | |
| 50 | this.filename = filename | |
| 51 | this.fd = 0 | |
| 52 | ||
| 53 | const { | |
| 54 | readable = true, | |
| 55 | writable = true | |
| 56 | } = opts | |
| 57 | ||
| 58 | this.mode = readable && writable ? RDWR : (readable ? RDONLY : WRONLY) | |
| 59 | ||
| 60 | this._pi = 0 // pool index | |
| 61 | this._pool = opts.pool || null | |
| 62 | this._size = size | |
| 63 | this._rmdir = !!opts.rmdir | |
| 64 | this._lock = opts.lock === true | |
| 65 | this._sparse = opts.sparse === true | |
| 66 | this._alloc = opts.alloc || Buffer.allocUnsafe | |
| 67 | this._alwaysCreate = size >= 0 | |
| 68 | } | |
| 69 | ||
| 70 | static createPool (maxSize) { | |
| 71 | return new Pool(maxSize) | |
| 72 | } | |
| 73 | ||
| 74 | _open (req) { | |
| 75 | const create = this._alwaysCreate || this.writing // .writing comes from RAS | |
| 76 | const self = this | |
| 77 | const mode = this.mode | (create ? CREAT : 0) | |
| 78 | ||
| 79 | if (create) fs.mkdir(path.dirname(this.filename), { recursive: true }, ondir) | |
| 80 | else ondir(null) | |
| 81 | ||
| 82 | function ondir (err) { | |
| 83 | if (err) return req.callback(err) | |
| 84 | fs.open(self.filename, mode, onopen) | |
| 85 | } | |
| 86 | ||
| 87 | function onopen (err, fd) { | |
| 88 | if (err) return onerror(err) | |
| 89 | ||
| 90 | self.fd = fd | |
| 91 | ||
| 92 | if (!self._lock || !fsext) return onlock(null) | |
| 93 | ||
| 94 | // Should we aquire a read lock? | |
| 95 | const shared = self.mode === RDONLY | |
| 96 | ||
| 97 | if (fsext.tryLock(self.fd, { shared })) onlock(null) | |
| 98 | else onlock(createLockError(self.filename)) | |
| 99 | } | |
| 100 | ||
| 101 | function onlock (err) { | |
| 102 | if (err) return onerrorafteropen(err) | |
| 103 | ||
| 104 | if (!self._sparse || !fsext || self.mode === RDONLY) return onsparse(null) | |
| 105 | ||
| 106 | fsext.sparse(self.fd).then(onsparse, onsparse) | |
| 107 | } | |
| 108 | ||
| 109 | function onsparse (err) { | |
| 110 | if (err) return onerrorafteropen(err) | |
| 111 | ||
| 112 | if (self._size < 0) return ontruncate(null) | |
| 113 | ||
| 114 | fs.ftruncate(self.fd, self._size, ontruncate) | |
| 115 | } | |
| 116 | ||
| 117 | function ontruncate (err) { | |
| 118 | if (err) return onerrorafteropen(err) | |
| 119 | if (self._pool !== null) self._pool._onactive(self) | |
| 120 | req.callback(null) | |
| 121 | } | |
| 122 | ||
| 123 | function onerror (err) { | |
| 124 | req.callback(err) | |
| 125 | } | |
| 126 | ||
| 127 | function onerrorafteropen (err) { | |
| 128 | fs.close(self.fd, function () { | |
| 129 | self.fd = 0 | |
| 130 | onerror(err) | |
| 131 | }) | |
| 132 | } | |
| 133 | } | |
| 134 | ||
| 135 | _write (req) { | |
| 136 | const data = req.data | |
| 137 | const fd = this.fd | |
| 138 | ||
| 139 | fs.write(fd, data, 0, req.size, req.offset, onwrite) | |
| 140 | ||
| 141 | function onwrite (err, wrote) { | |
| 142 | if (err) return req.callback(err) | |
| 143 | ||
| 144 | req.size -= wrote | |
| 145 | req.offset += wrote | |
| 146 | ||
| 147 | if (!req.size) return req.callback(null) | |
| 148 | fs.write(fd, data, data.length - req.size, req.size, req.offset, onwrite) | |
| 149 | } | |
| 150 | } | |
| 151 | ||
| 152 | _read (req) { | |
| 153 | const self = this | |
| 154 | const data = req.data || this._alloc(req.size) | |
| 155 | const fd = this.fd | |
| 156 | ||
| 157 | if (!req.size) return process.nextTick(readEmpty, req) | |
| 158 | fs.read(fd, data, 0, req.size, req.offset, onread) | |
| 159 | ||
| 160 | function onread (err, read) { | |
| 161 | if (err) return req.callback(err) | |
| 162 | if (!read) return req.callback(createReadError(self.filename, req.offset, req.size)) | |
| 163 | ||
| 164 | req.size -= read | |
| 165 | req.offset += read | |
| 166 | ||
| 167 | if (!req.size) return req.callback(null, data) | |
| 168 | fs.read(fd, data, data.length - req.size, req.size, req.offset, onread) | |
| 169 | } | |
| 170 | } | |
| 171 | ||
| 172 | _del (req) { | |
| 173 | if (req.size === Infinity) return this._truncate(req) // TODO: remove this when all callsites use truncate | |
| 174 | ||
| 175 | if (!fsext) return req.callback(null) | |
| 176 | ||
| 177 | fsext.trim(this.fd, req.offset, req.size).then(ontrim, ontrim) | |
| 178 | ||
| 179 | function ontrim (err) { | |
| 180 | req.callback(err) | |
| 181 | } | |
| 182 | } | |
| 183 | ||
| 184 | _truncate (req) { | |
| 185 | fs.ftruncate(this.fd, req.offset, ontruncate) | |
| 186 | ||
| 187 | function ontruncate (err) { | |
| 188 | req.callback(err) | |
| 189 | } | |
| 190 | } | |
| 191 | ||
| 192 | _stat (req) { | |
| 193 | fs.fstat(this.fd, onstat) | |
| 194 | ||
| 195 | function onstat (err, st) { | |
| 196 | req.callback(err, st) | |
| 197 | } | |
| 198 | } | |
| 199 | ||
| 200 | _close (req) { | |
| 201 | const self = this | |
| 202 | ||
| 203 | fs.close(this.fd, onclose) | |
| 204 | ||
| 205 | function onclose (err) { | |
| 206 | if (err) return req.callback(err) | |
| 207 | if (self._pool !== null) self._pool._oninactive(self) | |
| 208 | self.fd = 0 | |
| 209 | req.callback(null) | |
| 210 | } | |
| 211 | } | |
| 212 | ||
| 213 | _unlink (req) { | |
| 214 | const self = this | |
| 215 | ||
| 216 | const root = this.directory && path.resolve(path.join(this.directory, '.')) | |
| 217 | let dir = path.resolve(path.dirname(this.filename)) | |
| 218 | ||
| 219 | fs.unlink(this.filename, onunlink) | |
| 220 | ||
| 221 | function onunlink (err) { | |
| 222 | // if the file isn't there, its already unlinked, ignore | |
| 223 | if (err && err.code === 'ENOENT') err = null | |
| 224 | ||
| 225 | if (err || !self._rmdir || !root || dir === root) return req.callback(err) | |
| 226 | fs.rmdir(dir, onrmdir) | |
| 227 | } | |
| 228 | ||
| 229 | function onrmdir (err) { | |
| 230 | dir = path.join(dir, '..') | |
| 231 | if (err || dir === root) return req.callback(null) | |
| 232 | fs.rmdir(dir, onrmdir) | |
| 233 | } | |
| 234 | } | |
| 235 | } | |
| 236 | ||
| 237 | function readEmpty (req) { | |
| 238 | req.callback(null, Buffer.alloc(0)) | |
| 239 | } | |
| 240 | ||
| 241 | function createLockError (path) { | |
| 242 | const err = new Error('ELOCKED: File is locked') | |
| 243 | err.code = 'ELOCKED' | |
| 244 | err.path = path | |
| 245 | return err | |
| 246 | } | |
| 247 | ||
| 248 | function createReadError (path, offset, size) { | |
| 249 | const err = new Error('EPARTIALREAD: Could not satisfy length') | |
| 250 | err.code = 'EPARTIALREAD' | |
| 251 | err.path = path | |
| 252 | err.offset = offset | |
| 253 | err.size = size | |
| 254 | return err | |
| 255 | } |
| … | … | @@ -0,0 +1,46 @@ |
| 1 | { | |
| 2 | "name": "random-access-file", | |
| 3 | "version": "4.1.2", | |
| 4 | "description": "Continuous reading or writing to a file using random offsets and lengths", | |
| 5 | "main": "index.js", | |
| 6 | "scripts": { | |
| 7 | "test": "standard && brittle test/*.js" | |
| 8 | }, | |
| 9 | "browser": "./browser.js", | |
| 10 | "files": [ | |
| 11 | "index.js", | |
| 12 | "browser.js" | |
| 13 | ], | |
| 14 | "imports": { | |
| 15 | "fs": { | |
| 16 | "bare": "bare-fs", | |
| 17 | "default": "fs" | |
| 18 | }, | |
| 19 | "path": { | |
| 20 | "bare": "bare-path", | |
| 21 | "default": "path" | |
| 22 | } | |
| 23 | }, | |
| 24 | "dependencies": { | |
| 25 | "bare-fs": "^4.0.1", | |
| 26 | "bare-path": "^3.0.0", | |
| 27 | "random-access-storage": "^3.0.0" | |
| 28 | }, | |
| 29 | "optionalDependencies": { | |
| 30 | "fs-native-extensions": "^1.3.1" | |
| 31 | }, | |
| 32 | "devDependencies": { | |
| 33 | "brittle": "^3.3.0", | |
| 34 | "standard": "^17.0.0" | |
| 35 | }, | |
| 36 | "repository": { | |
| 37 | "type": "git", | |
| 38 | "url": "https://github.com/random-access-storage/random-access-file.git" | |
| 39 | }, | |
| 40 | "author": "Mathias Buus (@mafintosh)", | |
| 41 | "license": "MIT", | |
| 42 | "bugs": { | |
| 43 | "url": "https://github.com/random-access-storage/random-access-file/issues" | |
| 44 | }, | |
| 45 | "homepage": "https://github.com/random-access-storage/random-access-file" | |
| 46 | } |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2018 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,266 @@ |
| 1 | # random-access-storage | |
| 2 | ||
| 3 | Easily make random-access-storage instances | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install random-access-storage | |
| 7 | ``` | |
| 8 | ||
| 9 | A random-access-storage instance is a common interface for a storage abstraction, that provides the following core api. | |
| 10 | ||
| 11 | * `read(offset, size)` - Read a buffer at a custom offset. | |
| 12 | * `write(offset, data)` - Write a buffer to a custom offset. | |
| 13 | * `del(offset, size)` - Delete data at a custom offset. | |
| 14 | ||
| 15 | This module exposes a base class that implements most of the plumbing and flow you'd usually want to implement when making one. | |
| 16 | ||
| 17 | ## Usage | |
| 18 | ||
| 19 | ``` js | |
| 20 | const RandomAccessStorage = require('random-access-storage') | |
| 21 | const fs = require('fs') | |
| 22 | ||
| 23 | const file = fileReader('index.js') | |
| 24 | ||
| 25 | file.read(0, 10, (err, buf) => console.log('0-10: ' + buf.toString())) | |
| 26 | file.read(40, 15, (err, buf) => console.log('40-55: ' + buf.toString())) | |
| 27 | file.close() | |
| 28 | ||
| 29 | function fileReader (name) { | |
| 30 | let fd = 0 | |
| 31 | return new RandomAccessStorage({ | |
| 32 | open: function (req) { | |
| 33 | // called once automatically before the first read call | |
| 34 | fs.open(name, 'r', function (err, res) { | |
| 35 | if (err) return req.callback(err) | |
| 36 | fd = res | |
| 37 | req.callback(null) | |
| 38 | }) | |
| 39 | }, | |
| 40 | read: function (req) { | |
| 41 | const buf = Buffer.allocUnsafe(req.size) | |
| 42 | fs.read(fd, buf, 0, buf.length, req.offset, function (err, read) { | |
| 43 | if (err) return req.callback(err) | |
| 44 | if (read < buf.length) return req.callback(new Error('Could not read')) | |
| 45 | req.callback(null, buf) | |
| 46 | }) | |
| 47 | }, | |
| 48 | close: function (req) { | |
| 49 | if (!fd) return req.callback(null) | |
| 50 | fs.close(fd, err => req.callback(err)) | |
| 51 | } | |
| 52 | }) | |
| 53 | } | |
| 54 | ``` | |
| 55 | ||
| 56 | ## API | |
| 57 | ||
| 58 | #### `const storage = new RandomAccessStorage([options])` | |
| 59 | ||
| 60 | Make a new instance. Options include: | |
| 61 | ||
| 62 | ``` js | |
| 63 | { | |
| 64 | createAlways: false, // always create storage on first open | |
| 65 | open: fn, // sets ._open | |
| 66 | read: fn, // sets ._read | |
| 67 | write: fn, // sets ._write | |
| 68 | del: fn, // sets ._del | |
| 69 | truncate: fn, // sets ._truncate | |
| 70 | stat: fn, // sets ._stat | |
| 71 | suspend: fn, // sets ._suspend | |
| 72 | close: fn, // sets ._close | |
| 73 | unlink: fn // sets ._unlink | |
| 74 | } | |
| 75 | ``` | |
| 76 | ||
| 77 | #### `storage.readable` | |
| 78 | ||
| 79 | True if the storage implements `._read`. | |
| 80 | ||
| 81 | #### `storage.writable` | |
| 82 | ||
| 83 | True if the storage implements `._write`. | |
| 84 | ||
| 85 | #### `storage.deletable` | |
| 86 | ||
| 87 | True if the storage implements `._del`. | |
| 88 | ||
| 89 | #### `storage.truncatable` | |
| 90 | ||
| 91 | True if the storage implements `._truncate`. | |
| 92 | ||
| 93 | #### `storage.statable` | |
| 94 | ||
| 95 | True if the storage implements `._stat`. | |
| 96 | ||
| 97 | #### `storage.opened` | |
| 98 | ||
| 99 | True if the storage has been fully opened. | |
| 100 | ||
| 101 | #### `storage.closed` | |
| 102 | ||
| 103 | True if the storage has been fully closed. | |
| 104 | ||
| 105 | #### `storage.unlinked` | |
| 106 | ||
| 107 | True if the storage has been fully unlinked. | |
| 108 | ||
| 109 | #### `storage.writing` | |
| 110 | ||
| 111 | True if the storage is currently being written to. | |
| 112 | ||
| 113 | #### `storage.on('open')` | |
| 114 | ||
| 115 | Emitted when the storage is fully open. | |
| 116 | ||
| 117 | #### `storage.on('close')` | |
| 118 | ||
| 119 | Emitted when the storage is fully closed. | |
| 120 | ||
| 121 | #### `storage.on('unlink')` | |
| 122 | ||
| 123 | Emitted when the storage is fully unlinked. | |
| 124 | ||
| 125 | #### `storage.on('suspend')` | |
| 126 | ||
| 127 | Emitted when the storage is fully suspended | |
| 128 | ||
| 129 | #### `storage.on('unsuspend')` | |
| 130 | ||
| 131 | Emitted when the storage comes out of suspension. | |
| 132 | ||
| 133 | #### `storage.open(cb)` | |
| 134 | ||
| 135 | Explicitly open the storage. If you do not call this yourself, | |
| 136 | it will automatically called before any read/write/del/stat operation. | |
| 137 | ||
| 138 | It is safe to call this more than once. | |
| 139 | ||
| 140 | Triggers *one* call to `_open` if you implement that. | |
| 141 | ||
| 142 | #### `storage._open(req)` | |
| 143 | ||
| 144 | Implement storage open. | |
| 145 | ||
| 146 | * `req.create` is `true` if the storage should be created. | |
| 147 | ||
| 148 | Call `req.callback` when it is fully opened. | |
| 149 | ||
| 150 | #### `storage.read(offset, size, callback)` | |
| 151 | ||
| 152 | Read the specified bytes from the storage at the specified byte offset. | |
| 153 | Calls the callback with a `(err, buffer)`. | |
| 154 | ||
| 155 | #### `storage._read(req)` | |
| 156 | ||
| 157 | Implement storage read. | |
| 158 | ||
| 159 | * `req.offset` contains the byte offset you should read at. | |
| 160 | * `req.size` contains the amount of bytes you should read. | |
| 161 | ||
| 162 | Call `req.callback(err, buffer)` when the read is completed. | |
| 163 | ||
| 164 | Note that this is guaranteed to run after the storage has been opened and not after it has been closed. | |
| 165 | ||
| 166 | #### `storage.write(offset, buffer, [callback])` | |
| 167 | ||
| 168 | Write the specified buffer to the specified byte offset. Optionally pass a callback that is called with `(err)` when the write has completed. | |
| 169 | ||
| 170 | #### `storage._write(req)` | |
| 171 | ||
| 172 | Implement storage write. | |
| 173 | ||
| 174 | * `req.offset` contains the byte offset you should write at. | |
| 175 | * `req.data` contains the buffer you should write. | |
| 176 | ||
| 177 | Call `req.callback(err)` when the write is completed. | |
| 178 | ||
| 179 | Note that this is guaranteed to run after the storage has been opened and not after it has been closed. | |
| 180 | ||
| 181 | #### `storage.del(offset, size, [callback])` | |
| 182 | ||
| 183 | Delete the specified amount of bytes at the specified offset. Optionally pass a callback that is called with `(err)` when the delete has completed. | |
| 184 | ||
| 185 | #### `storage._del(req)` | |
| 186 | ||
| 187 | Implement storage delete. | |
| 188 | ||
| 189 | * `req.offset` contains the byte offset to delete at. | |
| 190 | * `req.size` contains the amount of bytes to delete. | |
| 191 | ||
| 192 | Call `req.callback(err)` when the delete has completed. | |
| 193 | ||
| 194 | Note that this is guaranteed to run after the storage has been opened and not after it has been closed. | |
| 195 | ||
| 196 | #### `storage.truncate(offset, [callback])` | |
| 197 | ||
| 198 | Truncate the storage at the specified offset. Optionally pass a callback that is called with `(err)` when the truncate has completed. | |
| 199 | ||
| 200 | #### `storage._truncate(req)` | |
| 201 | ||
| 202 | Implement storage truncate. Defaults to `storage._del(req)`. | |
| 203 | ||
| 204 | * `req.offset` contains the byte offset to truncate at. | |
| 205 | ||
| 206 | Call `req.callback(err)` when the truncate has completed. | |
| 207 | ||
| 208 | Note that this is guaranteed to run after the storage has been opened and not after it has been closed. | |
| 209 | ||
| 210 | #### `storage.stat(callback)` | |
| 211 | ||
| 212 | Stat the storage. Should return an object with useful information about the underlying storage, including: | |
| 213 | ||
| 214 | ``` | |
| 215 | { | |
| 216 | size: number // how many bytes of data is stored? | |
| 217 | } | |
| 218 | ``` | |
| 219 | ||
| 220 | #### `storage._stat(req)` | |
| 221 | ||
| 222 | Implement storage stat. | |
| 223 | ||
| 224 | Call `req.callback(err, statObject)` when the stat has completed. | |
| 225 | ||
| 226 | Note that this is guaranteed to run after the storage has been opened and not after it has been closed. | |
| 227 | ||
| 228 | #### `storage.suspend([callback])` | |
| 229 | ||
| 230 | Suspend (temporarily close) the storage instance. | |
| 231 | ||
| 232 | #### `storage._suspend(req)` | |
| 233 | ||
| 234 | Implement storage suspend. Defaults to calling `_close`. | |
| 235 | ||
| 236 | Optionally implement this to add a way for your storage instance to temporarily free resources. | |
| 237 | ||
| 238 | Call `req.callback(err)` when the storage has been fully suspended. | |
| 239 | ||
| 240 | #### `storage.close([callback])` | |
| 241 | ||
| 242 | Close the storage instance. | |
| 243 | ||
| 244 | #### `storage._close(req)` | |
| 245 | ||
| 246 | Implement storage close. | |
| 247 | ||
| 248 | Call `req.callback(err)` when the storage is fully closed. | |
| 249 | ||
| 250 | Note this is guaranteed to run after all pending read/write/stat/del operations has finished and no methods will run after. | |
| 251 | ||
| 252 | #### `storage.unlink([callback])` | |
| 253 | ||
| 254 | Unlink the storage instance, removing all underlying data. | |
| 255 | ||
| 256 | #### `storage._unlink(req)` | |
| 257 | ||
| 258 | Implement storage unlink. | |
| 259 | ||
| 260 | Call `req.callback(err)` when the storage has been fully unlinked. | |
| 261 | ||
| 262 | Note this is guaranteed to run after `.close()` has been called and no methods will be run after. | |
| 263 | ||
| 264 | ## License | |
| 265 | ||
| 266 | MIT |
| … | … | @@ -0,0 +1,333 @@ |
| 1 | const EventEmitter = require('events') | |
| 2 | const queueTick = require('queue-tick') | |
| 3 | ||
| 4 | const NOT_READABLE = defaultImpl(new Error('Not readable')) | |
| 5 | const NOT_WRITABLE = defaultImpl(new Error('Not writable')) | |
| 6 | const NOT_DELETABLE = defaultImpl(new Error('Not deletable')) | |
| 7 | const NOT_STATABLE = defaultImpl(new Error('Not statable')) | |
| 8 | ||
| 9 | const DEFAULT_OPEN = defaultImpl(null) | |
| 10 | const DEFAULT_CLOSE = defaultImpl(null) | |
| 11 | const DEFAULT_UNLINK = defaultImpl(null) | |
| 12 | ||
| 13 | // NON_BLOCKING_OPS | |
| 14 | const READ_OP = 0 | |
| 15 | const WRITE_OP = 1 | |
| 16 | const DEL_OP = 2 | |
| 17 | const TRUNCATE_OP = 3 | |
| 18 | const STAT_OP = 4 | |
| 19 | ||
| 20 | // BLOCKING_OPS | |
| 21 | const OPEN_OP = 5 | |
| 22 | const SUSPEND_OP = 6 | |
| 23 | const CLOSE_OP = 7 | |
| 24 | const UNLINK_OP = 8 | |
| 25 | ||
| 26 | module.exports = class RandomAccessStorage extends EventEmitter { | |
| 27 | constructor (opts) { | |
| 28 | super() | |
| 29 | ||
| 30 | this._queued = [] | |
| 31 | this._pending = 0 | |
| 32 | this._needsOpen = true | |
| 33 | ||
| 34 | this.opened = false | |
| 35 | this.suspended = false | |
| 36 | this.closed = false | |
| 37 | this.unlinked = false | |
| 38 | this.writing = false | |
| 39 | ||
| 40 | if (opts) { | |
| 41 | if (opts.open) this._open = opts.open | |
| 42 | if (opts.read) this._read = opts.read | |
| 43 | if (opts.write) this._write = opts.write | |
| 44 | if (opts.del) this._del = opts.del | |
| 45 | if (opts.truncate) this._truncate = opts.truncate | |
| 46 | if (opts.stat) this._stat = opts.stat | |
| 47 | if (opts.suspend) this._suspend = opts.suspend | |
| 48 | if (opts.close) this._close = opts.close | |
| 49 | if (opts.unlink) this._unlink = opts.unlink | |
| 50 | } | |
| 51 | ||
| 52 | this.readable = this._read !== RandomAccessStorage.prototype._read | |
| 53 | this.writable = this._write !== RandomAccessStorage.prototype._write | |
| 54 | this.deletable = this._del !== RandomAccessStorage.prototype._del | |
| 55 | this.truncatable = this._truncate !== RandomAccessStorage.prototype._truncate || this.deletable | |
| 56 | this.statable = this._stat !== RandomAccessStorage.prototype._stat | |
| 57 | } | |
| 58 | ||
| 59 | read (offset, size, cb) { | |
| 60 | this.run(new Request(this, READ_OP, offset, size, null, cb), false) | |
| 61 | } | |
| 62 | ||
| 63 | _read (req) { | |
| 64 | return NOT_READABLE(req) | |
| 65 | } | |
| 66 | ||
| 67 | write (offset, data, cb) { | |
| 68 | if (!cb) cb = noop | |
| 69 | this.run(new Request(this, WRITE_OP, offset, data.length, data, cb), true) | |
| 70 | } | |
| 71 | ||
| 72 | _write (req) { | |
| 73 | return NOT_WRITABLE(req) | |
| 74 | } | |
| 75 | ||
| 76 | del (offset, size, cb) { | |
| 77 | if (!cb) cb = noop | |
| 78 | this.run(new Request(this, DEL_OP, offset, size, null, cb), true) | |
| 79 | } | |
| 80 | ||
| 81 | _del (req) { | |
| 82 | return NOT_DELETABLE(req) | |
| 83 | } | |
| 84 | ||
| 85 | truncate (offset, cb) { | |
| 86 | if (!cb) cb = noop | |
| 87 | this.run(new Request(this, TRUNCATE_OP, offset, 0, null, cb), true) | |
| 88 | } | |
| 89 | ||
| 90 | _truncate (req) { | |
| 91 | req.size = Infinity | |
| 92 | this._del(req) | |
| 93 | } | |
| 94 | ||
| 95 | stat (cb) { | |
| 96 | this.run(new Request(this, STAT_OP, 0, 0, null, cb), false) | |
| 97 | } | |
| 98 | ||
| 99 | _stat (req) { | |
| 100 | return NOT_STATABLE(req) | |
| 101 | } | |
| 102 | ||
| 103 | open (cb) { | |
| 104 | if (!cb) cb = noop | |
| 105 | if (this.opened && !this._needsOpen) return nextTickCallback(cb) | |
| 106 | this._needsOpen = false | |
| 107 | queueAndRun(this, new Request(this, OPEN_OP, 0, 0, null, cb)) | |
| 108 | } | |
| 109 | ||
| 110 | _open (req) { | |
| 111 | return DEFAULT_OPEN(req) | |
| 112 | } | |
| 113 | ||
| 114 | suspend (cb) { | |
| 115 | if (!cb) cb = noop | |
| 116 | if (this.closed || this.suspended) return nextTickCallback(cb) | |
| 117 | this._needsOpen = true | |
| 118 | queueAndRun(this, new Request(this, SUSPEND_OP, 0, 0, null, cb)) | |
| 119 | } | |
| 120 | ||
| 121 | _suspend (req) { | |
| 122 | this._close(req) | |
| 123 | } | |
| 124 | ||
| 125 | close (cb) { | |
| 126 | if (!cb) cb = noop | |
| 127 | if (this.closed) return nextTickCallback(cb) | |
| 128 | queueAndRun(this, new Request(this, CLOSE_OP, 0, 0, null, cb)) | |
| 129 | } | |
| 130 | ||
| 131 | _close (req) { | |
| 132 | return DEFAULT_CLOSE(req) | |
| 133 | } | |
| 134 | ||
| 135 | unlink (cb) { | |
| 136 | if (!cb) cb = noop | |
| 137 | if (!this.closed) this.close(noop) | |
| 138 | queueAndRun(this, new Request(this, UNLINK_OP, 0, 0, null, cb)) | |
| 139 | } | |
| 140 | ||
| 141 | _unlink (req) { | |
| 142 | return DEFAULT_UNLINK(req) | |
| 143 | } | |
| 144 | ||
| 145 | run (req, writing) { | |
| 146 | if (writing && !this.writing) { | |
| 147 | this.writing = true | |
| 148 | this._needsOpen = true | |
| 149 | } | |
| 150 | ||
| 151 | if (this._needsOpen) this.open(noop) | |
| 152 | if (this._queued.length) this._queued.push(req) | |
| 153 | else req._run() | |
| 154 | } | |
| 155 | } | |
| 156 | ||
| 157 | class Request { | |
| 158 | constructor (self, type, offset, size, data, cb) { | |
| 159 | this.type = type | |
| 160 | this.offset = offset | |
| 161 | this.size = size | |
| 162 | this.data = data | |
| 163 | this.storage = self | |
| 164 | ||
| 165 | this._sync = false | |
| 166 | this._callback = cb | |
| 167 | this._openError = null | |
| 168 | } | |
| 169 | ||
| 170 | _maybeOpenError (err) { | |
| 171 | if (this.type !== OPEN_OP) return | |
| 172 | const queued = this.storage._queued | |
| 173 | for (let i = 1; i < queued.length; i++) { | |
| 174 | const q = queued[i] | |
| 175 | if (q.type === OPEN_OP) break | |
| 176 | q._openError = err | |
| 177 | } | |
| 178 | } | |
| 179 | ||
| 180 | _unqueue (err) { | |
| 181 | const ra = this.storage | |
| 182 | const queued = ra._queued | |
| 183 | ||
| 184 | if (err) { | |
| 185 | this._maybeOpenError(err) | |
| 186 | } else if (this.type > 4) { | |
| 187 | switch (this.type) { | |
| 188 | case OPEN_OP: | |
| 189 | if (ra.suspended) { | |
| 190 | ra.suspended = false | |
| 191 | ra.emit('unsuspend') | |
| 192 | } | |
| 193 | if (!ra.opened) { | |
| 194 | ra.opened = true | |
| 195 | ra.emit('open') | |
| 196 | } | |
| 197 | break | |
| 198 | ||
| 199 | case SUSPEND_OP: | |
| 200 | if (!ra.suspended) { | |
| 201 | ra.suspended = true | |
| 202 | ra.emit('suspend') | |
| 203 | } | |
| 204 | break | |
| 205 | ||
| 206 | case CLOSE_OP: | |
| 207 | if (!ra.closed) { | |
| 208 | ra.closed = true | |
| 209 | ra.emit('close') | |
| 210 | } | |
| 211 | break | |
| 212 | ||
| 213 | case UNLINK_OP: | |
| 214 | if (!ra.unlinked) { | |
| 215 | ra.unlinked = true | |
| 216 | ra.emit('unlink') | |
| 217 | } | |
| 218 | break | |
| 219 | } | |
| 220 | } | |
| 221 | ||
| 222 | if (queued.length && queued[0] === this) queued.shift() | |
| 223 | ||
| 224 | if (!--ra._pending) drainQueue(ra) | |
| 225 | } | |
| 226 | ||
| 227 | callback (err, val) { | |
| 228 | if (this._sync) return nextTick(this, err, val) | |
| 229 | this._unqueue(err) | |
| 230 | this._callback(err, val) | |
| 231 | } | |
| 232 | ||
| 233 | _openAndNotClosed () { | |
| 234 | const ra = this.storage | |
| 235 | if (ra.opened && !ra.closed && !ra.suspended) return true | |
| 236 | if (!ra.opened || ra.suspended) nextTick(this, this._openError || new Error('Not opened')) | |
| 237 | else if (ra.closed) nextTick(this, new Error('Closed')) | |
| 238 | return false | |
| 239 | } | |
| 240 | ||
| 241 | _open () { | |
| 242 | const ra = this.storage | |
| 243 | ||
| 244 | if (ra.opened && !ra.suspended) return nextTick(this, null) | |
| 245 | if (ra.closed) return nextTick(this, new Error('Closed')) | |
| 246 | ||
| 247 | ra._open(this) | |
| 248 | } | |
| 249 | ||
| 250 | _run () { | |
| 251 | const ra = this.storage | |
| 252 | ra._pending++ | |
| 253 | ||
| 254 | this._sync = true | |
| 255 | ||
| 256 | switch (this.type) { | |
| 257 | case READ_OP: | |
| 258 | if (this._openAndNotClosed()) ra._read(this) | |
| 259 | break | |
| 260 | ||
| 261 | case WRITE_OP: | |
| 262 | if (this._openAndNotClosed()) ra._write(this) | |
| 263 | break | |
| 264 | ||
| 265 | case DEL_OP: | |
| 266 | if (this._openAndNotClosed()) ra._del(this) | |
| 267 | break | |
| 268 | ||
| 269 | case TRUNCATE_OP: | |
| 270 | if (this._openAndNotClosed()) ra._truncate(this) | |
| 271 | break | |
| 272 | ||
| 273 | case STAT_OP: | |
| 274 | if (this._openAndNotClosed()) ra._stat(this) | |
| 275 | break | |
| 276 | ||
| 277 | case OPEN_OP: | |
| 278 | this._open() | |
| 279 | break | |
| 280 | ||
| 281 | case SUSPEND_OP: | |
| 282 | if (ra.closed || !ra.opened || ra.suspended) nextTick(this, null) | |
| 283 | else ra._suspend(this) | |
| 284 | break | |
| 285 | ||
| 286 | case CLOSE_OP: | |
| 287 | if (ra.closed || !ra.opened || ra.suspended) nextTick(this, null) | |
| 288 | else ra._close(this) | |
| 289 | break | |
| 290 | ||
| 291 | case UNLINK_OP: | |
| 292 | if (ra.unlinked) nextTick(this, null) | |
| 293 | else ra._unlink(this) | |
| 294 | break | |
| 295 | } | |
| 296 | ||
| 297 | this._sync = false | |
| 298 | } | |
| 299 | } | |
| 300 | ||
| 301 | function queueAndRun (self, req) { | |
| 302 | self._queued.push(req) | |
| 303 | if (!self._pending) req._run() | |
| 304 | } | |
| 305 | ||
| 306 | function drainQueue (self) { | |
| 307 | const queued = self._queued | |
| 308 | ||
| 309 | while (queued.length > 0) { | |
| 310 | const blocking = queued[0].type > 4 | |
| 311 | if (!blocking || !self._pending) queued[0]._run() | |
| 312 | if (blocking) return | |
| 313 | queued.shift() | |
| 314 | } | |
| 315 | } | |
| 316 | ||
| 317 | function defaultImpl (err) { | |
| 318 | return overridable | |
| 319 | ||
| 320 | function overridable (req) { | |
| 321 | nextTick(req, err) | |
| 322 | } | |
| 323 | } | |
| 324 | ||
| 325 | function nextTick (req, err, val) { | |
| 326 | queueTick(() => req.callback(err, val)) | |
| 327 | } | |
| 328 | ||
| 329 | function nextTickCallback (cb) { | |
| 330 | queueTick(() => cb(null)) | |
| 331 | } | |
| 332 | ||
| 333 | function noop () {} |
| … | … | @@ -0,0 +1,36 @@ |
| 1 | { | |
| 2 | "name": "random-access-storage", | |
| 3 | "version": "3.0.2", | |
| 4 | "description": "Easily make random-access-storage instances", | |
| 5 | "main": "index.js", | |
| 6 | "imports": { | |
| 7 | "events": { | |
| 8 | "bare": "bare-events", | |
| 9 | "default": "events" | |
| 10 | } | |
| 11 | }, | |
| 12 | "dependencies": { | |
| 13 | "bare-events": "^2.2.0", | |
| 14 | "queue-tick": "^1.0.0" | |
| 15 | }, | |
| 16 | "devDependencies": { | |
| 17 | "brittle": "^3.0.0", | |
| 18 | "standard": "^17.0.0" | |
| 19 | }, | |
| 20 | "files": [ | |
| 21 | "index.js" | |
| 22 | ], | |
| 23 | "scripts": { | |
| 24 | "test": "standard && brittle test.js" | |
| 25 | }, | |
| 26 | "repository": { | |
| 27 | "type": "git", | |
| 28 | "url": "https://github.com/random-access-storage/random-access-storage.git" | |
| 29 | }, | |
| 30 | "author": "Mathias Buus (@mafintosh)", | |
| 31 | "license": "MIT", | |
| 32 | "bugs": { | |
| 33 | "url": "https://github.com/random-access-storage/random-access-storage/issues" | |
| 34 | }, | |
| 35 | "homepage": "https://github.com/random-access-storage/random-access-storage" | |
| 36 | } |
| … | … | @@ -0,0 +1,24 @@ |
| 1 | name: Build Status | |
| 2 | ||
| 3 | on: | |
| 4 | push: | |
| 5 | branches: | |
| 6 | - master | |
| 7 | pull_request: | |
| 8 | branches: | |
| 9 | - master | |
| 10 | jobs: | |
| 11 | build: | |
| 12 | strategy: | |
| 13 | matrix: | |
| 14 | node-version: [10.x, 12.x, 14.x] | |
| 15 | os: [ubuntu-16.04, macos-latest, windows-latest] | |
| 16 | runs-on: ${{ matrix.os }} | |
| 17 | steps: | |
| 18 | - uses: actions/checkout@v2 | |
| 19 | - name: Use Node.js ${{ matrix.node-version }} | |
| 20 | uses: actions/setup-node@v1 | |
| 21 | with: | |
| 22 | node-version: ${{ matrix.node-version }} | |
| 23 | - run: npm install | |
| 24 | - run: npm test |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2020 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,48 @@ |
| 1 | # random-array-iterator | |
| 2 | ||
| 3 | An iterator to iterate an array in random order with controls to requeue or dequeue | |
| 4 | elements during the iteration. | |
| 5 | ||
| 6 | ``` | |
| 7 | npm install random-array-iterator | |
| 8 | ``` | |
| 9 | ||
| 10 | ## Usage | |
| 11 | ||
| 12 | ``` js | |
| 13 | const RandomArrayIterator = require('random-array-iterator') | |
| 14 | ||
| 15 | const ite = new RandomArrayIterator([1, 2, 3, 4, 5]) | |
| 16 | ||
| 17 | for (const val of ite) { | |
| 18 | console.log(val) // random value | |
| 19 | ||
| 20 | // call requeue if you want to revisit this value is the same iteration | |
| 21 | if (someCondition) ite.requeue() | |
| 22 | ||
| 23 | // call dequeue if you want to remove the value from the iteration and array entirely | |
| 24 | if (someOtherCondition) ite.dequeue() | |
| 25 | } | |
| 26 | ``` | |
| 27 | ||
| 28 | ## API | |
| 29 | ||
| 30 | #### `ite = new RandomArrayIterator(array)` | |
| 31 | ||
| 32 | Make a new iterator. Implements the JavaScript iterator interface. | |
| 33 | ||
| 34 | #### `ite.requeue()` | |
| 35 | ||
| 36 | Requeue the current value. Only valid to call during an iteration. | |
| 37 | ||
| 38 | #### `ite.dequeue()` | |
| 39 | ||
| 40 | Remove the current value from the array and iteration. Only valid to vall during an iteration. | |
| 41 | ||
| 42 | #### `ite.restart()` | |
| 43 | ||
| 44 | Restarts the iterator. | |
| 45 | ||
| 46 | ## License | |
| 47 | ||
| 48 | MIT |
| … | … | @@ -0,0 +1,46 @@ |
| 1 | module.exports = class RandomArrayIterator { | |
| 2 | constructor (values) { | |
| 3 | this.values = values | |
| 4 | this.start = 0 | |
| 5 | this.length = this.values.length | |
| 6 | } | |
| 7 | ||
| 8 | next () { | |
| 9 | if (this.length === 0) { | |
| 10 | if (this.start === 0) return { done: true, value: undefined } | |
| 11 | this.length = this.start | |
| 12 | this.start = 0 | |
| 13 | } | |
| 14 | ||
| 15 | const i = this.start + ((Math.random() * this.length) | 0) | |
| 16 | const j = this.start + --this.length | |
| 17 | const value = this.values[i] | |
| 18 | ||
| 19 | this.values[i] = this.values[j] | |
| 20 | this.values[j] = value | |
| 21 | ||
| 22 | return { done: false, value } | |
| 23 | } | |
| 24 | ||
| 25 | dequeue () { | |
| 26 | this.values[this.start + this.length] = this.values[this.values.length - 1] | |
| 27 | this.values.pop() | |
| 28 | } | |
| 29 | ||
| 30 | requeue () { | |
| 31 | const i = this.start + this.length | |
| 32 | const value = this.values[i] | |
| 33 | this.values[i] = this.values[this.start] | |
| 34 | this.values[this.start++] = value | |
| 35 | } | |
| 36 | ||
| 37 | restart () { | |
| 38 | this.start = 0 | |
| 39 | this.length = this.values.length | |
| 40 | return this | |
| 41 | } | |
| 42 | ||
| 43 | [Symbol.iterator] () { | |
| 44 | return this | |
| 45 | } | |
| 46 | } |
| … | … | @@ -0,0 +1,24 @@ |
| 1 | { | |
| 2 | "name": "random-array-iterator", | |
| 3 | "version": "1.0.0", | |
| 4 | "description": "An iterator to iterate an array in random order with controls to requeue or dequeue elements during the iteration", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": {}, | |
| 7 | "devDependencies": { | |
| 8 | "standard": "^16.0.3", | |
| 9 | "tape": "^5.0.1" | |
| 10 | }, | |
| 11 | "scripts": { | |
| 12 | "test": "standard && tape test.js" | |
| 13 | }, | |
| 14 | "repository": { | |
| 15 | "type": "git", | |
| 16 | "url": "https://github.com/mafintosh/random-array-iterator.git" | |
| 17 | }, | |
| 18 | "author": "Mathias Buus (@mafintosh)", | |
| 19 | "license": "MIT", | |
| 20 | "bugs": { | |
| 21 | "url": "https://github.com/mafintosh/random-array-iterator/issues" | |
| 22 | }, | |
| 23 | "homepage": "https://github.com/mafintosh/random-array-iterator" | |
| 24 | } |
| … | … | @@ -0,0 +1,96 @@ |
| 1 | const tape = require('tape') | |
| 2 | const Iterator = require('./') | |
| 3 | ||
| 4 | tape('basic', function (t) { | |
| 5 | let diff = 0 | |
| 6 | ||
| 7 | for (let n = 0; n < 5; n++) { | |
| 8 | const values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | |
| 9 | const old = values.join(',') | |
| 10 | ||
| 11 | const cnts = {} | |
| 12 | for (const i of new Iterator(values)) { | |
| 13 | cnts[i] = (cnts[i] || 0) + 1 | |
| 14 | t.same(cnts[i], 1) | |
| 15 | } | |
| 16 | ||
| 17 | if (values.join(',') !== old) diff++ | |
| 18 | t.same(Object.keys(cnts).length, values.length) | |
| 19 | } | |
| 20 | ||
| 21 | t.ok(diff > 1) | |
| 22 | t.end() | |
| 23 | }) | |
| 24 | ||
| 25 | tape('requeue', function (t) { | |
| 26 | const values = [1, 2, 3, 4, 5] | |
| 27 | ||
| 28 | let requeues = 2 | |
| 29 | ||
| 30 | const cnts = {} | |
| 31 | const ite = new Iterator(values) | |
| 32 | for (const i of ite) { | |
| 33 | if ((i === 1 || i === 3) && requeues-- > 0) { | |
| 34 | ite.requeue() | |
| 35 | } | |
| 36 | cnts[i] = (cnts[i] || 0) + 1 | |
| 37 | } | |
| 38 | ||
| 39 | t.same(cnts, { | |
| 40 | 1: 2, | |
| 41 | 2: 1, | |
| 42 | 3: 2, | |
| 43 | 4: 1, | |
| 44 | 5: 1 | |
| 45 | }) | |
| 46 | ||
| 47 | t.end() | |
| 48 | }) | |
| 49 | ||
| 50 | tape('dequeue', function (t) { | |
| 51 | const values = [1, 2, 3, 4, 5] | |
| 52 | ||
| 53 | const ite = new Iterator(values) | |
| 54 | for (const i of ite) { | |
| 55 | if (i === 2 || i === 5) ite.dequeue() | |
| 56 | } | |
| 57 | ||
| 58 | t.same(values.sort(), [1, 3, 4]) | |
| 59 | t.end() | |
| 60 | }) | |
| 61 | ||
| 62 | tape('dequeue all', function (t) { | |
| 63 | const values = [1, 2, 3, 4, 5] | |
| 64 | ||
| 65 | const trace = [] | |
| 66 | const ite = new Iterator(values) | |
| 67 | for (const i of ite) { | |
| 68 | ite.dequeue() | |
| 69 | trace.push(i) | |
| 70 | } | |
| 71 | ||
| 72 | t.same(trace.sort(), [1, 2, 3, 4, 5]) | |
| 73 | t.same(values, []) | |
| 74 | t.end() | |
| 75 | }) | |
| 76 | ||
| 77 | tape('dequeue and requeue', function (t) { | |
| 78 | const values = [1, 2, 3, 4, 5] | |
| 79 | ||
| 80 | const ite = new Iterator(values) | |
| 81 | const trace = [] | |
| 82 | let requeue = true | |
| 83 | ||
| 84 | for (const i of ite) { | |
| 85 | if (i === 2 || i === 5) ite.dequeue() | |
| 86 | if (requeue && i === 3) { | |
| 87 | requeue = false | |
| 88 | ite.requeue() | |
| 89 | } | |
| 90 | trace.push(i) | |
| 91 | } | |
| 92 | ||
| 93 | t.same(values.sort(), [1, 3, 4]) | |
| 94 | t.same(trace.sort(), [1, 2, 3, 3, 4, 5]) | |
| 95 | t.end() | |
| 96 | }) |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | name: Build Status | |
| 2 | on: | |
| 3 | push: | |
| 4 | branches: | |
| 5 | - master | |
| 6 | pull_request: | |
| 7 | branches: | |
| 8 | - master | |
| 9 | jobs: | |
| 10 | build: | |
| 11 | strategy: | |
| 12 | matrix: | |
| 13 | node-version: [lts/*] | |
| 14 | os: [ubuntu-latest, macos-latest, windows-latest] | |
| 15 | runs-on: ${{ matrix.os }} | |
| 16 | steps: | |
| 17 | - uses: actions/checkout@v2 | |
| 18 | - name: Use Node.js ${{ matrix.node-version }} | |
| 19 | uses: actions/setup-node@v2 | |
| 20 | with: | |
| 21 | node-version: ${{ matrix.node-version }} | |
| 22 | - run: npm install | |
| 23 | - run: npm test |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2023 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,35 @@ |
| 1 | # read-write-mutexify | |
| 2 | ||
| 3 | Like [mutexify](https://github.com/mafintosh/mutexify) but with read/write locks | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install read-write-mutexify | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const RW = require('read-write-mutexify') | |
| 13 | ||
| 14 | const rw = new RW() | |
| 15 | ||
| 16 | // read locks waits for writer locks to be released | |
| 17 | await rw.read.lock() | |
| 18 | await rw.read.lock() // make as many as you want | |
| 19 | ||
| 20 | // unlock with unlock | |
| 21 | rw.read.unlock() | |
| 22 | rw.read.unlock() | |
| 23 | ||
| 24 | // only one writer can have the write lock and it waits | |
| 25 | // for any read lock to be released | |
| 26 | ||
| 27 | await rw.write.lock() | |
| 28 | ||
| 29 | // unlock with unlock | |
| 30 | rw.write.unlock() | |
| 31 | ``` | |
| 32 | ||
| 33 | ## License | |
| 34 | ||
| 35 | MIT |
| … | … | @@ -0,0 +1,134 @@ |
| 1 | class WriteLock { | |
| 2 | constructor (parent) { | |
| 3 | this.writing = false | |
| 4 | ||
| 5 | this._waiting = [] | |
| 6 | this._parent = parent | |
| 7 | this._wait = pushToQueue.bind(this, this._waiting) | |
| 8 | } | |
| 9 | ||
| 10 | get locked () { | |
| 11 | return this.writing || this._parent.read.readers > 0 | |
| 12 | } | |
| 13 | ||
| 14 | get waiting () { | |
| 15 | return this._waiting.length | |
| 16 | } | |
| 17 | ||
| 18 | lock () { | |
| 19 | if (this._parent._destroying) { | |
| 20 | return Promise.reject(this._parent._destroyError) | |
| 21 | } | |
| 22 | ||
| 23 | if (this.writing === false && this._parent.read.readers === 0) { | |
| 24 | this.writing = true | |
| 25 | return Promise.resolve() | |
| 26 | } | |
| 27 | ||
| 28 | return new Promise(this._wait) | |
| 29 | } | |
| 30 | ||
| 31 | unlock () { | |
| 32 | this.writing = false | |
| 33 | this._parent._bump() | |
| 34 | } | |
| 35 | ||
| 36 | async flush () { | |
| 37 | if (this.writing === false) return | |
| 38 | try { | |
| 39 | await this.lock() | |
| 40 | } catch { | |
| 41 | return | |
| 42 | } | |
| 43 | this.unlock() | |
| 44 | } | |
| 45 | } | |
| 46 | ||
| 47 | class ReadLock { | |
| 48 | constructor (parent) { | |
| 49 | this.readers = 0 | |
| 50 | ||
| 51 | this._waiting = [] | |
| 52 | this._parent = parent | |
| 53 | this._wait = pushToQueue.bind(this, this._waiting) | |
| 54 | } | |
| 55 | ||
| 56 | get locked () { | |
| 57 | return this._parent.writing | |
| 58 | } | |
| 59 | ||
| 60 | get waiting () { | |
| 61 | return this._waiting.length | |
| 62 | } | |
| 63 | ||
| 64 | lock () { | |
| 65 | if (this._parent._destroying) { | |
| 66 | return Promise.reject(this._parent._destroyError) | |
| 67 | } | |
| 68 | ||
| 69 | if (this._parent.write.writing === false) { | |
| 70 | this.readers++ | |
| 71 | return Promise.resolve() | |
| 72 | } | |
| 73 | ||
| 74 | return new Promise(this._wait) | |
| 75 | } | |
| 76 | ||
| 77 | unlock () { | |
| 78 | this.readers-- | |
| 79 | this._parent._bump() | |
| 80 | } | |
| 81 | ||
| 82 | async flush () { | |
| 83 | if (this.writing === false) return | |
| 84 | try { | |
| 85 | await this.lock() | |
| 86 | } catch { | |
| 87 | return | |
| 88 | } | |
| 89 | this.unlock() | |
| 90 | } | |
| 91 | } | |
| 92 | ||
| 93 | module.exports = class ReadWriteLock { | |
| 94 | constructor () { | |
| 95 | this.read = new ReadLock(this) | |
| 96 | this.write = new WriteLock(this) | |
| 97 | ||
| 98 | this._destroyError = null | |
| 99 | this._destroying = null | |
| 100 | } | |
| 101 | ||
| 102 | get destroyed () { | |
| 103 | return !!this._destroying | |
| 104 | } | |
| 105 | ||
| 106 | destroy (err) { | |
| 107 | if (this._destroying) return this._destroying | |
| 108 | ||
| 109 | this._destroying = Promise.all([this.read.flush(), this.write.flush()]) | |
| 110 | this._destroyError = err || new Error('Mutex has been destroyed') | |
| 111 | ||
| 112 | if (err) { | |
| 113 | while (this.read._waiting) this._waiting.shift()[1](err) | |
| 114 | while (this.write._waiting) this._waiting.shift()[1](err) | |
| 115 | } | |
| 116 | ||
| 117 | return this._destroying | |
| 118 | } | |
| 119 | ||
| 120 | _bump () { | |
| 121 | if (this.write.writing === false && this.read.readers === 0 && this.write._waiting.length > 0) { | |
| 122 | this.write.writing = true | |
| 123 | this.write._waiting.shift()[0]() | |
| 124 | } | |
| 125 | while (this.write.writing === false && this.read._waiting.length > 0) { | |
| 126 | this.read.readers++ | |
| 127 | this.read._waiting.shift()[0]() | |
| 128 | } | |
| 129 | } | |
| 130 | } | |
| 131 | ||
| 132 | function pushToQueue (queue, resolve, reject) { | |
| 133 | queue.push([resolve, reject]) | |
| 134 | } |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | { | |
| 2 | "name": "read-write-mutexify", | |
| 3 | "version": "2.1.0", | |
| 4 | "description": "Like mutexify but with read/write locks", | |
| 5 | "main": "index.js", | |
| 6 | "scripts": { | |
| 7 | "test": "standard && brittle test.js" | |
| 8 | }, | |
| 9 | "devDependencies": { | |
| 10 | "brittle": "^3.3.0", | |
| 11 | "standard": "^17.1.0" | |
| 12 | }, | |
| 13 | "repository": { | |
| 14 | "type": "git", | |
| 15 | "url": "https://github.com/mafintosh/read-write-mutexify.git" | |
| 16 | }, | |
| 17 | "author": "Mathias Buus (@mafintosh)", | |
| 18 | "license": "MIT", | |
| 19 | "bugs": { | |
| 20 | "url": "https://github.com/mafintosh/read-write-mutexify/issues" | |
| 21 | }, | |
| 22 | "homepage": "https://github.com/mafintosh/read-write-mutexify" | |
| 23 | } |
| … | … | @@ -0,0 +1,73 @@ |
| 1 | const test = require('brittle') | |
| 2 | const RW = require('./') | |
| 3 | ||
| 4 | test('as many reads as you want', async function (t) { | |
| 5 | const rw = new RW() | |
| 6 | ||
| 7 | await rw.read.lock() | |
| 8 | await rw.read.lock() | |
| 9 | ||
| 10 | t.pass('didnt dead lock') | |
| 11 | ||
| 12 | rw.read.unlock() | |
| 13 | rw.read.unlock() | |
| 14 | }) | |
| 15 | ||
| 16 | test('only one read at the time', async function (t) { | |
| 17 | t.plan(1) | |
| 18 | ||
| 19 | const rw = new RW() | |
| 20 | ||
| 21 | let released = false | |
| 22 | await rw.write.lock() | |
| 23 | ||
| 24 | rw.write.lock().then(() => { | |
| 25 | t.ok(released, 'only one writer active') | |
| 26 | }) | |
| 27 | ||
| 28 | await new Promise(resolve => setImmediate(resolve)) | |
| 29 | ||
| 30 | rw.write.unlock() | |
| 31 | released = true | |
| 32 | }) | |
| 33 | ||
| 34 | test('reads waits for writer', async function (t) { | |
| 35 | t.plan(1) | |
| 36 | ||
| 37 | const rw = new RW() | |
| 38 | ||
| 39 | let released = false | |
| 40 | await rw.write.lock() | |
| 41 | ||
| 42 | rw.read.lock().then(() => { | |
| 43 | t.ok(released, 'read waited for writer') | |
| 44 | }) | |
| 45 | ||
| 46 | await new Promise(resolve => setImmediate(resolve)) | |
| 47 | ||
| 48 | rw.write.unlock() | |
| 49 | released = true | |
| 50 | }) | |
| 51 | ||
| 52 | test('writer waits for all reads', async function (t) { | |
| 53 | t.plan(1) | |
| 54 | ||
| 55 | const rw = new RW() | |
| 56 | ||
| 57 | let released = false | |
| 58 | await rw.read.lock() | |
| 59 | await rw.read.lock() | |
| 60 | ||
| 61 | rw.write.lock().then(() => { | |
| 62 | t.ok(released, 'write waited for all readers') | |
| 63 | }) | |
| 64 | ||
| 65 | await new Promise(resolve => setImmediate(resolve)) | |
| 66 | ||
| 67 | rw.read.unlock() | |
| 68 | ||
| 69 | await new Promise(resolve => setImmediate(resolve)) | |
| 70 | ||
| 71 | rw.read.unlock() | |
| 72 | released = true | |
| 73 | }) |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2022 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,39 @@ |
| 1 | # ready-resource | |
| 2 | ||
| 3 | Modern single resource management | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install ready-resource | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const ReadyResource = require('ready-resource') | |
| 13 | ||
| 14 | class Thing extends ReadyResource { | |
| 15 | constructor () { | |
| 16 | super() | |
| 17 | } | |
| 18 | ||
| 19 | async _open () { | |
| 20 | // open the resource | |
| 21 | } | |
| 22 | ||
| 23 | async _close () { | |
| 24 | // close the resource | |
| 25 | } | |
| 26 | } | |
| 27 | ||
| 28 | const r = new Thing() | |
| 29 | ||
| 30 | await r.ready() // calls _open once | |
| 31 | await r.ready() // noop | |
| 32 | ||
| 33 | await r.close() // calls _close after _open has finished | |
| 34 | await r.close() // noop | |
| 35 | ``` | |
| 36 | ||
| 37 | ## License | |
| 38 | ||
| 39 | MIT |
| … | … | @@ -0,0 +1,53 @@ |
| 1 | declare module 'ready-resource' { | |
| 2 | import { EventEmitter } from 'events'; | |
| 3 | ||
| 4 | /** | |
| 5 | * Runtime export is CommonJS: | |
| 6 | * module.exports = class ReadyResource extends EventEmitter {} | |
| 7 | * This declaration mirrors that shape. | |
| 8 | */ | |
| 9 | class ReadyResource extends EventEmitter { | |
| 10 | constructor(); | |
| 11 | ||
| 12 | /** Set when a call to ready() has started; resolves when _open completes */ | |
| 13 | opening: Promise<void> | null; | |
| 14 | ||
| 15 | /** Set when a call to close() has started; resolves when _close completes */ | |
| 16 | closing: Promise<void> | null; | |
| 17 | ||
| 18 | /** True after successful _open */ | |
| 19 | opened: boolean; | |
| 20 | ||
| 21 | /** True after successful _close (or attempted close after failed open) */ | |
| 22 | closed: boolean; | |
| 23 | ||
| 24 | /** Ensure the resource is opened; returns the in-flight promise if any */ | |
| 25 | ready(): Promise<void>; | |
| 26 | ||
| 27 | /** Ensure the resource is closed; returns the in-flight promise if any */ | |
| 28 | close(): Promise<void>; | |
| 29 | ||
| 30 | /** Override these in subclasses */ | |
| 31 | protected _open(): Promise<void>; | |
| 32 | protected _close(): Promise<void>; | |
| 33 | ||
| 34 | // Typed events emitted by the implementation | |
| 35 | on(event: 'ready', listener: () => void): this; | |
| 36 | on(event: 'close', listener: () => void): this; | |
| 37 | once(event: 'ready', listener: () => void): this; | |
| 38 | once(event: 'close', listener: () => void): this; | |
| 39 | off(event: 'ready', listener: () => void): this; | |
| 40 | off(event: 'close', listener: () => void): this; | |
| 41 | emit(event: 'ready'): boolean; | |
| 42 | emit(event: 'close'): boolean; | |
| 43 | ||
| 44 | // Fallback EventEmitter overloads | |
| 45 | on(event: string | symbol, listener: (...args: any[]) => void): this; | |
| 46 | once(event: string | symbol, listener: (...args: any[]) => void): this; | |
| 47 | off(event: string | symbol, listener: (...args: any[]) => void): this; | |
| 48 | emit(event: string | symbol, ...args: any[]): boolean; | |
| 49 | } | |
| 50 | ||
| 51 | // Match CommonJS runtime export | |
| 52 | export = ReadyResource; | |
| 53 | } |
| … | … | @@ -0,0 +1,59 @@ |
| 1 | const EventEmitter = require('events') | |
| 2 | ||
| 3 | module.exports = class ReadyResource extends EventEmitter { | |
| 4 | constructor () { | |
| 5 | super() | |
| 6 | ||
| 7 | this.opening = null | |
| 8 | this.closing = null | |
| 9 | ||
| 10 | this.opened = false | |
| 11 | this.closed = false | |
| 12 | } | |
| 13 | ||
| 14 | ready () { | |
| 15 | if (this.opening !== null) return this.opening | |
| 16 | this.opening = open(this) | |
| 17 | return this.opening | |
| 18 | } | |
| 19 | ||
| 20 | close () { | |
| 21 | if (this.closing !== null) return this.closing | |
| 22 | this.closing = close(this) | |
| 23 | return this.closing | |
| 24 | } | |
| 25 | ||
| 26 | async _open () { | |
| 27 | // add impl here | |
| 28 | } | |
| 29 | ||
| 30 | async _close () { | |
| 31 | // add impl here | |
| 32 | } | |
| 33 | } | |
| 34 | ||
| 35 | async function open (self) { | |
| 36 | // open after close | |
| 37 | if (self.closing !== null) return | |
| 38 | ||
| 39 | try { | |
| 40 | await self._open() | |
| 41 | } catch (err) { | |
| 42 | self.close() // safe to run in bg | |
| 43 | throw err | |
| 44 | } | |
| 45 | ||
| 46 | self.opened = true | |
| 47 | self.emit('ready') | |
| 48 | } | |
| 49 | ||
| 50 | async function close (self) { | |
| 51 | try { | |
| 52 | if (self.opened === false && self.opening !== null) await self.opening | |
| 53 | } catch { | |
| 54 | // ignore errors on closing | |
| 55 | } | |
| 56 | if (self.opened === true || self.opening === null) await self._close() | |
| 57 | self.closed = true | |
| 58 | self.emit('close') | |
| 59 | } |
| … | … | @@ -0,0 +1,37 @@ |
| 1 | { | |
| 2 | "name": "ready-resource", | |
| 3 | "version": "1.2.0", | |
| 4 | "description": "Modern single resource management", | |
| 5 | "main": "index.js", | |
| 6 | "imports": { | |
| 7 | "events": { | |
| 8 | "bare": "bare-events", | |
| 9 | "default": "events" | |
| 10 | } | |
| 11 | }, | |
| 12 | "types": "index.d.ts", | |
| 13 | "files": [ | |
| 14 | "index.js", | |
| 15 | "index.d.ts" | |
| 16 | ], | |
| 17 | "scripts": { | |
| 18 | "test": "standard && brittle test.js" | |
| 19 | }, | |
| 20 | "devDependencies": { | |
| 21 | "brittle": "^3.1.0", | |
| 22 | "standard": "^17.0.0" | |
| 23 | }, | |
| 24 | "repository": { | |
| 25 | "type": "git", | |
| 26 | "url": "git+https://github.com/holepunchto/ready-resource.git" | |
| 27 | }, | |
| 28 | "author": "Mathias Buus (@mafintosh)", | |
| 29 | "license": "MIT", | |
| 30 | "bugs": { | |
| 31 | "url": "https://github.com/holepunchto/ready-resource/issues" | |
| 32 | }, | |
| 33 | "homepage": "https://github.com/holepunchto/ready-resource", | |
| 34 | "dependencies": { | |
| 35 | "bare-events": "^2.2.0" | |
| 36 | } | |
| 37 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,13 @@ |
| 1 | Copyright 2023 Holepunch Inc | |
| 2 | ||
| 3 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 | you may not use this file except in compliance with the License. | |
| 5 | You may obtain a copy of the License at | |
| 6 | ||
| 7 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | ||
| 9 | Unless required by applicable law or agreed to in writing, software | |
| 10 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 | See the License for the specific language governing permissions and | |
| 13 | limitations under the License. |
| … | … | @@ -0,0 +1,25 @@ |
| 1 | # refcounter | |
| 2 | ||
| 3 | Simple refcounter | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install refcounter | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const RefCounter = require('refcounter') | |
| 13 | ||
| 14 | const refs = new RefCounter() | |
| 15 | ||
| 16 | refs.inc() | |
| 17 | refs.dec() | |
| 18 | ||
| 19 | refs.isIdle() // true is no refs | |
| 20 | await refs.idle() // wait for no refs | |
| 21 | ``` | |
| 22 | ||
| 23 | ## License | |
| 24 | ||
| 25 | Apache-2.0 |
| … | … | @@ -0,0 +1,38 @@ |
| 1 | module.exports = class RefCounter { | |
| 2 | constructor() { | |
| 3 | this.count = 0 | |
| 4 | ||
| 5 | this._onidle = null | |
| 6 | this._idle = null | |
| 7 | } | |
| 8 | ||
| 9 | isIdle() { | |
| 10 | return this.count === 0 | |
| 11 | } | |
| 12 | ||
| 13 | idle() { | |
| 14 | if (this.count === 0) return Promise.resolve() | |
| 15 | if (this._idle !== null) return this._idle | |
| 16 | ||
| 17 | this._idle = new Promise((resolve) => { | |
| 18 | this._onidle = resolve | |
| 19 | }) | |
| 20 | ||
| 21 | return this._idle | |
| 22 | } | |
| 23 | ||
| 24 | inc() { | |
| 25 | this.count++ | |
| 26 | } | |
| 27 | ||
| 28 | dec() { | |
| 29 | if (--this.count > 0) return | |
| 30 | ||
| 31 | if (this._onidle !== null) { | |
| 32 | const resolve = this._onidle | |
| 33 | this._idle = null | |
| 34 | this._onidle = null | |
| 35 | resolve() | |
| 36 | } | |
| 37 | } | |
| 38 | } |
| … | … | @@ -0,0 +1,18 @@ |
| 1 | { | |
| 2 | "name": "refcounter", | |
| 3 | "version": "1.0.0", | |
| 4 | "description": "Simple refcounter", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": {}, | |
| 7 | "devDependencies": {}, | |
| 8 | "repository": { | |
| 9 | "type": "git", | |
| 10 | "url": "https://github.com/holepunchto/refcounter.git" | |
| 11 | }, | |
| 12 | "author": "Holepunch Inc", | |
| 13 | "license": "Apache-2.0", | |
| 14 | "bugs": { | |
| 15 | "url": "https://github.com/holepunchto/refcounter/issues" | |
| 16 | }, | |
| 17 | "homepage": "https://github.com/holepunchto/refcounter" | |
| 18 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,19 @@ |
| 1 | # require-addon | |
| 2 | ||
| 3 | Import native addons across JavaScript runtimes. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i require-addon | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | require.addon = require('require-addon') | |
| 13 | ||
| 14 | const bindings = require.addon('.', __filename) | |
| 15 | ``` | |
| 16 | ||
| 17 | ## License | |
| 18 | ||
| 19 | Apache-2.0 |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require.addon.bind(require) |
| … | … | @@ -0,0 +1,7 @@ |
| 1 | if (typeof require.addon === 'function') { | |
| 2 | module.exports = require.addon.bind(require) | |
| 3 | } else { | |
| 4 | module.exports = function addon(specifier, parentURL) { | |
| 5 | throw new Error(`Cannot find addon '${specifier}' imported from '${parentURL}'`) | |
| 6 | } | |
| 7 | } |
| … | … | @@ -0,0 +1,71 @@ |
| 1 | if (typeof require.addon === 'function') { | |
| 2 | module.exports = require.addon.bind(require) | |
| 3 | } else { | |
| 4 | const url = require('url') | |
| 5 | const fs = require('fs') | |
| 6 | const resolve = require('bare-addon-resolve') | |
| 7 | ||
| 8 | let host = process.platform + '-' + process.arch | |
| 9 | const conditions = ['addon', 'node', process.platform, process.arch] | |
| 10 | const extensions = ['.node'] | |
| 11 | ||
| 12 | if (isAlpine()) { | |
| 13 | host += '-musl' | |
| 14 | conditions.push('musl') | |
| 15 | } | |
| 16 | ||
| 17 | module.exports = function addon(specifier, parentURL) { | |
| 18 | if (typeof parentURL === 'string') parentURL = url.pathToFileURL(parentURL) | |
| 19 | ||
| 20 | const candidates = [] | |
| 21 | ||
| 22 | let cause | |
| 23 | ||
| 24 | for (const resolution of resolve( | |
| 25 | specifier, | |
| 26 | parentURL, | |
| 27 | { host, conditions, extensions }, | |
| 28 | readPackage | |
| 29 | )) { | |
| 30 | candidates.push(resolution) | |
| 31 | ||
| 32 | switch (resolution.protocol) { | |
| 33 | case 'file:': | |
| 34 | try { | |
| 35 | return require(url.fileURLToPath(resolution)) | |
| 36 | } catch (err) { | |
| 37 | cause = err | |
| 38 | continue | |
| 39 | } | |
| 40 | } | |
| 41 | } | |
| 42 | ||
| 43 | let message = `Cannot find addon '${specifier}' imported from '${parentURL.href}'` | |
| 44 | ||
| 45 | if (candidates.length > 0) { | |
| 46 | message += '\nCandidates:' | |
| 47 | message += '\n' + candidates.map((url) => '- ' + url.href).join('\n') | |
| 48 | } | |
| 49 | ||
| 50 | const err = new Error(message, cause ? { cause } : {}) | |
| 51 | ||
| 52 | err.code = 'ADDON_NOT_FOUND' | |
| 53 | err.specifier = specifier | |
| 54 | err.referrer = parentURL | |
| 55 | err.candidates = candidates | |
| 56 | ||
| 57 | throw err | |
| 58 | } | |
| 59 | ||
| 60 | function readPackage(packageURL) { | |
| 61 | try { | |
| 62 | return require(url.fileURLToPath(packageURL)) | |
| 63 | } catch (err) { | |
| 64 | return null | |
| 65 | } | |
| 66 | } | |
| 67 | ||
| 68 | function isAlpine() { | |
| 69 | return process.platform === 'linux' && fs.existsSync('/etc/alpine-release') | |
| 70 | } | |
| 71 | } |
| … | … | @@ -0,0 +1,63 @@ |
| 1 | { | |
| 2 | "name": "require-addon", | |
| 3 | "version": "1.2.0", | |
| 4 | "description": "Import native addons across JavaScript runtimes", | |
| 5 | "exports": { | |
| 6 | "./package": "./package.json", | |
| 7 | ".": { | |
| 8 | "bare": "./lib/bare.js", | |
| 9 | "node": "./lib/node.js", | |
| 10 | "default": "./lib/default.js" | |
| 11 | } | |
| 12 | }, | |
| 13 | "imports": { | |
| 14 | "fs": { | |
| 15 | "bare": "bare-fs", | |
| 16 | "default": "fs" | |
| 17 | }, | |
| 18 | "path": { | |
| 19 | "bare": "bare-path", | |
| 20 | "default": "path" | |
| 21 | }, | |
| 22 | "url": { | |
| 23 | "bare": "bare-url", | |
| 24 | "default": "url" | |
| 25 | } | |
| 26 | }, | |
| 27 | "files": [ | |
| 28 | "lib" | |
| 29 | ], | |
| 30 | "scripts": { | |
| 31 | "test": "npm run lint && npm run test:bare && npm run test:node", | |
| 32 | "test:bare": "bare test.js", | |
| 33 | "test:node": "node test.js", | |
| 34 | "lint": "prettier --check .", | |
| 35 | "format": "prettier --write ." | |
| 36 | }, | |
| 37 | "repository": { | |
| 38 | "type": "git", | |
| 39 | "url": "git+https://github.com/holepunchto/require-addon.git" | |
| 40 | }, | |
| 41 | "author": "Holepunch", | |
| 42 | "license": "Apache-2.0", | |
| 43 | "bugs": { | |
| 44 | "url": "https://github.com/holepunchto/require-addon/issues" | |
| 45 | }, | |
| 46 | "homepage": "https://github.com/holepunchto/require-addon#readme", | |
| 47 | "engines": { | |
| 48 | "bare": ">=1.10.0" | |
| 49 | }, | |
| 50 | "dependencies": { | |
| 51 | "bare-addon-resolve": "^1.3.0" | |
| 52 | }, | |
| 53 | "devDependencies": { | |
| 54 | "bare-bundle": "^1.8.1", | |
| 55 | "bare-bundle-evaluate": "^1.1.0", | |
| 56 | "bare-fs": "^4.0.0", | |
| 57 | "bare-path": "^3.0.0", | |
| 58 | "bare-url": "^2.1.0", | |
| 59 | "brittle": "^3.7.0", | |
| 60 | "prettier": "^3.6.2", | |
| 61 | "prettier-config-holepunch": "^2.0.0" | |
| 62 | } | |
| 63 | } |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | name: Build Status | |
| 2 | on: | |
| 3 | push: | |
| 4 | branches: | |
| 5 | - main | |
| 6 | pull_request: | |
| 7 | branches: | |
| 8 | - main | |
| 9 | jobs: | |
| 10 | build: | |
| 11 | strategy: | |
| 12 | matrix: | |
| 13 | node-version: [lts/*] | |
| 14 | os: [ubuntu-latest, macos-latest, windows-latest] | |
| 15 | runs-on: ${{ matrix.os }} | |
| 16 | steps: | |
| 17 | - uses: actions/checkout@v3 | |
| 18 | - name: Use Node.js ${{ matrix.node-version }} | |
| 19 | uses: actions/setup-node@v3 | |
| 20 | with: | |
| 21 | node-version: ${{ matrix.node-version }} | |
| 22 | - run: npm install | |
| 23 | - run: npm test |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2024 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,19 @@ |
| 1 | # resolve-reject-promise | |
| 2 | ||
| 3 | Create an inverted promise with no function allocs | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install resolve-reject-promise | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const rrp = require('resolve-reject-promise') | |
| 13 | ||
| 14 | const { resolve, reject, promise } = rrp() | |
| 15 | ``` | |
| 16 | ||
| 17 | ## License | |
| 18 | ||
| 19 | MIT |
| … | … | @@ -0,0 +1,18 @@ |
| 1 | let tmpResolve = null | |
| 2 | let tmpReject = null | |
| 3 | ||
| 4 | if (Promise.withResolvers) { | |
| 5 | module.exports = Promise.withResolvers.bind(Promise) | |
| 6 | } else { | |
| 7 | module.exports = function resolveRejectPromise () { | |
| 8 | const promise = new Promise(setTmp) | |
| 9 | const result = { promise, resolve: tmpResolve, reject: tmpReject } | |
| 10 | tmpResolve = tmpReject = null | |
| 11 | return result | |
| 12 | } | |
| 13 | } | |
| 14 | ||
| 15 | function setTmp (resolve, reject) { | |
| 16 | tmpResolve = resolve | |
| 17 | tmpReject = reject | |
| 18 | } |
| … | … | @@ -0,0 +1,22 @@ |
| 1 | { | |
| 2 | "name": "resolve-reject-promise", | |
| 3 | "version": "1.1.0", | |
| 4 | "description": "Create an inverted promise with no function allocs", | |
| 5 | "main": "index.js", | |
| 6 | "scripts": { | |
| 7 | "test": "standard" | |
| 8 | }, | |
| 9 | "devDependencies": { | |
| 10 | "standard": "^17.1.2" | |
| 11 | }, | |
| 12 | "repository": { | |
| 13 | "type": "git", | |
| 14 | "url": "https://github.com/mafintosh/resolve-reject-promise.git" | |
| 15 | }, | |
| 16 | "author": "Mathias Buus (@mafintosh)", | |
| 17 | "license": "MIT", | |
| 18 | "bugs": { | |
| 19 | "url": "https://github.com/mafintosh/resolve-reject-promise/issues" | |
| 20 | }, | |
| 21 | "homepage": "https://github.com/mafintosh/resolve-reject-promise" | |
| 22 | } |
| … | … | @@ -0,0 +1,63 @@ |
| 1 | cmake_minimum_required(VERSION 4.0) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) | |
| 5 | find_package(cmake-napi REQUIRED PATHS node_modules/cmake-napi) | |
| 6 | find_package(cmake-npm REQUIRED PATHS node_modules/cmake-npm) | |
| 7 | ||
| 8 | project(rocksdb_native C CXX) | |
| 9 | ||
| 10 | bare_target(target) | |
| 11 | ||
| 12 | fetch_package("github:holepunchto/librocksdb#efb0607") | |
| 13 | fetch_package("github:holepunchto/libjstl#387f6d0") | |
| 14 | ||
| 15 | add_bare_module(rocksdb_native_bare) | |
| 16 | ||
| 17 | target_sources( | |
| 18 | ${rocksdb_native_bare} | |
| 19 | PRIVATE | |
| 20 | binding.cc | |
| 21 | ) | |
| 22 | ||
| 23 | target_link_libraries( | |
| 24 | ${rocksdb_native_bare} | |
| 25 | PRIVATE | |
| 26 | $<TARGET_OBJECTS:rocksdb> | |
| 27 | jstl | |
| 28 | PUBLIC | |
| 29 | rocksdb | |
| 30 | rocksdb_facebook | |
| 31 | ) | |
| 32 | ||
| 33 | add_napi_module(rocksdb_native_napi) | |
| 34 | ||
| 35 | target_sources( | |
| 36 | ${rocksdb_native_napi} | |
| 37 | PRIVATE | |
| 38 | binding.cc | |
| 39 | ) | |
| 40 | ||
| 41 | target_compile_definitions( | |
| 42 | ${rocksdb_native_napi} | |
| 43 | PRIVATE | |
| 44 | NAPI_VERSION=9 | |
| 45 | ) | |
| 46 | ||
| 47 | target_link_libraries( | |
| 48 | ${rocksdb_native_napi} | |
| 49 | PRIVATE | |
| 50 | $<TARGET_OBJECTS:rocksdb> | |
| 51 | jstl | |
| 52 | PUBLIC | |
| 53 | rocksdb | |
| 54 | rocksdb_facebook | |
| 55 | ) | |
| 56 | ||
| 57 | resolve_node_module(bare-compat-napi compat) | |
| 58 | ||
| 59 | target_include_directories( | |
| 60 | ${rocksdb_native_napi} | |
| 61 | PRIVATE | |
| 62 | "${compat}/include" | |
| 63 | ) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,32 @@ |
| 1 | # rocksdb-native | |
| 2 | ||
| 3 | <https://github.com/holepunchto/librocksdb> bindings for JavaScript. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i rocksdb-native | |
| 7 | ``` | |
| 8 | ||
| 9 | > [!IMPORTANT] | |
| 10 | > On Linux `libatomic` must be installed as well. | |
| 11 | ||
| 12 | ## Usage | |
| 13 | ||
| 14 | ```js | |
| 15 | const RocksDB = require('rocksdb-native') | |
| 16 | ||
| 17 | const db = new RocksDB('./example.db') | |
| 18 | ||
| 19 | const w = db.write() | |
| 20 | w.put('hello', 'world') | |
| 21 | await w.flush() | |
| 22 | ||
| 23 | const r = db.read() | |
| 24 | const p = r.get('hello') | |
| 25 | r.flush() | |
| 26 | ||
| 27 | console.log(await p) | |
| 28 | ``` | |
| 29 | ||
| 30 | ## License | |
| 31 | ||
| 32 | Apache-2.0 |
| … | … | @@ -0,0 +1,1869 @@ |
| 1 | #include <set> | |
| 2 | ||
| 3 | #include <assert.h> | |
| 4 | #include <bare.h> | |
| 5 | #include <js.h> | |
| 6 | #include <jstl.h> | |
| 7 | #include <rocksdb.h> | |
| 8 | #include <stdlib.h> | |
| 9 | #include <string.h> | |
| 10 | #include <utf.h> | |
| 11 | #include <uv.h> | |
| 12 | ||
| 13 | using rocksdb_native_on_open_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>; | |
| 14 | using rocksdb_native_on_close_t = js_function_t<void, js_receiver_t>; | |
| 15 | using rocksdb_native_on_suspend_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>; | |
| 16 | using rocksdb_native_on_resume_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>; | |
| 17 | using rocksdb_native_on_flush_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>; | |
| 18 | using rocksdb_native_on_write_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>; | |
| 19 | using rocksdb_native_on_read_t = js_function_t<void, js_receiver_t, js_array_t, js_array_t>; | |
| 20 | using rocksdb_native_on_iterator_open_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>; | |
| 21 | using rocksdb_native_on_iterator_close_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>; | |
| 22 | using rocksdb_native_on_iterator_read_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>, js_array_t, js_array_t>; | |
| 23 | using rocksdb_native_on_compact_range_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>; | |
| 24 | using rocksdb_native_on_approximate_size_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>, uint64_t>; | |
| 25 | ||
| 26 | struct rocksdb_native_t; | |
| 27 | ||
| 28 | struct rocksdb_native_column_family_t { | |
| 29 | rocksdb_column_family_t *handle; | |
| 30 | rocksdb_column_family_descriptor_t descriptor; | |
| 31 | ||
| 32 | rocksdb_native_t *db; | |
| 33 | ||
| 34 | js_persistent_t<js_arraybuffer_t> ctx; | |
| 35 | }; | |
| 36 | ||
| 37 | struct rocksdb_native_snapshot_t { | |
| 38 | rocksdb_snapshot_t handle; | |
| 39 | ||
| 40 | rocksdb_native_t *db; | |
| 41 | ||
| 42 | js_persistent_t<js_arraybuffer_t> ctx; | |
| 43 | }; | |
| 44 | ||
| 45 | struct rocksdb_native_t { | |
| 46 | rocksdb_t handle; | |
| 47 | rocksdb_options_t options; | |
| 48 | ||
| 49 | js_env_t *env; | |
| 50 | js_persistent_t<js_receiver_t> ctx; | |
| 51 | ||
| 52 | bool closing; | |
| 53 | bool exiting; | |
| 54 | ||
| 55 | std::set<rocksdb_native_column_family_t *> column_families; | |
| 56 | std::set<rocksdb_native_snapshot_t *> snapshots; | |
| 57 | ||
| 58 | js_deferred_teardown_t *teardown; | |
| 59 | }; | |
| 60 | ||
| 61 | struct rocksdb_native_open_t { | |
| 62 | rocksdb_open_t handle; | |
| 63 | ||
| 64 | js_env_t *env; | |
| 65 | js_persistent_t<js_receiver_t> ctx; | |
| 66 | js_persistent_t<rocksdb_native_on_open_t> on_open; | |
| 67 | ||
| 68 | js_persistent_t<js_array_t> column_families; | |
| 69 | }; | |
| 70 | ||
| 71 | struct rocksdb_native_close_t { | |
| 72 | rocksdb_close_t handle; | |
| 73 | ||
| 74 | js_env_t *env; | |
| 75 | js_persistent_t<js_receiver_t> ctx; | |
| 76 | js_persistent_t<rocksdb_native_on_close_t> on_close; | |
| 77 | }; | |
| 78 | ||
| 79 | struct rocksdb_native_suspend_t { | |
| 80 | rocksdb_suspend_t handle; | |
| 81 | ||
| 82 | js_env_t *env; | |
| 83 | js_persistent_t<js_receiver_t> ctx; | |
| 84 | js_persistent_t<rocksdb_native_on_suspend_t> on_suspend; | |
| 85 | }; | |
| 86 | ||
| 87 | struct rocksdb_native_resume_t { | |
| 88 | rocksdb_resume_t handle; | |
| 89 | ||
| 90 | js_env_t *env; | |
| 91 | js_persistent_t<js_receiver_t> ctx; | |
| 92 | js_persistent_t<rocksdb_native_on_resume_t> on_resume; | |
| 93 | }; | |
| 94 | ||
| 95 | struct rocksdb_native_iterator_t { | |
| 96 | rocksdb_iterator_t handle; | |
| 97 | ||
| 98 | rocksdb_slice_t *keys; | |
| 99 | rocksdb_slice_t *values; | |
| 100 | ||
| 101 | js_env_t *env; | |
| 102 | js_persistent_t<js_receiver_t> ctx; | |
| 103 | js_persistent_t<rocksdb_native_on_iterator_open_t> on_open; | |
| 104 | js_persistent_t<rocksdb_native_on_iterator_close_t> on_close; | |
| 105 | js_persistent_t<rocksdb_native_on_iterator_read_t> on_read; | |
| 106 | ||
| 107 | bool closing; | |
| 108 | bool exiting; | |
| 109 | ||
| 110 | js_deferred_teardown_t *teardown; | |
| 111 | }; | |
| 112 | ||
| 113 | struct rocksdb_native_read_batch_t { | |
| 114 | rocksdb_read_batch_t handle; | |
| 115 | ||
| 116 | rocksdb_read_t *reads; | |
| 117 | ||
| 118 | size_t capacity; | |
| 119 | ||
| 120 | js_env_t *env; | |
| 121 | js_persistent_t<js_receiver_t> ctx; | |
| 122 | js_persistent_t<rocksdb_native_on_read_t> on_read; | |
| 123 | }; | |
| 124 | ||
| 125 | struct rocksdb_native_write_batch_t { | |
| 126 | rocksdb_write_batch_t handle; | |
| 127 | ||
| 128 | rocksdb_write_t *writes; | |
| 129 | ||
| 130 | size_t capacity; | |
| 131 | ||
| 132 | js_env_t *env; | |
| 133 | js_persistent_t<js_receiver_t> ctx; | |
| 134 | js_persistent_t<rocksdb_native_on_write_t> on_write; | |
| 135 | }; | |
| 136 | ||
| 137 | struct rocksdb_native_flush_t { | |
| 138 | rocksdb_flush_t handle; | |
| 139 | ||
| 140 | js_env_t *env; | |
| 141 | js_persistent_t<js_receiver_t> ctx; | |
| 142 | js_persistent_t<rocksdb_native_on_flush_t> on_flush; | |
| 143 | ||
| 144 | js_persistent_t<rocksdb_native_column_family_t> column_family; | |
| 145 | }; | |
| 146 | ||
| 147 | struct rocksdb_native_compact_range_t { | |
| 148 | rocksdb_compact_range_t handle; | |
| 149 | ||
| 150 | js_env_t *env; | |
| 151 | js_persistent_t<js_receiver_t> ctx; | |
| 152 | js_persistent_t<rocksdb_native_on_compact_range_t> on_compact_range; | |
| 153 | }; | |
| 154 | ||
| 155 | struct rocksdb_native_approximate_size_t { | |
| 156 | rocksdb_approximate_size_t handle; | |
| 157 | ||
| 158 | js_env_t *env; | |
| 159 | js_persistent_t<js_receiver_t> ctx; | |
| 160 | js_persistent_t<rocksdb_native_on_approximate_size_t> on_approximate_size; | |
| 161 | }; | |
| 162 | ||
| 163 | static void | |
| 164 | rocksdb_native__on_free(js_env_t *env, char *data) { | |
| 165 | free(data); | |
| 166 | } | |
| 167 | ||
| 168 | static int | |
| 169 | rocksdb_native__try_create_external_arraybuffer(js_env_t *env, char *data, size_t len, js_arraybuffer_t &result) { | |
| 170 | int err; | |
| 171 | ||
| 172 | err = js_create_external_arraybuffer<rocksdb_native__on_free>(env, data, len, result); | |
| 173 | if (err == 0) return 0; | |
| 174 | ||
| 175 | err = js_create_arraybuffer(env, data, len, result); | |
| 176 | assert(err == 0); | |
| 177 | ||
| 178 | free(data); | |
| 179 | ||
| 180 | return 0; | |
| 181 | } | |
| 182 | ||
| 183 | static void | |
| 184 | rocksdb_native__on_open(rocksdb_open_t *handle, int status) { | |
| 185 | int err; | |
| 186 | ||
| 187 | assert(status == 0); | |
| 188 | ||
| 189 | auto req = reinterpret_cast<rocksdb_native_open_t *>(handle->data); | |
| 190 | ||
| 191 | auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db); | |
| 192 | ||
| 193 | auto env = req->env; | |
| 194 | ||
| 195 | auto descriptors = handle->column_families; | |
| 196 | auto handles = handle->handles; | |
| 197 | ||
| 198 | js_handle_scope_t *scope; | |
| 199 | err = js_open_handle_scope(env, &scope); | |
| 200 | assert(err == 0); | |
| 201 | ||
| 202 | js_receiver_t ctx; | |
| 203 | err = js_get_reference_value(env, req->ctx, ctx); | |
| 204 | assert(err == 0); | |
| 205 | ||
| 206 | rocksdb_native_on_open_t cb; | |
| 207 | err = js_get_reference_value(env, req->on_open, cb); | |
| 208 | assert(err == 0); | |
| 209 | ||
| 210 | js_array_t column_families; | |
| 211 | err = js_get_reference_value(env, req->column_families, column_families); | |
| 212 | assert(err == 0); | |
| 213 | ||
| 214 | req->on_open.reset(); | |
| 215 | req->column_families.reset(); | |
| 216 | req->ctx.reset(); | |
| 217 | ||
| 218 | std::optional<js_object_t> error; | |
| 219 | ||
| 220 | if (req->handle.error) { | |
| 221 | err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace()); | |
| 222 | assert(err == 0); | |
| 223 | } else { | |
| 224 | std::vector<js_arraybuffer_t> elements; | |
| 225 | err = js_get_array_elements(env, column_families, elements); | |
| 226 | assert(err == 0); | |
| 227 | ||
| 228 | const auto len = elements.size(); | |
| 229 | ||
| 230 | for (uint32_t i = 0; i < len; i++) { | |
| 231 | if (db->exiting) rocksdb_column_family_destroy(&db->handle, handles[i]); | |
| 232 | else { | |
| 233 | js_arraybuffer_t handle = elements[i]; | |
| 234 | ||
| 235 | rocksdb_native_column_family_t *column_family; | |
| 236 | err = js_get_arraybuffer_info(env, handle, column_family); | |
| 237 | assert(err == 0); | |
| 238 | ||
| 239 | column_family->handle = handles[i]; | |
| 240 | ||
| 241 | err = js_create_reference(env, handle, column_family->ctx); | |
| 242 | assert(err == 0); | |
| 243 | ||
| 244 | db->column_families.insert(column_family); | |
| 245 | } | |
| 246 | } | |
| 247 | } | |
| 248 | ||
| 249 | if (!db->exiting) { | |
| 250 | err = js_call_function_with_checkpoint(env, cb, ctx, error); | |
| 251 | (void) err; | |
| 252 | } | |
| 253 | ||
| 254 | err = js_close_handle_scope(env, scope); | |
| 255 | assert(err == 0); | |
| 256 | ||
| 257 | delete[] descriptors; | |
| 258 | delete[] handles; | |
| 259 | } | |
| 260 | ||
| 261 | static void | |
| 262 | rocksdb_native__on_idle(rocksdb_t *handle) { | |
| 263 | int err; | |
| 264 | ||
| 265 | auto db = reinterpret_cast<rocksdb_native_t *>(handle); | |
| 266 | ||
| 267 | for (auto &column_family : db->column_families) { | |
| 268 | err = rocksdb_column_family_destroy(&column_family->db->handle, column_family->handle); | |
| 269 | assert(err == 0); | |
| 270 | ||
| 271 | column_family->handle = nullptr; | |
| 272 | ||
| 273 | column_family->ctx.reset(); | |
| 274 | } | |
| 275 | ||
| 276 | for (auto &snapshot : db->snapshots) { | |
| 277 | rocksdb_snapshot_destroy(&snapshot->handle); | |
| 278 | ||
| 279 | snapshot->ctx.reset(); | |
| 280 | } | |
| 281 | ||
| 282 | db->column_families.~set(); | |
| 283 | db->snapshots.~set(); | |
| 284 | } | |
| 285 | ||
| 286 | static void | |
| 287 | rocksdb_native__on_close(rocksdb_close_t *handle, int status) { | |
| 288 | int err; | |
| 289 | ||
| 290 | assert(status == 0); | |
| 291 | ||
| 292 | auto req = reinterpret_cast<rocksdb_native_close_t *>(handle->data); | |
| 293 | ||
| 294 | auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db); | |
| 295 | ||
| 296 | auto env = req->env; | |
| 297 | ||
| 298 | auto teardown = db->teardown; | |
| 299 | ||
| 300 | if (db->closing) { | |
| 301 | js_handle_scope_t *scope; | |
| 302 | err = js_open_handle_scope(env, &scope); | |
| 303 | assert(err == 0); | |
| 304 | ||
| 305 | js_receiver_t ctx; | |
| 306 | err = js_get_reference_value(env, req->ctx, ctx); | |
| 307 | assert(err == 0); | |
| 308 | ||
| 309 | rocksdb_native_on_close_t cb; | |
| 310 | err = js_get_reference_value(env, req->on_close, cb); | |
| 311 | assert(err == 0); | |
| 312 | ||
| 313 | req->on_close.reset(); | |
| 314 | req->ctx.reset(); | |
| 315 | ||
| 316 | if (!db->exiting) { | |
| 317 | err = js_call_function_with_checkpoint(env, cb, ctx); | |
| 318 | (void) err; | |
| 319 | } | |
| 320 | ||
| 321 | err = js_close_handle_scope(env, scope); | |
| 322 | assert(err == 0); | |
| 323 | } else { | |
| 324 | delete req; | |
| 325 | } | |
| 326 | ||
| 327 | db->ctx.reset(); | |
| 328 | ||
| 329 | err = js_finish_deferred_teardown_callback(teardown); | |
| 330 | assert(err == 0); | |
| 331 | } | |
| 332 | ||
| 333 | static void | |
| 334 | rocksdb_native__on_teardown(js_deferred_teardown_t *teardown, void *data) { | |
| 335 | int err; | |
| 336 | ||
| 337 | auto db = reinterpret_cast<rocksdb_native_t *>(data); | |
| 338 | ||
| 339 | db->exiting = true; | |
| 340 | ||
| 341 | if (db->closing) return; | |
| 342 | ||
| 343 | auto env = db->env; | |
| 344 | ||
| 345 | auto req = new rocksdb_native_close_t(); | |
| 346 | ||
| 347 | req->env = env; | |
| 348 | req->handle.data = req; | |
| 349 | ||
| 350 | err = rocksdb_close(&db->handle, &req->handle, rocksdb_native__on_idle, rocksdb_native__on_close); | |
| 351 | assert(err == 0); | |
| 352 | } | |
| 353 | ||
| 354 | static js_arraybuffer_t | |
| 355 | rocksdb_native_init( | |
| 356 | js_env_t *env, | |
| 357 | bool read_only, | |
| 358 | bool create_if_missing, | |
| 359 | bool create_missing_column_families, | |
| 360 | int32_t max_background_jobs, | |
| 361 | uint64_t bytes_per_sync, | |
| 362 | int32_t max_open_files, | |
| 363 | bool use_direct_reads, | |
| 364 | bool avoid_unnecessary_blocking_io, | |
| 365 | bool skip_stats_update_on_db_open, | |
| 366 | bool use_direct_io_for_flush_and_compaction, | |
| 367 | int32_t max_file_opening_threads, | |
| 368 | uint32_t wal_recovery_mode, | |
| 369 | bool best_efforts_recovery | |
| 370 | ) { | |
| 371 | int err; | |
| 372 | ||
| 373 | js_arraybuffer_t handle; | |
| 374 | ||
| 375 | rocksdb_native_t *db; | |
| 376 | err = js_create_arraybuffer(env, db, handle); | |
| 377 | assert(err == 0); | |
| 378 | ||
| 379 | db->env = env; | |
| 380 | db->closing = false; | |
| 381 | db->exiting = false; | |
| 382 | ||
| 383 | new (&db->column_families) std::set<rocksdb_native_column_family_t *>(); | |
| 384 | new (&db->snapshots) std::set<rocksdb_native_snapshot_t *>(); | |
| 385 | ||
| 386 | db->options = (rocksdb_options_t) { | |
| 387 | 5, | |
| 388 | read_only, | |
| 389 | create_if_missing, | |
| 390 | create_missing_column_families, | |
| 391 | max_background_jobs, | |
| 392 | bytes_per_sync, | |
| 393 | max_open_files, | |
| 394 | use_direct_reads, | |
| 395 | avoid_unnecessary_blocking_io, | |
| 396 | skip_stats_update_on_db_open, | |
| 397 | use_direct_io_for_flush_and_compaction, | |
| 398 | max_file_opening_threads, | |
| 399 | -1, | |
| 400 | rocksdb_wal_recovery_mode_t(wal_recovery_mode), | |
| 401 | best_efforts_recovery, | |
| 402 | }; | |
| 403 | ||
| 404 | return handle; | |
| 405 | } | |
| 406 | ||
| 407 | static js_arraybuffer_t | |
| 408 | rocksdb_native_open( | |
| 409 | js_env_t *env, | |
| 410 | js_arraybuffer_span_of_t<rocksdb_native_t, 1> db, | |
| 411 | js_receiver_t self, | |
| 412 | char *path, | |
| 413 | js_array_t column_families_array, | |
| 414 | int lock, | |
| 415 | js_receiver_t ctx, | |
| 416 | rocksdb_native_on_open_t on_open | |
| 417 | ) { | |
| 418 | int err; | |
| 419 | ||
| 420 | std::vector<js_arraybuffer_t> elements; | |
| 421 | err = js_get_array_elements(env, column_families_array, elements); | |
| 422 | assert(err == 0); | |
| 423 | ||
| 424 | const auto len = elements.size(); | |
| 425 | ||
| 426 | auto column_families = new rocksdb_column_family_descriptor_t[len]; | |
| 427 | ||
| 428 | for (uint32_t i = 0; i < len; i++) { | |
| 429 | js_arraybuffer_t handle = elements[i]; | |
| 430 | ||
| 431 | rocksdb_native_column_family_t *column_family; | |
| 432 | err = js_get_arraybuffer_info(env, handle, column_family); | |
| 433 | assert(err == 0); | |
| 434 | ||
| 435 | memcpy(&column_families[i], &column_family->descriptor, sizeof(rocksdb_column_family_descriptor_t)); | |
| 436 | ||
| 437 | column_family->db = db; | |
| 438 | } | |
| 439 | ||
| 440 | auto handles = new rocksdb_column_family_t *[len]; | |
| 441 | ||
| 442 | uv_loop_t *loop; | |
| 443 | err = js_get_env_loop(env, &loop); | |
| 444 | assert(err == 0); | |
| 445 | ||
| 446 | js_arraybuffer_t handle; | |
| 447 | ||
| 448 | rocksdb_native_open_t *req; | |
| 449 | err = js_create_arraybuffer(env, req, handle); | |
| 450 | assert(err == 0); | |
| 451 | ||
| 452 | req->env = env; | |
| 453 | req->handle.data = req; | |
| 454 | ||
| 455 | db->options.lock = lock; | |
| 456 | ||
| 457 | err = rocksdb_open(loop, &db->handle, &req->handle, path, &db->options, column_families, handles, len, nullptr, rocksdb_native__on_open); | |
| 458 | ||
| 459 | if (err < 0) { | |
| 460 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 461 | assert(err == 0); | |
| 462 | ||
| 463 | if (lock >= 0) { | |
| 464 | uv_fs_t fs; | |
| 465 | err = uv_fs_close(NULL, &fs, lock, NULL); | |
| 466 | assert(err == 0); | |
| 467 | } | |
| 468 | ||
| 469 | throw js_pending_exception; | |
| 470 | } | |
| 471 | ||
| 472 | err = js_create_reference(env, self, db->ctx); | |
| 473 | assert(err == 0); | |
| 474 | ||
| 475 | err = js_create_reference(env, ctx, req->ctx); | |
| 476 | assert(err == 0); | |
| 477 | ||
| 478 | err = js_create_reference(env, on_open, req->on_open); | |
| 479 | assert(err == 0); | |
| 480 | ||
| 481 | err = js_create_reference(env, column_families_array, req->column_families); | |
| 482 | assert(err == 0); | |
| 483 | ||
| 484 | err = js_add_deferred_teardown_callback(env, rocksdb_native__on_teardown, db, &db->teardown); | |
| 485 | assert(err == 0); | |
| 486 | ||
| 487 | return handle; | |
| 488 | } | |
| 489 | ||
| 490 | static js_arraybuffer_t | |
| 491 | rocksdb_native_close( | |
| 492 | js_env_t *env, | |
| 493 | js_arraybuffer_span_of_t<rocksdb_native_t, 1> db, | |
| 494 | js_receiver_t ctx, | |
| 495 | rocksdb_native_on_close_t on_close | |
| 496 | ) { | |
| 497 | int err; | |
| 498 | ||
| 499 | js_arraybuffer_t handle; | |
| 500 | ||
| 501 | rocksdb_native_close_t *req; | |
| 502 | err = js_create_arraybuffer(env, req, handle); | |
| 503 | assert(err == 0); | |
| 504 | ||
| 505 | req->env = env; | |
| 506 | req->handle.data = req; | |
| 507 | ||
| 508 | err = rocksdb_close(&db->handle, &req->handle, rocksdb_native__on_idle, rocksdb_native__on_close); | |
| 509 | ||
| 510 | if (err < 0) { | |
| 511 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 512 | assert(err == 0); | |
| 513 | ||
| 514 | throw js_pending_exception; | |
| 515 | } | |
| 516 | ||
| 517 | err = js_create_reference(env, ctx, req->ctx); | |
| 518 | assert(err == 0); | |
| 519 | ||
| 520 | err = js_create_reference(env, on_close, req->on_close); | |
| 521 | assert(err == 0); | |
| 522 | ||
| 523 | db->closing = true; | |
| 524 | ||
| 525 | return handle; | |
| 526 | } | |
| 527 | ||
| 528 | static void | |
| 529 | rocksdb_native__on_suspend(rocksdb_suspend_t *handle, int status) { | |
| 530 | int err; | |
| 531 | ||
| 532 | assert(status == 0); | |
| 533 | ||
| 534 | auto req = reinterpret_cast<rocksdb_native_suspend_t *>(handle->data); | |
| 535 | ||
| 536 | auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db); | |
| 537 | ||
| 538 | auto env = req->env; | |
| 539 | ||
| 540 | js_handle_scope_t *scope; | |
| 541 | err = js_open_handle_scope(env, &scope); | |
| 542 | assert(err == 0); | |
| 543 | ||
| 544 | js_receiver_t ctx; | |
| 545 | err = js_get_reference_value(env, req->ctx, ctx); | |
| 546 | assert(err == 0); | |
| 547 | ||
| 548 | rocksdb_native_on_suspend_t cb; | |
| 549 | err = js_get_reference_value(env, req->on_suspend, cb); | |
| 550 | assert(err == 0); | |
| 551 | ||
| 552 | req->on_suspend.reset(); | |
| 553 | req->ctx.reset(); | |
| 554 | ||
| 555 | std::optional<js_object_t> error; | |
| 556 | ||
| 557 | if (req->handle.error) { | |
| 558 | err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace()); | |
| 559 | assert(err == 0); | |
| 560 | } | |
| 561 | ||
| 562 | if (!db->exiting) { | |
| 563 | err = js_call_function_with_checkpoint(env, cb, ctx, error); | |
| 564 | (void) err; | |
| 565 | } | |
| 566 | ||
| 567 | err = js_close_handle_scope(env, scope); | |
| 568 | assert(err == 0); | |
| 569 | } | |
| 570 | ||
| 571 | static js_arraybuffer_t | |
| 572 | rocksdb_native_suspend( | |
| 573 | js_env_t *env, | |
| 574 | js_arraybuffer_span_of_t<rocksdb_native_t, 1> db, | |
| 575 | js_receiver_t ctx, | |
| 576 | rocksdb_native_on_suspend_t on_suspend | |
| 577 | ) { | |
| 578 | int err; | |
| 579 | ||
| 580 | js_arraybuffer_t handle; | |
| 581 | ||
| 582 | rocksdb_native_suspend_t *req; | |
| 583 | err = js_create_arraybuffer(env, req, handle); | |
| 584 | assert(err == 0); | |
| 585 | ||
| 586 | req->env = env; | |
| 587 | req->handle.data = req; | |
| 588 | ||
| 589 | err = rocksdb_suspend(&db->handle, &req->handle, rocksdb_native__on_suspend); | |
| 590 | ||
| 591 | if (err < 0) { | |
| 592 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 593 | assert(err == 0); | |
| 594 | ||
| 595 | throw js_pending_exception; | |
| 596 | } | |
| 597 | ||
| 598 | err = js_create_reference(env, ctx, req->ctx); | |
| 599 | assert(err == 0); | |
| 600 | ||
| 601 | err = js_create_reference(env, on_suspend, req->on_suspend); | |
| 602 | assert(err == 0); | |
| 603 | ||
| 604 | return handle; | |
| 605 | } | |
| 606 | ||
| 607 | static void | |
| 608 | rocksdb_native__on_resume(rocksdb_resume_t *handle, int status) { | |
| 609 | int err; | |
| 610 | ||
| 611 | assert(status == 0); | |
| 612 | ||
| 613 | auto req = reinterpret_cast<rocksdb_native_resume_t *>(handle->data); | |
| 614 | ||
| 615 | auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db); | |
| 616 | ||
| 617 | auto env = req->env; | |
| 618 | ||
| 619 | js_handle_scope_t *scope; | |
| 620 | err = js_open_handle_scope(env, &scope); | |
| 621 | assert(err == 0); | |
| 622 | ||
| 623 | js_receiver_t ctx; | |
| 624 | err = js_get_reference_value(env, req->ctx, ctx); | |
| 625 | assert(err == 0); | |
| 626 | ||
| 627 | rocksdb_native_on_resume_t cb; | |
| 628 | err = js_get_reference_value(env, req->on_resume, cb); | |
| 629 | assert(err == 0); | |
| 630 | ||
| 631 | req->on_resume.reset(); | |
| 632 | req->ctx.reset(); | |
| 633 | ||
| 634 | std::optional<js_object_t> error; | |
| 635 | ||
| 636 | if (req->handle.error) { | |
| 637 | err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace()); | |
| 638 | assert(err == 0); | |
| 639 | } | |
| 640 | ||
| 641 | if (!db->exiting) { | |
| 642 | err = js_call_function_with_checkpoint(env, cb, ctx, error); | |
| 643 | (void) err; | |
| 644 | } | |
| 645 | ||
| 646 | err = js_close_handle_scope(env, scope); | |
| 647 | assert(err == 0); | |
| 648 | } | |
| 649 | ||
| 650 | static js_arraybuffer_t | |
| 651 | rocksdb_native_resume( | |
| 652 | js_env_t *env, | |
| 653 | js_arraybuffer_span_of_t<rocksdb_native_t, 1> db, | |
| 654 | js_receiver_t ctx, | |
| 655 | rocksdb_native_on_resume_t on_resume | |
| 656 | ) { | |
| 657 | int err; | |
| 658 | ||
| 659 | js_arraybuffer_t handle; | |
| 660 | ||
| 661 | rocksdb_native_resume_t *req; | |
| 662 | err = js_create_arraybuffer(env, req, handle); | |
| 663 | assert(err == 0); | |
| 664 | ||
| 665 | req->env = env; | |
| 666 | req->handle.data = req; | |
| 667 | ||
| 668 | err = rocksdb_resume(&db->handle, &req->handle, rocksdb_native__on_resume); | |
| 669 | ||
| 670 | if (err < 0) { | |
| 671 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 672 | assert(err == 0); | |
| 673 | ||
| 674 | throw js_pending_exception; | |
| 675 | } | |
| 676 | ||
| 677 | err = js_create_reference(env, ctx, req->ctx); | |
| 678 | assert(err == 0); | |
| 679 | ||
| 680 | err = js_create_reference(env, on_resume, req->on_resume); | |
| 681 | assert(err == 0); | |
| 682 | ||
| 683 | return handle; | |
| 684 | } | |
| 685 | ||
| 686 | static js_arraybuffer_t | |
| 687 | rocksdb_native_column_family_init( | |
| 688 | js_env_t *env, | |
| 689 | char *name, | |
| 690 | bool enable_blob_files, | |
| 691 | uint64_t min_blob_size, | |
| 692 | uint64_t blob_file_size, | |
| 693 | bool enable_blob_garbage_collection, | |
| 694 | uint64_t block_size, | |
| 695 | bool cache_index_and_filter_blocks, | |
| 696 | uint32_t format_version, | |
| 697 | bool optimize_filters_for_memory, | |
| 698 | bool no_block_cache, | |
| 699 | uint32_t filter_policy_type, | |
| 700 | double bits_per_key, | |
| 701 | int32_t bloom_before_level, | |
| 702 | uint32_t top_level_index_pinning_tier, | |
| 703 | uint32_t partition_pinning_tier, | |
| 704 | uint32_t unpartitioned_pinning_tier, | |
| 705 | bool optimize_filters_for_hits, | |
| 706 | int32_t num_levels, | |
| 707 | int32_t max_write_buffer_number, | |
| 708 | double blob_garbage_collection_age_cutoff, | |
| 709 | double blob_garbage_collection_force_threshold | |
| 710 | ) { | |
| 711 | int err; | |
| 712 | ||
| 713 | rocksdb_filter_policy_t filter_policy = {rocksdb_filter_policy_type_t(filter_policy_type)}; | |
| 714 | ||
| 715 | switch (filter_policy_type) { | |
| 716 | case rocksdb_bloom_filter_policy: | |
| 717 | filter_policy.bloom = (rocksdb_bloom_filter_options_t) { | |
| 718 | 0, | |
| 719 | bits_per_key | |
| 720 | }; | |
| 721 | break; | |
| 722 | ||
| 723 | case rocksdb_ribbon_filter_policy: | |
| 724 | filter_policy.ribbon = (rocksdb_ribbon_filter_options_t) { | |
| 725 | 0, | |
| 726 | bits_per_key, | |
| 727 | bloom_before_level, | |
| 728 | }; | |
| 729 | break; | |
| 730 | } | |
| 731 | ||
| 732 | uv_loop_t *loop; | |
| 733 | err = js_get_env_loop(env, &loop); | |
| 734 | assert(err == 0); | |
| 735 | ||
| 736 | js_arraybuffer_t handle; | |
| 737 | ||
| 738 | rocksdb_native_column_family_t *column_family; | |
| 739 | err = js_create_arraybuffer(env, column_family, handle); | |
| 740 | assert(err == 0); | |
| 741 | ||
| 742 | column_family->db = nullptr; | |
| 743 | column_family->handle = nullptr; | |
| 744 | ||
| 745 | column_family->descriptor = (rocksdb_column_family_descriptor_t) { | |
| 746 | name, | |
| 747 | { | |
| 748 | 5, | |
| 749 | rocksdb_level_compaction, | |
| 750 | enable_blob_files, | |
| 751 | min_blob_size, | |
| 752 | blob_file_size, | |
| 753 | enable_blob_garbage_collection, | |
| 754 | block_size, | |
| 755 | cache_index_and_filter_blocks, | |
| 756 | format_version, | |
| 757 | optimize_filters_for_memory, | |
| 758 | no_block_cache, | |
| 759 | filter_policy, | |
| 760 | rocksdb_pinning_tier_t(top_level_index_pinning_tier), | |
| 761 | rocksdb_pinning_tier_t(partition_pinning_tier), | |
| 762 | rocksdb_pinning_tier_t(unpartitioned_pinning_tier), | |
| 763 | optimize_filters_for_hits, | |
| 764 | num_levels, | |
| 765 | max_write_buffer_number, | |
| 766 | blob_garbage_collection_age_cutoff, | |
| 767 | blob_garbage_collection_force_threshold, | |
| 768 | } | |
| 769 | }; | |
| 770 | ||
| 771 | return handle; | |
| 772 | } | |
| 773 | ||
| 774 | static void | |
| 775 | rocksdb_native_column_family_destroy( | |
| 776 | js_env_t *env, | |
| 777 | js_arraybuffer_span_of_t<rocksdb_native_column_family_t, 1> column_family | |
| 778 | ) { | |
| 779 | int err; | |
| 780 | ||
| 781 | if (column_family->handle == nullptr) return; | |
| 782 | ||
| 783 | err = rocksdb_column_family_destroy(&column_family->db->handle, column_family->handle); | |
| 784 | assert(err == 0); | |
| 785 | ||
| 786 | column_family->handle = nullptr; | |
| 787 | ||
| 788 | column_family->db->column_families.erase(column_family); | |
| 789 | ||
| 790 | column_family->ctx.reset(); | |
| 791 | } | |
| 792 | ||
| 793 | static js_arraybuffer_t | |
| 794 | rocksdb_native_iterator_init(js_env_t *env) { | |
| 795 | int err; | |
| 796 | ||
| 797 | js_arraybuffer_t handle; | |
| 798 | ||
| 799 | rocksdb_native_iterator_t *req; | |
| 800 | err = js_create_arraybuffer(env, req, handle); | |
| 801 | assert(err == 0); | |
| 802 | ||
| 803 | req->env = env; | |
| 804 | req->closing = false; | |
| 805 | req->exiting = false; | |
| 806 | req->handle.data = req; | |
| 807 | ||
| 808 | return handle; | |
| 809 | } | |
| 810 | ||
| 811 | static js_arraybuffer_t | |
| 812 | rocksdb_native_iterator_buffer( | |
| 813 | js_env_t *env, | |
| 814 | js_arraybuffer_span_of_t<rocksdb_native_iterator_t, 1> req, | |
| 815 | uint32_t capacity | |
| 816 | ) { | |
| 817 | int err; | |
| 818 | ||
| 819 | js_arraybuffer_t handle; | |
| 820 | ||
| 821 | uint8_t *data; | |
| 822 | err = js_create_arraybuffer(env, 2 * capacity * sizeof(rocksdb_slice_t), data, handle); | |
| 823 | assert(err == 0); | |
| 824 | ||
| 825 | size_t offset = 0; | |
| 826 | ||
| 827 | req->keys = reinterpret_cast<rocksdb_slice_t *>(&data[offset]); | |
| 828 | ||
| 829 | offset += capacity * sizeof(rocksdb_slice_t); | |
| 830 | ||
| 831 | req->values = reinterpret_cast<rocksdb_slice_t *>(&data[offset]); | |
| 832 | ||
| 833 | return handle; | |
| 834 | } | |
| 835 | ||
| 836 | static void | |
| 837 | rocksdb_native__on_iterator_close(rocksdb_iterator_t *handle, int status) { | |
| 838 | int err; | |
| 839 | ||
| 840 | assert(status == 0); | |
| 841 | ||
| 842 | auto req = reinterpret_cast<rocksdb_native_iterator_t *>(handle->data); | |
| 843 | ||
| 844 | auto env = req->env; | |
| 845 | ||
| 846 | auto teardown = req->teardown; | |
| 847 | ||
| 848 | js_handle_scope_t *scope; | |
| 849 | err = js_open_handle_scope(env, &scope); | |
| 850 | assert(err == 0); | |
| 851 | ||
| 852 | js_receiver_t ctx; | |
| 853 | err = js_get_reference_value(env, req->ctx, ctx); | |
| 854 | assert(err == 0); | |
| 855 | ||
| 856 | rocksdb_native_on_iterator_close_t cb; | |
| 857 | err = js_get_reference_value(env, req->on_close, cb); | |
| 858 | assert(err == 0); | |
| 859 | ||
| 860 | req->on_open.reset(); | |
| 861 | req->on_close.reset(); | |
| 862 | req->on_read.reset(); | |
| 863 | req->ctx.reset(); | |
| 864 | ||
| 865 | std::optional<js_object_t> error; | |
| 866 | ||
| 867 | if (req->handle.error) { | |
| 868 | err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace()); | |
| 869 | assert(err == 0); | |
| 870 | } | |
| 871 | ||
| 872 | if (!req->exiting) { | |
| 873 | err = js_call_function_with_checkpoint(env, cb, ctx, error); | |
| 874 | (void) err; | |
| 875 | } | |
| 876 | ||
| 877 | err = js_close_handle_scope(env, scope); | |
| 878 | assert(err == 0); | |
| 879 | ||
| 880 | err = js_finish_deferred_teardown_callback(teardown); | |
| 881 | assert(err == 0); | |
| 882 | } | |
| 883 | ||
| 884 | static void | |
| 885 | rocksdb_native__on_iterator_open(rocksdb_iterator_t *handle, int status) { | |
| 886 | int err; | |
| 887 | ||
| 888 | assert(status == 0); | |
| 889 | ||
| 890 | auto req = reinterpret_cast<rocksdb_native_iterator_t *>(handle->data); | |
| 891 | ||
| 892 | auto env = req->env; | |
| 893 | ||
| 894 | js_handle_scope_t *scope; | |
| 895 | err = js_open_handle_scope(env, &scope); | |
| 896 | assert(err == 0); | |
| 897 | ||
| 898 | js_receiver_t ctx; | |
| 899 | err = js_get_reference_value(env, req->ctx, ctx); | |
| 900 | assert(err == 0); | |
| 901 | ||
| 902 | rocksdb_native_on_iterator_open_t cb; | |
| 903 | err = js_get_reference_value(env, req->on_open, cb); | |
| 904 | assert(err == 0); | |
| 905 | ||
| 906 | std::optional<js_object_t> error; | |
| 907 | ||
| 908 | if (req->handle.error) { | |
| 909 | err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace()); | |
| 910 | assert(err == 0); | |
| 911 | } | |
| 912 | ||
| 913 | if (!req->exiting) { | |
| 914 | err = js_call_function_with_checkpoint(env, cb, ctx, error); | |
| 915 | (void) err; | |
| 916 | } | |
| 917 | ||
| 918 | err = js_close_handle_scope(env, scope); | |
| 919 | assert(err == 0); | |
| 920 | } | |
| 921 | ||
| 922 | static void | |
| 923 | rocksdb_native__on_iterator_teardown(js_deferred_teardown_t *teardown, void *data) { | |
| 924 | int err; | |
| 925 | ||
| 926 | auto req = reinterpret_cast<rocksdb_native_iterator_t *>(data); | |
| 927 | ||
| 928 | req->exiting = true; | |
| 929 | ||
| 930 | if (req->closing) return; | |
| 931 | ||
| 932 | err = rocksdb_iterator_close(&req->handle, rocksdb_native__on_iterator_close); | |
| 933 | assert(err == 0); | |
| 934 | } | |
| 935 | ||
| 936 | static void | |
| 937 | rocksdb_native_iterator_open( | |
| 938 | js_env_t *env, | |
| 939 | js_arraybuffer_span_of_t<rocksdb_native_t, 1> db, | |
| 940 | js_arraybuffer_span_of_t<rocksdb_native_iterator_t, 1> req, | |
| 941 | js_arraybuffer_span_of_t<rocksdb_native_column_family_t, 1> column_family, | |
| 942 | js_typedarray_t<> gt, | |
| 943 | js_typedarray_t<> gte, | |
| 944 | js_typedarray_t<> lt, | |
| 945 | js_typedarray_t<> lte, | |
| 946 | bool reverse, | |
| 947 | bool keys_only, | |
| 948 | std::optional<js_arraybuffer_span_of_t<rocksdb_native_snapshot_t, 1>> snapshot, | |
| 949 | js_receiver_t ctx, | |
| 950 | rocksdb_native_on_iterator_open_t on_open, | |
| 951 | rocksdb_native_on_iterator_close_t on_close, | |
| 952 | rocksdb_native_on_iterator_read_t on_read | |
| 953 | ) { | |
| 954 | int err; | |
| 955 | ||
| 956 | rocksdb_range_t range; | |
| 957 | ||
| 958 | err = js_get_typedarray_info(env, gt, range.gt.data, range.gt.len); | |
| 959 | assert(err == 0); | |
| 960 | ||
| 961 | err = js_get_typedarray_info(env, gte, range.gte.data, range.gte.len); | |
| 962 | assert(err == 0); | |
| 963 | ||
| 964 | err = js_get_typedarray_info(env, lt, range.lt.data, range.lt.len); | |
| 965 | assert(err == 0); | |
| 966 | ||
| 967 | err = js_get_typedarray_info(env, lte, range.lte.data, range.lte.len); | |
| 968 | assert(err == 0); | |
| 969 | ||
| 970 | rocksdb_iterator_options_t options = { | |
| 971 | .version = 0, | |
| 972 | .reverse = reverse, | |
| 973 | .keys_only = keys_only | |
| 974 | }; | |
| 975 | ||
| 976 | if (snapshot) options.snapshot = &snapshot.value()->handle; | |
| 977 | ||
| 978 | err = rocksdb_iterator_open(&db->handle, &req->handle, column_family->handle, range, &options, rocksdb_native__on_iterator_open); | |
| 979 | ||
| 980 | if (err < 0) { | |
| 981 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 982 | assert(err == 0); | |
| 983 | ||
| 984 | throw js_pending_exception; | |
| 985 | } | |
| 986 | ||
| 987 | err = js_create_reference(env, ctx, req->ctx); | |
| 988 | assert(err == 0); | |
| 989 | ||
| 990 | err = js_create_reference(env, on_open, req->on_open); | |
| 991 | assert(err == 0); | |
| 992 | ||
| 993 | err = js_create_reference(env, on_close, req->on_close); | |
| 994 | assert(err == 0); | |
| 995 | ||
| 996 | err = js_create_reference(env, on_read, req->on_read); | |
| 997 | assert(err == 0); | |
| 998 | ||
| 999 | err = js_add_deferred_teardown_callback(env, rocksdb_native__on_iterator_teardown, req, &req->teardown); | |
| 1000 | assert(err == 0); | |
| 1001 | } | |
| 1002 | ||
| 1003 | static void | |
| 1004 | rocksdb_native_iterator_close(js_env_t *env, js_arraybuffer_span_of_t<rocksdb_native_iterator_t, 1> req) { | |
| 1005 | int err; | |
| 1006 | ||
| 1007 | err = rocksdb_iterator_close(&req->handle, rocksdb_native__on_iterator_close); | |
| 1008 | ||
| 1009 | if (err < 0) { | |
| 1010 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1011 | assert(err == 0); | |
| 1012 | ||
| 1013 | throw js_pending_exception; | |
| 1014 | } | |
| 1015 | ||
| 1016 | req->closing = true; | |
| 1017 | } | |
| 1018 | ||
| 1019 | static void | |
| 1020 | rocksdb_native__on_iterator_read(rocksdb_iterator_t *handle, int status) { | |
| 1021 | int err; | |
| 1022 | ||
| 1023 | assert(status == 0); | |
| 1024 | ||
| 1025 | auto req = reinterpret_cast<rocksdb_native_iterator_t *>(handle->data); | |
| 1026 | ||
| 1027 | auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db); | |
| 1028 | ||
| 1029 | auto len = req->handle.len; | |
| 1030 | ||
| 1031 | auto env = req->env; | |
| 1032 | ||
| 1033 | js_handle_scope_t *scope; | |
| 1034 | err = js_open_handle_scope(env, &scope); | |
| 1035 | assert(err == 0); | |
| 1036 | ||
| 1037 | js_receiver_t ctx; | |
| 1038 | err = js_get_reference_value(env, req->ctx, ctx); | |
| 1039 | assert(err == 0); | |
| 1040 | ||
| 1041 | rocksdb_native_on_iterator_read_t cb; | |
| 1042 | err = js_get_reference_value(env, req->on_read, cb); | |
| 1043 | assert(err == 0); | |
| 1044 | ||
| 1045 | std::optional<js_object_t> error; | |
| 1046 | ||
| 1047 | js_array_t keys; | |
| 1048 | err = js_create_array(env, len, keys); | |
| 1049 | assert(err == 0); | |
| 1050 | ||
| 1051 | js_array_t values; | |
| 1052 | err = js_create_array(env, len, values); | |
| 1053 | assert(err == 0); | |
| 1054 | ||
| 1055 | if (req->handle.error) { | |
| 1056 | err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace()); | |
| 1057 | assert(err == 0); | |
| 1058 | } else { | |
| 1059 | for (size_t i = 0; i < len; i++) { | |
| 1060 | rocksdb_slice_t *key = &req->keys[i]; | |
| 1061 | rocksdb_slice_t *value = &req->values[i]; | |
| 1062 | ||
| 1063 | if (req->exiting) { | |
| 1064 | rocksdb_slice_destroy(key); | |
| 1065 | rocksdb_slice_destroy(value); | |
| 1066 | } else { | |
| 1067 | js_arraybuffer_t result; | |
| 1068 | ||
| 1069 | err = rocksdb_native__try_create_external_arraybuffer(env, const_cast<char *>(key->data), key->len, result); | |
| 1070 | assert(err == 0); | |
| 1071 | ||
| 1072 | err = js_set_element(env, keys, i, result); | |
| 1073 | assert(err == 0); | |
| 1074 | ||
| 1075 | err = rocksdb_native__try_create_external_arraybuffer(env, const_cast<char *>(value->data), value->len, result); | |
| 1076 | assert(err == 0); | |
| 1077 | ||
| 1078 | err = js_set_element(env, values, i, result); | |
| 1079 | assert(err == 0); | |
| 1080 | } | |
| 1081 | } | |
| 1082 | } | |
| 1083 | ||
| 1084 | if (!req->exiting) { | |
| 1085 | err = js_call_function_with_checkpoint(env, cb, ctx, error, keys, values); | |
| 1086 | (void) err; | |
| 1087 | } | |
| 1088 | ||
| 1089 | err = js_close_handle_scope(env, scope); | |
| 1090 | assert(err == 0); | |
| 1091 | } | |
| 1092 | ||
| 1093 | static void | |
| 1094 | rocksdb_native_iterator_read( | |
| 1095 | js_env_t *env, | |
| 1096 | js_arraybuffer_span_of_t<rocksdb_native_iterator_t, 1> req, | |
| 1097 | uint32_t capacity | |
| 1098 | ) { | |
| 1099 | int err; | |
| 1100 | ||
| 1101 | err = rocksdb_iterator_read(&req->handle, req->keys, req->values, capacity, rocksdb_native__on_iterator_read); | |
| 1102 | ||
| 1103 | if (err < 0) { | |
| 1104 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1105 | assert(err == 0); | |
| 1106 | ||
| 1107 | throw js_pending_exception; | |
| 1108 | } | |
| 1109 | } | |
| 1110 | ||
| 1111 | static js_arraybuffer_t | |
| 1112 | rocksdb_native_read_init(js_env_t *env) { | |
| 1113 | int err; | |
| 1114 | ||
| 1115 | js_arraybuffer_t handle; | |
| 1116 | ||
| 1117 | rocksdb_native_read_batch_t *req; | |
| 1118 | err = js_create_arraybuffer(env, req, handle); | |
| 1119 | assert(err == 0); | |
| 1120 | ||
| 1121 | req->env = env; | |
| 1122 | req->handle.data = req; | |
| 1123 | ||
| 1124 | return handle; | |
| 1125 | } | |
| 1126 | ||
| 1127 | static js_arraybuffer_t | |
| 1128 | rocksdb_native_read_buffer( | |
| 1129 | js_env_t *env, | |
| 1130 | js_arraybuffer_span_of_t<rocksdb_native_read_batch_t, 1> req, | |
| 1131 | uint32_t capacity | |
| 1132 | ) { | |
| 1133 | int err; | |
| 1134 | ||
| 1135 | js_arraybuffer_t handle; | |
| 1136 | ||
| 1137 | rocksdb_read_t *reads; | |
| 1138 | err = js_create_arraybuffer(env, capacity, reads, handle); | |
| 1139 | assert(err == 0); | |
| 1140 | ||
| 1141 | req->capacity = capacity; | |
| 1142 | req->reads = reads; | |
| 1143 | ||
| 1144 | return handle; | |
| 1145 | } | |
| 1146 | ||
| 1147 | static void | |
| 1148 | rocksdb_native__on_read(rocksdb_read_batch_t *handle, int status) { | |
| 1149 | int err; | |
| 1150 | ||
| 1151 | assert(status == 0); | |
| 1152 | ||
| 1153 | auto req = reinterpret_cast<rocksdb_native_read_batch_t *>(handle->data); | |
| 1154 | ||
| 1155 | auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db); | |
| 1156 | ||
| 1157 | auto len = req->handle.len; | |
| 1158 | ||
| 1159 | auto env = req->env; | |
| 1160 | ||
| 1161 | js_handle_scope_t *scope; | |
| 1162 | err = js_open_handle_scope(env, &scope); | |
| 1163 | assert(err == 0); | |
| 1164 | ||
| 1165 | js_receiver_t ctx; | |
| 1166 | err = js_get_reference_value(env, req->ctx, ctx); | |
| 1167 | assert(err == 0); | |
| 1168 | ||
| 1169 | rocksdb_native_on_read_t cb; | |
| 1170 | err = js_get_reference_value(env, req->on_read, cb); | |
| 1171 | assert(err == 0); | |
| 1172 | ||
| 1173 | req->on_read.reset(); | |
| 1174 | req->ctx.reset(); | |
| 1175 | ||
| 1176 | js_array_t errors; | |
| 1177 | err = js_create_array(env, len, errors); | |
| 1178 | assert(err == 0); | |
| 1179 | ||
| 1180 | js_array_t values; | |
| 1181 | err = js_create_array(env, len, values); | |
| 1182 | assert(err == 0); | |
| 1183 | ||
| 1184 | for (size_t i = 0; i < len; i++) { | |
| 1185 | char *error = req->handle.errors[i]; | |
| 1186 | int status = req->handle.statuses[i]; | |
| 1187 | ||
| 1188 | if (error) { | |
| 1189 | js_object_t result; | |
| 1190 | ||
| 1191 | err = js_create_error(env, uv_err_name(status), error, result); | |
| 1192 | assert(err == 0); | |
| 1193 | ||
| 1194 | err = js_set_element(env, errors, i, result); | |
| 1195 | assert(err == 0); | |
| 1196 | } else { | |
| 1197 | rocksdb_slice_t *value = &req->reads[i].value; | |
| 1198 | ||
| 1199 | if (db->exiting) rocksdb_slice_destroy(value); | |
| 1200 | else { | |
| 1201 | js_arraybuffer_t result; | |
| 1202 | ||
| 1203 | if (value->data == nullptr && value->len == size_t(-1)) { | |
| 1204 | err = js_get_null(env, static_cast<js_value_t **>(result)); | |
| 1205 | assert(err == 0); | |
| 1206 | } else { | |
| 1207 | err = rocksdb_native__try_create_external_arraybuffer(env, const_cast<char *>(value->data), value->len, result); | |
| 1208 | assert(err == 0); | |
| 1209 | } | |
| 1210 | ||
| 1211 | err = js_set_element(env, values, i, result); | |
| 1212 | assert(err == 0); | |
| 1213 | } | |
| 1214 | } | |
| 1215 | } | |
| 1216 | ||
| 1217 | if (!db->exiting) { | |
| 1218 | err = js_call_function_with_checkpoint(env, cb, ctx, errors, values); | |
| 1219 | (void) err; | |
| 1220 | } | |
| 1221 | ||
| 1222 | err = js_close_handle_scope(env, scope); | |
| 1223 | assert(err == 0); | |
| 1224 | } | |
| 1225 | ||
| 1226 | static void | |
| 1227 | rocksdb_native_read( | |
| 1228 | js_env_t *env, | |
| 1229 | js_arraybuffer_span_of_t<rocksdb_native_t, 1> db, | |
| 1230 | js_arraybuffer_span_of_t<rocksdb_native_read_batch_t, 1> req, | |
| 1231 | js_array_t operations, | |
| 1232 | std::optional<js_arraybuffer_span_of_t<rocksdb_native_snapshot_t, 1>> snapshot, | |
| 1233 | bool async_io, | |
| 1234 | bool fill_cache, | |
| 1235 | js_receiver_t ctx, | |
| 1236 | rocksdb_native_on_read_t on_read | |
| 1237 | ) { | |
| 1238 | int err; | |
| 1239 | ||
| 1240 | std::vector<js_object_t> elements; | |
| 1241 | err = js_get_array_elements(env, operations, elements); | |
| 1242 | assert(err == 0); | |
| 1243 | ||
| 1244 | const auto len = elements.size(); | |
| 1245 | ||
| 1246 | for (uint32_t i = 0; i < len; i++) { | |
| 1247 | js_object_t read = elements[i]; | |
| 1248 | ||
| 1249 | rocksdb_read_type_t type; | |
| 1250 | err = js_get_property(env, read, "type", reinterpret_cast<uint32_t &>(type)); | |
| 1251 | assert(err == 0); | |
| 1252 | ||
| 1253 | req->reads[i].type = type; | |
| 1254 | ||
| 1255 | js_arraybuffer_t column_family_property; | |
| 1256 | err = js_get_property(env, read, "columnFamily", column_family_property); | |
| 1257 | assert(err == 0); | |
| 1258 | ||
| 1259 | rocksdb_native_column_family_t *column_family; | |
| 1260 | err = js_get_arraybuffer_info(env, column_family_property, column_family); | |
| 1261 | assert(err == 0); | |
| 1262 | ||
| 1263 | req->reads[i].column_family = column_family->handle; | |
| 1264 | ||
| 1265 | switch (type) { | |
| 1266 | case rocksdb_get: { | |
| 1267 | js_typedarray_t property; | |
| 1268 | ||
| 1269 | rocksdb_slice_t *key = &req->reads[i].key; | |
| 1270 | ||
| 1271 | err = js_get_property(env, read, "key", property); | |
| 1272 | assert(err == 0); | |
| 1273 | ||
| 1274 | err = js_get_typedarray_info(env, property, key->data, key->len); | |
| 1275 | assert(err == 0); | |
| 1276 | break; | |
| 1277 | } | |
| 1278 | } | |
| 1279 | } | |
| 1280 | ||
| 1281 | rocksdb_read_options_t options = { | |
| 1282 | .version = 1, | |
| 1283 | .async_io = async_io, | |
| 1284 | .fill_cache = fill_cache | |
| 1285 | }; | |
| 1286 | ||
| 1287 | if (snapshot) options.snapshot = &snapshot.value()->handle; | |
| 1288 | ||
| 1289 | err = rocksdb_read(&db->handle, &req->handle, req->reads, len, &options, rocksdb_native__on_read); | |
| 1290 | ||
| 1291 | if (err < 0) { | |
| 1292 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1293 | assert(err == 0); | |
| 1294 | ||
| 1295 | throw js_pending_exception; | |
| 1296 | } | |
| 1297 | ||
| 1298 | err = js_create_reference(env, ctx, req->ctx); | |
| 1299 | assert(err == 0); | |
| 1300 | ||
| 1301 | err = js_create_reference(env, on_read, req->on_read); | |
| 1302 | assert(err == 0); | |
| 1303 | } | |
| 1304 | ||
| 1305 | static js_arraybuffer_t | |
| 1306 | rocksdb_native_write_init(js_env_t *env) { | |
| 1307 | int err; | |
| 1308 | ||
| 1309 | js_arraybuffer_t handle; | |
| 1310 | ||
| 1311 | rocksdb_native_write_batch_t *req; | |
| 1312 | err = js_create_arraybuffer(env, req, handle); | |
| 1313 | assert(err == 0); | |
| 1314 | ||
| 1315 | req->env = env; | |
| 1316 | req->handle.data = req; | |
| 1317 | ||
| 1318 | return handle; | |
| 1319 | } | |
| 1320 | ||
| 1321 | static js_arraybuffer_t | |
| 1322 | rocksdb_native_write_buffer( | |
| 1323 | js_env_t *env, | |
| 1324 | js_arraybuffer_span_of_t<rocksdb_native_write_batch_t, 1> req, | |
| 1325 | uint32_t capacity | |
| 1326 | ) { | |
| 1327 | int err; | |
| 1328 | ||
| 1329 | js_arraybuffer_t handle; | |
| 1330 | ||
| 1331 | rocksdb_write_t *writes; | |
| 1332 | err = js_create_arraybuffer(env, capacity, writes, handle); | |
| 1333 | assert(err == 0); | |
| 1334 | ||
| 1335 | req->capacity = capacity; | |
| 1336 | req->writes = writes; | |
| 1337 | ||
| 1338 | return handle; | |
| 1339 | } | |
| 1340 | ||
| 1341 | static void | |
| 1342 | rocksdb_native__on_write(rocksdb_write_batch_t *handle, int status) { | |
| 1343 | int err; | |
| 1344 | ||
| 1345 | assert(status == 0); | |
| 1346 | ||
| 1347 | auto req = reinterpret_cast<rocksdb_native_write_batch_t *>(handle->data); | |
| 1348 | ||
| 1349 | auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db); | |
| 1350 | ||
| 1351 | auto env = req->env; | |
| 1352 | ||
| 1353 | js_handle_scope_t *scope; | |
| 1354 | err = js_open_handle_scope(env, &scope); | |
| 1355 | assert(err == 0); | |
| 1356 | ||
| 1357 | js_receiver_t ctx; | |
| 1358 | err = js_get_reference_value(env, req->ctx, ctx); | |
| 1359 | assert(err == 0); | |
| 1360 | ||
| 1361 | rocksdb_native_on_write_t cb; | |
| 1362 | err = js_get_reference_value(env, req->on_write, cb); | |
| 1363 | assert(err == 0); | |
| 1364 | ||
| 1365 | req->on_write.reset(); | |
| 1366 | req->ctx.reset(); | |
| 1367 | ||
| 1368 | std::optional<js_object_t> error; | |
| 1369 | ||
| 1370 | if (req->handle.error) { | |
| 1371 | err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace()); | |
| 1372 | assert(err == 0); | |
| 1373 | } | |
| 1374 | ||
| 1375 | if (!db->exiting) { | |
| 1376 | err = js_call_function_with_checkpoint(env, cb, ctx, error); | |
| 1377 | (void) err; | |
| 1378 | } | |
| 1379 | ||
| 1380 | err = js_close_handle_scope(env, scope); | |
| 1381 | assert(err == 0); | |
| 1382 | } | |
| 1383 | ||
| 1384 | static void | |
| 1385 | rocksdb_native_write( | |
| 1386 | js_env_t *env, | |
| 1387 | js_arraybuffer_span_of_t<rocksdb_native_t, 1> db, | |
| 1388 | js_arraybuffer_span_of_t<rocksdb_native_write_batch_t, 1> req, | |
| 1389 | js_array_t operations, | |
| 1390 | js_receiver_t ctx, | |
| 1391 | rocksdb_native_on_write_t on_write | |
| 1392 | ) { | |
| 1393 | int err; | |
| 1394 | ||
| 1395 | std::vector<js_object_t> elements; | |
| 1396 | err = js_get_array_elements(env, operations, elements); | |
| 1397 | assert(err == 0); | |
| 1398 | ||
| 1399 | const auto len = elements.size(); | |
| 1400 | ||
| 1401 | for (uint32_t i = 0; i < len; i++) { | |
| 1402 | js_object_t write = elements[i]; | |
| 1403 | ||
| 1404 | rocksdb_write_type_t type; | |
| 1405 | err = js_get_property(env, write, "type", reinterpret_cast<uint32_t &>(type)); | |
| 1406 | assert(err == 0); | |
| 1407 | ||
| 1408 | req->writes[i].type = type; | |
| 1409 | ||
| 1410 | js_arraybuffer_t column_family_property; | |
| 1411 | err = js_get_property(env, write, "columnFamily", column_family_property); | |
| 1412 | assert(err == 0); | |
| 1413 | ||
| 1414 | rocksdb_native_column_family_t *column_family; | |
| 1415 | err = js_get_arraybuffer_info(env, column_family_property, column_family); | |
| 1416 | assert(err == 0); | |
| 1417 | ||
| 1418 | req->writes[i].column_family = column_family->handle; | |
| 1419 | ||
| 1420 | js_typedarray_t property; | |
| 1421 | ||
| 1422 | switch (type) { | |
| 1423 | case rocksdb_put: { | |
| 1424 | rocksdb_slice_t *key = &req->writes[i].key; | |
| 1425 | ||
| 1426 | err = js_get_property(env, write, "key", property); | |
| 1427 | assert(err == 0); | |
| 1428 | ||
| 1429 | err = js_get_typedarray_info(env, property, key->data, key->len); | |
| 1430 | assert(err == 0); | |
| 1431 | ||
| 1432 | rocksdb_slice_t *value = &req->writes[i].value; | |
| 1433 | ||
| 1434 | err = js_get_property(env, write, "value", property); | |
| 1435 | assert(err == 0); | |
| 1436 | ||
| 1437 | err = js_get_typedarray_info(env, property, value->data, value->len); | |
| 1438 | assert(err == 0); | |
| 1439 | break; | |
| 1440 | } | |
| 1441 | ||
| 1442 | case rocksdb_delete: { | |
| 1443 | rocksdb_slice_t *key = &req->writes[i].key; | |
| 1444 | ||
| 1445 | err = js_get_property(env, write, "key", property); | |
| 1446 | assert(err == 0); | |
| 1447 | ||
| 1448 | err = js_get_typedarray_info(env, property, key->data, key->len); | |
| 1449 | assert(err == 0); | |
| 1450 | break; | |
| 1451 | } | |
| 1452 | ||
| 1453 | case rocksdb_delete_range: { | |
| 1454 | rocksdb_slice_t *start = &req->writes[i].start; | |
| 1455 | ||
| 1456 | err = js_get_property(env, write, "start", property); | |
| 1457 | assert(err == 0); | |
| 1458 | ||
| 1459 | err = js_get_typedarray_info(env, property, start->data, start->len); | |
| 1460 | assert(err == 0); | |
| 1461 | ||
| 1462 | rocksdb_slice_t *end = &req->writes[i].end; | |
| 1463 | ||
| 1464 | err = js_get_property(env, write, "end", property); | |
| 1465 | assert(err == 0); | |
| 1466 | ||
| 1467 | err = js_get_typedarray_info(env, property, end->data, end->len); | |
| 1468 | assert(err == 0); | |
| 1469 | break; | |
| 1470 | } | |
| 1471 | } | |
| 1472 | } | |
| 1473 | ||
| 1474 | err = rocksdb_write(&db->handle, &req->handle, req->writes, len, nullptr, rocksdb_native__on_write); | |
| 1475 | ||
| 1476 | if (err < 0) { | |
| 1477 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1478 | assert(err == 0); | |
| 1479 | ||
| 1480 | throw js_pending_exception; | |
| 1481 | } | |
| 1482 | ||
| 1483 | err = js_create_reference(env, ctx, req->ctx); | |
| 1484 | assert(err == 0); | |
| 1485 | ||
| 1486 | err = js_create_reference(env, on_write, req->on_write); | |
| 1487 | assert(err == 0); | |
| 1488 | } | |
| 1489 | ||
| 1490 | static void | |
| 1491 | rocksdb_native__on_flush(rocksdb_flush_t *handle, int status) { | |
| 1492 | int err; | |
| 1493 | ||
| 1494 | assert(status == 0); | |
| 1495 | ||
| 1496 | auto req = reinterpret_cast<rocksdb_native_flush_t *>(handle->data); | |
| 1497 | ||
| 1498 | auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db); | |
| 1499 | ||
| 1500 | auto env = req->env; | |
| 1501 | ||
| 1502 | js_handle_scope_t *scope; | |
| 1503 | err = js_open_handle_scope(env, &scope); | |
| 1504 | assert(err == 0); | |
| 1505 | ||
| 1506 | js_receiver_t ctx; | |
| 1507 | err = js_get_reference_value(env, req->ctx, ctx); | |
| 1508 | assert(err == 0); | |
| 1509 | ||
| 1510 | rocksdb_native_on_flush_t cb; | |
| 1511 | err = js_get_reference_value(env, req->on_flush, cb); | |
| 1512 | assert(err == 0); | |
| 1513 | ||
| 1514 | req->on_flush.reset(); | |
| 1515 | req->ctx.reset(); | |
| 1516 | ||
| 1517 | std::optional<js_object_t> error; | |
| 1518 | ||
| 1519 | if (req->handle.error) { | |
| 1520 | err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace()); | |
| 1521 | assert(err == 0); | |
| 1522 | } | |
| 1523 | ||
| 1524 | if (!db->exiting) { | |
| 1525 | err = js_call_function_with_checkpoint(env, cb, ctx, error); | |
| 1526 | (void) err; | |
| 1527 | } | |
| 1528 | ||
| 1529 | err = js_close_handle_scope(env, scope); | |
| 1530 | assert(err == 0); | |
| 1531 | } | |
| 1532 | ||
| 1533 | static js_arraybuffer_t | |
| 1534 | rocksdb_native_flush( | |
| 1535 | js_env_t *env, | |
| 1536 | js_arraybuffer_span_of_t<rocksdb_native_t, 1> db, | |
| 1537 | js_arraybuffer_span_of_t<rocksdb_native_column_family_t, 1> column_family, | |
| 1538 | js_receiver_t ctx, | |
| 1539 | rocksdb_native_on_flush_t on_flush | |
| 1540 | ) { | |
| 1541 | int err; | |
| 1542 | ||
| 1543 | js_arraybuffer_t handle; | |
| 1544 | ||
| 1545 | rocksdb_native_flush_t *req; | |
| 1546 | err = js_create_arraybuffer(env, req, handle); | |
| 1547 | assert(err == 0); | |
| 1548 | ||
| 1549 | req->env = env; | |
| 1550 | req->handle.data = req; | |
| 1551 | ||
| 1552 | err = rocksdb_flush(&db->handle, &req->handle, column_family->handle, nullptr, rocksdb_native__on_flush); | |
| 1553 | ||
| 1554 | if (err < 0) { | |
| 1555 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1556 | assert(err == 0); | |
| 1557 | ||
| 1558 | throw js_pending_exception; | |
| 1559 | } | |
| 1560 | ||
| 1561 | err = js_create_reference(env, ctx, req->ctx); | |
| 1562 | assert(err == 0); | |
| 1563 | ||
| 1564 | err = js_create_reference(env, on_flush, req->on_flush); | |
| 1565 | assert(err == 0); | |
| 1566 | ||
| 1567 | return handle; | |
| 1568 | } | |
| 1569 | ||
| 1570 | static void | |
| 1571 | rocksdb_native__on_compact_range(rocksdb_compact_range_t *handle, int status) { | |
| 1572 | int err; | |
| 1573 | ||
| 1574 | assert(status == 0); | |
| 1575 | ||
| 1576 | auto req = reinterpret_cast<rocksdb_native_compact_range_t *>(handle->data); | |
| 1577 | ||
| 1578 | auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db); | |
| 1579 | ||
| 1580 | auto env = req->env; | |
| 1581 | ||
| 1582 | js_handle_scope_t *scope; | |
| 1583 | err = js_open_handle_scope(env, &scope); | |
| 1584 | assert(err == 0); | |
| 1585 | ||
| 1586 | js_receiver_t ctx; | |
| 1587 | err = js_get_reference_value(env, req->ctx, ctx); | |
| 1588 | assert(err == 0); | |
| 1589 | ||
| 1590 | rocksdb_native_on_compact_range_t cb; | |
| 1591 | err = js_get_reference_value(env, req->on_compact_range, cb); | |
| 1592 | assert(err == 0); | |
| 1593 | ||
| 1594 | req->on_compact_range.reset(); | |
| 1595 | req->ctx.reset(); | |
| 1596 | ||
| 1597 | std::optional<js_object_t> error; | |
| 1598 | ||
| 1599 | if (req->handle.error) { | |
| 1600 | err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace()); | |
| 1601 | assert(err == 0); | |
| 1602 | } | |
| 1603 | ||
| 1604 | if (!db->exiting) { | |
| 1605 | err = js_call_function_with_checkpoint(env, cb, ctx, error); | |
| 1606 | (void) err; | |
| 1607 | } | |
| 1608 | ||
| 1609 | err = js_close_handle_scope(env, scope); | |
| 1610 | assert(err == 0); | |
| 1611 | } | |
| 1612 | ||
| 1613 | static js_arraybuffer_t | |
| 1614 | rocksdb_native_compact_range( | |
| 1615 | js_env_t *env, | |
| 1616 | js_arraybuffer_span_of_t<rocksdb_native_t, 1> db, | |
| 1617 | js_arraybuffer_span_of_t<rocksdb_native_column_family_t, 1> column_family, | |
| 1618 | js_typedarray_t<> start, | |
| 1619 | js_typedarray_t<> end, | |
| 1620 | bool exclusive, | |
| 1621 | uint32_t blob_garbage_collection_policy, | |
| 1622 | double blob_garbage_collection_age_cutoff, | |
| 1623 | uint32_t bottommost_level_compaction, | |
| 1624 | js_receiver_t ctx, | |
| 1625 | rocksdb_native_on_compact_range_t on_compact_range | |
| 1626 | ) { | |
| 1627 | int err; | |
| 1628 | ||
| 1629 | rocksdb_slice_t start_slice; | |
| 1630 | err = js_get_typedarray_info(env, start, start_slice.data, start_slice.len); | |
| 1631 | assert(err == 0); | |
| 1632 | ||
| 1633 | rocksdb_slice_t end_slice; | |
| 1634 | err = js_get_typedarray_info(env, end, end_slice.data, end_slice.len); | |
| 1635 | assert(err == 0); | |
| 1636 | ||
| 1637 | js_arraybuffer_t handle; | |
| 1638 | ||
| 1639 | rocksdb_native_compact_range_t *req; | |
| 1640 | err = js_create_arraybuffer(env, req, handle); | |
| 1641 | assert(err == 0); | |
| 1642 | ||
| 1643 | req->env = env; | |
| 1644 | req->handle.data = req; | |
| 1645 | ||
| 1646 | rocksdb_compact_range_options_t options = { | |
| 1647 | .version = 1, | |
| 1648 | .exclusive_manual_compaction = exclusive, | |
| 1649 | .blob_garbage_collection_policy = rocksdb_blob_garbage_collection_policy_t(blob_garbage_collection_policy), | |
| 1650 | .blob_garbage_collection_age_cutoff = blob_garbage_collection_age_cutoff, | |
| 1651 | .bottommost_level_compaction = rocksdb_bottommost_level_compaction_t(bottommost_level_compaction), | |
| 1652 | }; | |
| 1653 | ||
| 1654 | err = rocksdb_compact_range(&db->handle, &req->handle, column_family->handle, start_slice, end_slice, &options, rocksdb_native__on_compact_range); | |
| 1655 | ||
| 1656 | if (err < 0) { | |
| 1657 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1658 | assert(err == 0); | |
| 1659 | ||
| 1660 | throw js_pending_exception; | |
| 1661 | } | |
| 1662 | ||
| 1663 | err = js_create_reference(env, ctx, req->ctx); | |
| 1664 | assert(err == 0); | |
| 1665 | ||
| 1666 | err = js_create_reference(env, on_compact_range, req->on_compact_range); | |
| 1667 | assert(err == 0); | |
| 1668 | ||
| 1669 | return handle; | |
| 1670 | } | |
| 1671 | ||
| 1672 | static void | |
| 1673 | rocksdb_native__on_approximate_size(rocksdb_approximate_size_t *handle, int status) { | |
| 1674 | int err; | |
| 1675 | ||
| 1676 | assert(status == 0); | |
| 1677 | ||
| 1678 | auto req = reinterpret_cast<rocksdb_native_approximate_size_t *>(handle->data); | |
| 1679 | ||
| 1680 | auto db = reinterpret_cast<rocksdb_native_t *>(req->handle.req.db); | |
| 1681 | ||
| 1682 | auto env = req->env; | |
| 1683 | ||
| 1684 | js_handle_scope_t *scope; | |
| 1685 | err = js_open_handle_scope(env, &scope); | |
| 1686 | assert(err == 0); | |
| 1687 | ||
| 1688 | js_receiver_t ctx; | |
| 1689 | err = js_get_reference_value(env, req->ctx, ctx); | |
| 1690 | assert(err == 0); | |
| 1691 | ||
| 1692 | rocksdb_native_on_approximate_size_t cb; | |
| 1693 | err = js_get_reference_value(env, req->on_approximate_size, cb); | |
| 1694 | assert(err == 0); | |
| 1695 | ||
| 1696 | req->on_approximate_size.reset(); | |
| 1697 | req->ctx.reset(); | |
| 1698 | ||
| 1699 | std::optional<js_object_t> error; | |
| 1700 | ||
| 1701 | if (req->handle.error) { | |
| 1702 | err = js_create_error(env, uv_err_name(req->handle.status), req->handle.error, error.emplace()); | |
| 1703 | assert(err == 0); | |
| 1704 | } | |
| 1705 | ||
| 1706 | if (!db->exiting) { | |
| 1707 | err = js_call_function_with_checkpoint(env, cb, ctx, error, req->handle.result); | |
| 1708 | (void) err; | |
| 1709 | } | |
| 1710 | ||
| 1711 | err = js_close_handle_scope(env, scope); | |
| 1712 | assert(err == 0); | |
| 1713 | } | |
| 1714 | ||
| 1715 | static js_arraybuffer_t | |
| 1716 | rocksdb_native_approximate_size( | |
| 1717 | js_env_t *env, | |
| 1718 | js_arraybuffer_span_of_t<rocksdb_native_t, 1> db, | |
| 1719 | js_arraybuffer_span_of_t<rocksdb_native_column_family_t, 1> column_family, | |
| 1720 | js_typedarray_t<> start, | |
| 1721 | js_typedarray_t<> end, | |
| 1722 | bool include_memtables, | |
| 1723 | bool include_files, | |
| 1724 | double error_margin, | |
| 1725 | js_receiver_t ctx, | |
| 1726 | rocksdb_native_on_approximate_size_t on_approximate_size | |
| 1727 | ) { | |
| 1728 | int err; | |
| 1729 | ||
| 1730 | rocksdb_slice_t start_slice; | |
| 1731 | err = js_get_typedarray_info(env, start, start_slice.data, start_slice.len); | |
| 1732 | assert(err == 0); | |
| 1733 | ||
| 1734 | rocksdb_slice_t end_slice; | |
| 1735 | err = js_get_typedarray_info(env, end, end_slice.data, end_slice.len); | |
| 1736 | assert(err == 0); | |
| 1737 | ||
| 1738 | js_arraybuffer_t handle; | |
| 1739 | ||
| 1740 | rocksdb_native_approximate_size_t *req; | |
| 1741 | err = js_create_arraybuffer(env, req, handle); | |
| 1742 | assert(err == 0); | |
| 1743 | ||
| 1744 | req->env = env; | |
| 1745 | req->handle.data = req; | |
| 1746 | ||
| 1747 | rocksdb_approximate_size_options_t options = { | |
| 1748 | .version = 0, | |
| 1749 | .include_memtables = include_memtables, | |
| 1750 | .include_files = include_files, | |
| 1751 | .files_size_error_margin = error_margin, | |
| 1752 | }; | |
| 1753 | ||
| 1754 | err = rocksdb_approximate_size(&db->handle, &req->handle, column_family->handle, start_slice, end_slice, &options, rocksdb_native__on_approximate_size); | |
| 1755 | ||
| 1756 | if (err < 0) { | |
| 1757 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1758 | assert(err == 0); | |
| 1759 | ||
| 1760 | throw js_pending_exception; | |
| 1761 | } | |
| 1762 | ||
| 1763 | err = js_create_reference(env, ctx, req->ctx); | |
| 1764 | assert(err == 0); | |
| 1765 | ||
| 1766 | err = js_create_reference(env, on_approximate_size, req->on_approximate_size); | |
| 1767 | assert(err == 0); | |
| 1768 | ||
| 1769 | return handle; | |
| 1770 | } | |
| 1771 | ||
| 1772 | static js_arraybuffer_t | |
| 1773 | rocksdb_native_snapshot_create(js_env_t *env, js_arraybuffer_span_of_t<rocksdb_native_t, 1> db) { | |
| 1774 | int err; | |
| 1775 | ||
| 1776 | js_arraybuffer_t handle; | |
| 1777 | ||
| 1778 | rocksdb_native_snapshot_t *snapshot; | |
| 1779 | err = js_create_arraybuffer(env, snapshot, handle); | |
| 1780 | assert(err == 0); | |
| 1781 | ||
| 1782 | snapshot->db = db; | |
| 1783 | ||
| 1784 | err = rocksdb_snapshot_create(&db->handle, &snapshot->handle); | |
| 1785 | ||
| 1786 | if (err < 0) { | |
| 1787 | err = js_throw_error(env, uv_err_name(err), uv_strerror(err)); | |
| 1788 | assert(err == 0); | |
| 1789 | ||
| 1790 | throw js_pending_exception; | |
| 1791 | } | |
| 1792 | ||
| 1793 | err = js_create_reference(env, handle, snapshot->ctx); | |
| 1794 | assert(err == 0); | |
| 1795 | ||
| 1796 | db->snapshots.insert(snapshot); | |
| 1797 | ||
| 1798 | return handle; | |
| 1799 | } | |
| 1800 | ||
| 1801 | static void | |
| 1802 | rocksdb_native_snapshot_destroy(js_env_t *env, js_arraybuffer_span_of_t<rocksdb_native_snapshot_t, 1> snapshot) { | |
| 1803 | int err; | |
| 1804 | ||
| 1805 | rocksdb_snapshot_destroy(&snapshot->handle); | |
| 1806 | ||
| 1807 | snapshot->db->snapshots.erase(snapshot); | |
| 1808 | ||
| 1809 | snapshot->ctx.reset(); | |
| 1810 | } | |
| 1811 | ||
| 1812 | static js_value_t * | |
| 1813 | rocksdb_native_exports(js_env_t *env, js_value_t *exports) { | |
| 1814 | int err; | |
| 1815 | ||
| 1816 | #define V(name, fn) \ | |
| 1817 | err = js_set_property<fn>(env, exports, name); \ | |
| 1818 | assert(err == 0); | |
| 1819 | ||
| 1820 | V("init", rocksdb_native_init) | |
| 1821 | V("open", rocksdb_native_open) | |
| 1822 | V("close", rocksdb_native_close) | |
| 1823 | V("suspend", rocksdb_native_suspend) | |
| 1824 | V("resume", rocksdb_native_resume) | |
| 1825 | ||
| 1826 | V("columnFamilyInit", rocksdb_native_column_family_init) | |
| 1827 | V("columnFamilyDestroy", rocksdb_native_column_family_destroy) | |
| 1828 | ||
| 1829 | V("readInit", rocksdb_native_read_init) | |
| 1830 | V("readBuffer", rocksdb_native_read_buffer) | |
| 1831 | V("read", rocksdb_native_read) | |
| 1832 | ||
| 1833 | V("writeInit", rocksdb_native_write_init) | |
| 1834 | V("writeBuffer", rocksdb_native_write_buffer) | |
| 1835 | V("write", rocksdb_native_write) | |
| 1836 | ||
| 1837 | V("iteratorInit", rocksdb_native_iterator_init) | |
| 1838 | V("iteratorBuffer", rocksdb_native_iterator_buffer) | |
| 1839 | V("iteratorOpen", rocksdb_native_iterator_open) | |
| 1840 | V("iteratorClose", rocksdb_native_iterator_close) | |
| 1841 | V("iteratorRead", rocksdb_native_iterator_read) | |
| 1842 | ||
| 1843 | V("flush", rocksdb_native_flush) | |
| 1844 | V("compactRange", rocksdb_native_compact_range) | |
| 1845 | V("approximateSize", rocksdb_native_approximate_size) | |
| 1846 | ||
| 1847 | V("snapshotCreate", rocksdb_native_snapshot_create) | |
| 1848 | V("snapshotDestroy", rocksdb_native_snapshot_destroy) | |
| 1849 | #undef V | |
| 1850 | ||
| 1851 | #define V(name, n) \ | |
| 1852 | { \ | |
| 1853 | js_value_t *val; \ | |
| 1854 | err = js_create_uint32(env, n, &val); \ | |
| 1855 | assert(err == 0); \ | |
| 1856 | err = js_set_named_property(env, exports, name, val); \ | |
| 1857 | assert(err == 0); \ | |
| 1858 | } | |
| 1859 | ||
| 1860 | V("GET", rocksdb_get) | |
| 1861 | V("PUT", rocksdb_put) | |
| 1862 | V("DELETE", rocksdb_delete) | |
| 1863 | V("DELETE_RANGE", rocksdb_delete_range) | |
| 1864 | #undef V | |
| 1865 | ||
| 1866 | return exports; | |
| 1867 | } | |
| 1868 | ||
| 1869 | BARE_MODULE(rocksdb_native, rocksdb_native_exports) |
| … | … | @@ -0,0 +1,3 @@ |
| 1 | require.addon = require('require-addon') | |
| 2 | ||
| 3 | module.exports = require.addon('.', __filename) |
| … | … | @@ -0,0 +1,223 @@ |
| 1 | const ColumnFamily = require('./lib/column-family') | |
| 2 | const Iterator = require('./lib/iterator') | |
| 3 | const Snapshot = require('./lib/snapshot') | |
| 4 | const State = require('./lib/state') | |
| 5 | const { BloomFilterPolicy, RibbonFilterPolicy } = require('./lib/filter-policy') | |
| 6 | const constants = require('./lib/constants') | |
| 7 | ||
| 8 | class RocksDB { | |
| 9 | constructor(path, opts = {}) { | |
| 10 | const { | |
| 11 | columnFamily, | |
| 12 | state = new State(this, path, opts), | |
| 13 | snapshot = null, | |
| 14 | keyEncoding = null, | |
| 15 | valueEncoding = null | |
| 16 | } = opts | |
| 17 | ||
| 18 | this._state = state | |
| 19 | this._snapshot = snapshot | |
| 20 | this._columnFamily = state.getColumnFamily(columnFamily) | |
| 21 | this._keyEncoding = keyEncoding | |
| 22 | this._valueEncoding = valueEncoding | |
| 23 | this._index = -1 | |
| 24 | ||
| 25 | this._state.addSession(this) | |
| 26 | } | |
| 27 | ||
| 28 | get opened() { | |
| 29 | return this._state.opened | |
| 30 | } | |
| 31 | ||
| 32 | get closed() { | |
| 33 | return this.isRoot() ? this._state.closed : this._index === -1 | |
| 34 | } | |
| 35 | ||
| 36 | get path() { | |
| 37 | return this._state.path | |
| 38 | } | |
| 39 | ||
| 40 | get stats() { | |
| 41 | return this._state.stats | |
| 42 | } | |
| 43 | ||
| 44 | get snapshotted() { | |
| 45 | return this._snapshot !== null | |
| 46 | } | |
| 47 | ||
| 48 | get defaultColumnFamily() { | |
| 49 | return this._columnFamily | |
| 50 | } | |
| 51 | ||
| 52 | session({ | |
| 53 | columnFamily = this._columnFamily, | |
| 54 | snapshot = this._snapshot !== null, | |
| 55 | keyEncoding = this._keyEncoding, | |
| 56 | valueEncoding = this._valueEncoding | |
| 57 | } = {}) { | |
| 58 | maybeClosed(this) | |
| 59 | ||
| 60 | return new RocksDB(null, { | |
| 61 | state: this._state, | |
| 62 | columnFamily, | |
| 63 | snapshot: snapshot ? this._snapshot || new Snapshot(this._state) : null, | |
| 64 | keyEncoding, | |
| 65 | valueEncoding | |
| 66 | }) | |
| 67 | } | |
| 68 | ||
| 69 | columnFamily(name, opts) { | |
| 70 | return this.session({ ...opts, columnFamily: name }) | |
| 71 | } | |
| 72 | ||
| 73 | snapshot() { | |
| 74 | return this.session({ snapshot: true }) | |
| 75 | } | |
| 76 | ||
| 77 | isRoot() { | |
| 78 | return this === this._state.db | |
| 79 | } | |
| 80 | ||
| 81 | ready() { | |
| 82 | return this._state.ready() | |
| 83 | } | |
| 84 | ||
| 85 | async close({ force } = {}) { | |
| 86 | if (!this._state.opened) await this._state.ready() | |
| 87 | ||
| 88 | if (this._index !== -1) this._state.removeSession(this) | |
| 89 | ||
| 90 | if (force) { | |
| 91 | while (this._state.sessions.length > 0) { | |
| 92 | await this._state.sessions[this._state.sessions.length - 1].close() | |
| 93 | } | |
| 94 | } | |
| 95 | ||
| 96 | return this.isRoot() ? this._state.close() : Promise.resolve() | |
| 97 | } | |
| 98 | ||
| 99 | suspend() { | |
| 100 | maybeClosed(this) | |
| 101 | ||
| 102 | return this._state.suspend() | |
| 103 | } | |
| 104 | ||
| 105 | resume() { | |
| 106 | maybeClosed(this) | |
| 107 | ||
| 108 | return this._state.resume() | |
| 109 | } | |
| 110 | ||
| 111 | isIdle() { | |
| 112 | return this._state.handles.isIdle() | |
| 113 | } | |
| 114 | ||
| 115 | idle() { | |
| 116 | return this._state.handles.idle() | |
| 117 | } | |
| 118 | ||
| 119 | iterator(range, opts) { | |
| 120 | maybeClosed(this) | |
| 121 | ||
| 122 | return new Iterator(this, { ...range, ...opts }) | |
| 123 | } | |
| 124 | ||
| 125 | async *keys(range, opts) { | |
| 126 | for await (const { key } of this.iterator(range, { | |
| 127 | ...opts, | |
| 128 | values: false | |
| 129 | })) { | |
| 130 | yield key | |
| 131 | } | |
| 132 | } | |
| 133 | ||
| 134 | async peek(range, opts) { | |
| 135 | for await (const value of this.iterator({ ...range, ...opts, limit: 1 })) { | |
| 136 | return value | |
| 137 | } | |
| 138 | ||
| 139 | return null | |
| 140 | } | |
| 141 | ||
| 142 | read(opts) { | |
| 143 | maybeClosed(this) | |
| 144 | ||
| 145 | return this._state.createReadBatch(this, opts) | |
| 146 | } | |
| 147 | ||
| 148 | write(opts) { | |
| 149 | maybeClosed(this) | |
| 150 | ||
| 151 | return this._state.createWriteBatch(this, opts) | |
| 152 | } | |
| 153 | ||
| 154 | flush(opts) { | |
| 155 | maybeClosed(this) | |
| 156 | ||
| 157 | return this._state.flush(this, opts) | |
| 158 | } | |
| 159 | ||
| 160 | async get(key, opts) { | |
| 161 | const batch = this.read({ ...opts, capacity: 1, autoDestroy: true }) | |
| 162 | const value = batch.get(key) | |
| 163 | batch.tryFlush() | |
| 164 | return value | |
| 165 | } | |
| 166 | ||
| 167 | async put(key, value, opts) { | |
| 168 | const batch = this.write({ ...opts, capacity: 1, autoDestroy: true }) | |
| 169 | batch.tryPut(key, value) | |
| 170 | await batch.flush() | |
| 171 | } | |
| 172 | ||
| 173 | async delete(key, opts) { | |
| 174 | const batch = this.write({ ...opts, capacity: 1, autoDestroy: true }) | |
| 175 | batch.tryDelete(key) | |
| 176 | await batch.flush() | |
| 177 | } | |
| 178 | ||
| 179 | async deleteRange(start, end, opts) { | |
| 180 | const batch = this.write({ ...opts, capacity: 1, autoDestroy: true }) | |
| 181 | batch.tryDeleteRange(start, end) | |
| 182 | await batch.flush() | |
| 183 | } | |
| 184 | ||
| 185 | async compactRange(start = null, end = null, opts = {}) { | |
| 186 | if (typeof end === 'object' && end !== null) { | |
| 187 | opts = end | |
| 188 | end = null | |
| 189 | } else if (typeof start === 'object' && start !== null) { | |
| 190 | opts = start | |
| 191 | start = null | |
| 192 | end = null | |
| 193 | } | |
| 194 | ||
| 195 | await this._state.compactRange(this, start, end, opts) | |
| 196 | } | |
| 197 | ||
| 198 | async approximateSize(start, end, opts = {}) { | |
| 199 | return this._state.approximateSize(this, start, end, opts) | |
| 200 | } | |
| 201 | ||
| 202 | _ref() { | |
| 203 | if (this._snapshot) this._snapshot.ref() | |
| 204 | this._state.handles.inc() | |
| 205 | } | |
| 206 | ||
| 207 | _unref() { | |
| 208 | if (this._snapshot) this._snapshot.unref() | |
| 209 | this._state.handles.dec() | |
| 210 | } | |
| 211 | } | |
| 212 | ||
| 213 | module.exports = exports = RocksDB | |
| 214 | ||
| 215 | exports.constants = constants | |
| 216 | ||
| 217 | exports.ColumnFamily = ColumnFamily | |
| 218 | exports.BloomFilterPolicy = BloomFilterPolicy | |
| 219 | exports.RibbonFilterPolicy = RibbonFilterPolicy | |
| 220 | ||
| 221 | function maybeClosed(db) { | |
| 222 | if (db._state.closing || db._index === -1) throw new Error('RocksDB session is closed') | |
| 223 | } |
| … | … | @@ -0,0 +1,438 @@ |
| 1 | const c = require('compact-encoding') | |
| 2 | const binding = require('../binding') | |
| 3 | ||
| 4 | const empty = Buffer.alloc(0) | |
| 5 | const resolved = Promise.resolve() | |
| 6 | ||
| 7 | class RocksDBBatch { | |
| 8 | constructor(db, opts = {}) { | |
| 9 | const { capacity = 8, autoDestroy = false } = opts | |
| 10 | ||
| 11 | db._ref() | |
| 12 | ||
| 13 | this._db = db | |
| 14 | this._destroyed = false | |
| 15 | this._capacity = capacity | |
| 16 | this._operations = [] | |
| 17 | this._promises = [] | |
| 18 | ||
| 19 | this._enqueuePromise = this._enqueuePromise.bind(this) | |
| 20 | ||
| 21 | this._request = null | |
| 22 | this._resolve = null | |
| 23 | this._reject = null | |
| 24 | ||
| 25 | this._handle = null | |
| 26 | this._buffer = null | |
| 27 | this._autoDestroy = autoDestroy | |
| 28 | this._stats = null | |
| 29 | ||
| 30 | this._resetStats() | |
| 31 | ||
| 32 | if (db._state.opened === true) this.ready() | |
| 33 | } | |
| 34 | ||
| 35 | _reuse(db, opts = {}) { | |
| 36 | const { autoDestroy = false } = opts | |
| 37 | ||
| 38 | db._ref() | |
| 39 | ||
| 40 | this._db = db | |
| 41 | this._destroyed = false | |
| 42 | this._autoDestroy = autoDestroy | |
| 43 | } | |
| 44 | ||
| 45 | _onfinished(err) { | |
| 46 | const resolve = this._resolve | |
| 47 | const reject = this._reject | |
| 48 | ||
| 49 | if (this._request) this._db._state.io.dec() | |
| 50 | ||
| 51 | this._operations = [] | |
| 52 | this._promises = [] | |
| 53 | this._request = null | |
| 54 | this._resolve = null | |
| 55 | this._reject = null | |
| 56 | ||
| 57 | this._resetStats() | |
| 58 | ||
| 59 | if (this._autoDestroy === true) this.destroy() | |
| 60 | ||
| 61 | if (reject !== null && err) reject(err) | |
| 62 | else if (resolve !== null) resolve() | |
| 63 | } | |
| 64 | ||
| 65 | _resize() { | |
| 66 | if (this._operations.length <= this._capacity) return false | |
| 67 | ||
| 68 | while (this._operations.length > this._capacity) { | |
| 69 | this._capacity *= 2 | |
| 70 | } | |
| 71 | ||
| 72 | return true | |
| 73 | } | |
| 74 | ||
| 75 | async ready() { | |
| 76 | if (this._handle !== null) return | |
| 77 | ||
| 78 | if (this._db._state.opened === false) await this._db._state.ready() | |
| 79 | ||
| 80 | this._init() | |
| 81 | } | |
| 82 | ||
| 83 | destroy() { | |
| 84 | if (this._request) throw new Error('Request in progress') | |
| 85 | if (this._destroyed) return | |
| 86 | ||
| 87 | this._destroyed = true | |
| 88 | ||
| 89 | if (this._promises.length) this._abort() | |
| 90 | ||
| 91 | this._db._unref() | |
| 92 | this._onfree() | |
| 93 | } | |
| 94 | ||
| 95 | _onfree() { | |
| 96 | this._resetStats() | |
| 97 | this._db._state.freeBatch(this, false) | |
| 98 | this._db = null | |
| 99 | } | |
| 100 | ||
| 101 | _abort() { | |
| 102 | for (let i = 0; i < this._promises.length; i++) { | |
| 103 | const promise = this._promises[i] | |
| 104 | if (promise !== null) promise.reject(new Error('Batch is destroyed')) | |
| 105 | } | |
| 106 | ||
| 107 | this._onfinished(new Error('Batch is destroyed')) | |
| 108 | } | |
| 109 | ||
| 110 | _resetStats() {} | |
| 111 | ||
| 112 | async flush() { | |
| 113 | if (this._request) throw new Error('Request in progress') | |
| 114 | if (this._destroyed) throw new Error('Batch is destroyed') | |
| 115 | ||
| 116 | this._request = new Promise((resolve, reject) => { | |
| 117 | this._resolve = resolve | |
| 118 | this._reject = reject | |
| 119 | }) | |
| 120 | ||
| 121 | this._flush() | |
| 122 | ||
| 123 | return this._request | |
| 124 | } | |
| 125 | ||
| 126 | tryFlush() { | |
| 127 | if (this._request) throw new Error('Request in progress') | |
| 128 | if (this._destroyed) throw new Error('Batch is destroyed') | |
| 129 | ||
| 130 | this._request = resolved | |
| 131 | ||
| 132 | this._flush() | |
| 133 | } | |
| 134 | ||
| 135 | async _flush() { | |
| 136 | if (this._handle === null) await this.ready() | |
| 137 | ||
| 138 | this._db._state.io.inc() | |
| 139 | ||
| 140 | if (this._db._state.resumed !== null) { | |
| 141 | const resumed = await this._db._state.resumed.promise | |
| 142 | ||
| 143 | if (!resumed) { | |
| 144 | if (this._destroyed) { | |
| 145 | this._db._state.io.dec() | |
| 146 | } else { | |
| 147 | this._destroyed = true | |
| 148 | this._abort() | |
| 149 | this._db._unref() | |
| 150 | } | |
| 151 | } | |
| 152 | } | |
| 153 | } | |
| 154 | ||
| 155 | _enqueuePromise(resolve, reject) { | |
| 156 | this._promises.push({ resolve, reject }) | |
| 157 | } | |
| 158 | ||
| 159 | _encodeKey(k) { | |
| 160 | if (this._db._keyEncoding) return c.encode(this._db._keyEncoding, k) | |
| 161 | if (typeof k === 'string') return Buffer.from(k) | |
| 162 | return k | |
| 163 | } | |
| 164 | ||
| 165 | _encodeValue(v) { | |
| 166 | if (this._db._valueEncoding) return c.encode(this._db._valueEncoding, v) | |
| 167 | if (v === null) return empty | |
| 168 | if (typeof v === 'string') return Buffer.from(v) | |
| 169 | return v | |
| 170 | } | |
| 171 | ||
| 172 | _decodeValue(b) { | |
| 173 | if (this._db._valueEncoding) return c.decode(this._db._valueEncoding, b) | |
| 174 | return b | |
| 175 | } | |
| 176 | } | |
| 177 | ||
| 178 | exports.ReadBatch = class RocksDBReadBatch extends RocksDBBatch { | |
| 179 | constructor(db, opts = {}) { | |
| 180 | super(db, opts) | |
| 181 | ||
| 182 | const { asyncIO = false, fillCache = true } = opts | |
| 183 | ||
| 184 | this._asyncIO = asyncIO | |
| 185 | this._fillCache = fillCache | |
| 186 | } | |
| 187 | ||
| 188 | _init() { | |
| 189 | this._handle = binding.readInit() | |
| 190 | this._buffer = binding.readBuffer(this._handle, this._capacity) | |
| 191 | } | |
| 192 | ||
| 193 | _resize() { | |
| 194 | if (super._resize() && this._handle !== null) { | |
| 195 | this._buffer = binding.readBuffer(this._handle, this._capacity) | |
| 196 | } | |
| 197 | } | |
| 198 | ||
| 199 | async _flush() { | |
| 200 | await super._flush() | |
| 201 | ||
| 202 | if (this._destroyed) return | |
| 203 | ||
| 204 | try { | |
| 205 | binding.read( | |
| 206 | this._db._state._handle, | |
| 207 | this._handle, | |
| 208 | this._operations, | |
| 209 | this._db._snapshot ? this._db._snapshot._handle : undefined, | |
| 210 | this._asyncIO, | |
| 211 | this._fillCache, | |
| 212 | this, | |
| 213 | this._onread | |
| 214 | ) | |
| 215 | } catch (err) { | |
| 216 | this._db._state.io.dec() | |
| 217 | throw err | |
| 218 | } | |
| 219 | ||
| 220 | this._db._state.stats.gets += this._stats.gets | |
| 221 | this._db._state.stats.readBatches++ | |
| 222 | } | |
| 223 | ||
| 224 | _onread(errs, values) { | |
| 225 | let applied = true | |
| 226 | ||
| 227 | for (let i = 0, n = this._promises.length; i < n; i++) { | |
| 228 | const err = errs[i] | |
| 229 | if (err) applied = false | |
| 230 | ||
| 231 | const promise = this._promises[i] | |
| 232 | if (promise === null) continue | |
| 233 | ||
| 234 | if (err) promise.reject(err) | |
| 235 | else promise.resolve(values[i] ? this._decodeValue(Buffer.from(values[i])) : null) | |
| 236 | } | |
| 237 | ||
| 238 | this._onfinished(applied ? null : new AggregateError(errs, 'Batch was not applied')) | |
| 239 | } | |
| 240 | ||
| 241 | _resetStats() { | |
| 242 | this._stats = { gets: 0 } | |
| 243 | } | |
| 244 | ||
| 245 | get(key) { | |
| 246 | if (this._request) throw new Error('Request already in progress') | |
| 247 | this._stats.gets++ | |
| 248 | ||
| 249 | const promise = new Promise(this._enqueuePromise) | |
| 250 | ||
| 251 | this._operations.push(new RocksDBGet(this._encodeKey(key), this._db._columnFamily)) | |
| 252 | ||
| 253 | this._resize() | |
| 254 | ||
| 255 | return promise | |
| 256 | } | |
| 257 | } | |
| 258 | ||
| 259 | exports.WriteBatch = class RocksDBWriteBatch extends RocksDBBatch { | |
| 260 | _init() { | |
| 261 | this._handle = binding.writeInit() | |
| 262 | this._buffer = binding.writeBuffer(this._handle, this._capacity) | |
| 263 | } | |
| 264 | ||
| 265 | _resize() { | |
| 266 | if (super._resize() && this._handle !== null) { | |
| 267 | this._buffer = binding.writeBuffer(this._handle, this._capacity) | |
| 268 | } | |
| 269 | } | |
| 270 | ||
| 271 | _onfree() { | |
| 272 | this._resetStats() | |
| 273 | this._db._state.freeBatch(this, true) | |
| 274 | this._db = null | |
| 275 | } | |
| 276 | ||
| 277 | async _flush() { | |
| 278 | await super._flush() | |
| 279 | ||
| 280 | if (this._destroyed) return | |
| 281 | ||
| 282 | try { | |
| 283 | binding.write(this._db._state._handle, this._handle, this._operations, this, this._onwrite) | |
| 284 | } catch (err) { | |
| 285 | this._db._state.io.dec() | |
| 286 | throw err | |
| 287 | } | |
| 288 | ||
| 289 | this._db._state.stats.puts += this._stats.puts | |
| 290 | this._db._state.stats.deletes += this._stats.deletes | |
| 291 | this._db._state.stats.rangeDeletes += this._stats.rangeDeletes | |
| 292 | this._db._state.stats.writeBatches++ | |
| 293 | } | |
| 294 | ||
| 295 | _onwrite(err) { | |
| 296 | const applied = !err | |
| 297 | ||
| 298 | for (let i = 0, n = this._promises.length; i < n; i++) { | |
| 299 | const promise = this._promises[i] | |
| 300 | if (promise === null) continue | |
| 301 | ||
| 302 | if (err) promise.reject(err) | |
| 303 | else promise.resolve() | |
| 304 | } | |
| 305 | ||
| 306 | this._onfinished(applied ? null : new Error('Batch was not applied', { cause: err })) | |
| 307 | } | |
| 308 | ||
| 309 | _resetStats() { | |
| 310 | this._stats = { puts: 0, deletes: 0, rangeDeletes: 0 } | |
| 311 | } | |
| 312 | ||
| 313 | put(key, value) { | |
| 314 | if (this._request) throw new Error('Request already in progress') | |
| 315 | this._stats.puts++ | |
| 316 | ||
| 317 | const promise = new Promise(this._enqueuePromise) | |
| 318 | ||
| 319 | this._operations.push( | |
| 320 | new RocksDBPut(this._encodeKey(key), this._encodeValue(value), this._db._columnFamily) | |
| 321 | ) | |
| 322 | ||
| 323 | this._resize() | |
| 324 | ||
| 325 | return promise | |
| 326 | } | |
| 327 | ||
| 328 | tryPut(key, value) { | |
| 329 | if (this._request) throw new Error('Request already in progress') | |
| 330 | this._stats.puts++ | |
| 331 | ||
| 332 | this._operations.push( | |
| 333 | new RocksDBPut(this._encodeKey(key), this._encodeValue(value), this._db._columnFamily) | |
| 334 | ) | |
| 335 | ||
| 336 | this._promises.push(null) | |
| 337 | ||
| 338 | this._resize() | |
| 339 | } | |
| 340 | ||
| 341 | delete(key) { | |
| 342 | if (this._request) throw new Error('Request already in progress') | |
| 343 | this._stats.deletes++ | |
| 344 | ||
| 345 | const promise = new Promise(this._enqueuePromise) | |
| 346 | ||
| 347 | this._operations.push(new RocksDBDelete(this._encodeKey(key), this._db._columnFamily)) | |
| 348 | ||
| 349 | this._resize() | |
| 350 | ||
| 351 | return promise | |
| 352 | } | |
| 353 | ||
| 354 | tryDelete(key) { | |
| 355 | if (this._request) throw new Error('Request already in progress') | |
| 356 | this._stats.deletes++ | |
| 357 | ||
| 358 | this._operations.push(new RocksDBDelete(this._encodeKey(key), this._db._columnFamily)) | |
| 359 | ||
| 360 | this._promises.push(null) | |
| 361 | ||
| 362 | this._resize() | |
| 363 | } | |
| 364 | ||
| 365 | deleteRange(start, end) { | |
| 366 | if (this._request) throw new Error('Request already in progress') | |
| 367 | this._stats.rangeDeletes++ | |
| 368 | ||
| 369 | const promise = new Promise(this._enqueuePromise) | |
| 370 | ||
| 371 | this._operations.push( | |
| 372 | new RocksDBDeleteRange(this._encodeKey(start), this._encodeKey(end), this._db._columnFamily) | |
| 373 | ) | |
| 374 | ||
| 375 | this._resize() | |
| 376 | ||
| 377 | return promise | |
| 378 | } | |
| 379 | ||
| 380 | tryDeleteRange(start, end) { | |
| 381 | if (this._request) throw new Error('Request already in progress') | |
| 382 | this._stats.rangeDeletes++ | |
| 383 | ||
| 384 | this._operations.push( | |
| 385 | new RocksDBDeleteRange(this._encodeKey(start), this._encodeKey(end), this._db._columnFamily) | |
| 386 | ) | |
| 387 | ||
| 388 | this._promises.push(null) | |
| 389 | ||
| 390 | this._resize() | |
| 391 | } | |
| 392 | } | |
| 393 | ||
| 394 | class RocksDBGet { | |
| 395 | constructor(key, columnFamily) { | |
| 396 | this.key = key | |
| 397 | this.columnFamily = columnFamily._handle | |
| 398 | } | |
| 399 | ||
| 400 | get type() { | |
| 401 | return binding.GET | |
| 402 | } | |
| 403 | } | |
| 404 | ||
| 405 | class RocksDBPut { | |
| 406 | constructor(key, value, columnFamily) { | |
| 407 | this.key = key | |
| 408 | this.value = value | |
| 409 | this.columnFamily = columnFamily._handle | |
| 410 | } | |
| 411 | ||
| 412 | get type() { | |
| 413 | return binding.PUT | |
| 414 | } | |
| 415 | } | |
| 416 | ||
| 417 | class RocksDBDelete { | |
| 418 | constructor(key, columnFamily) { | |
| 419 | this.key = key | |
| 420 | this.columnFamily = columnFamily._handle | |
| 421 | } | |
| 422 | ||
| 423 | get type() { | |
| 424 | return binding.DELETE | |
| 425 | } | |
| 426 | } | |
| 427 | ||
| 428 | class RocksDBDeleteRange { | |
| 429 | constructor(start, end, columnFamily) { | |
| 430 | this.start = start | |
| 431 | this.end = end | |
| 432 | this.columnFamily = columnFamily._handle | |
| 433 | } | |
| 434 | ||
| 435 | get type() { | |
| 436 | return binding.DELETE_RANGE | |
| 437 | } | |
| 438 | } |
| … | … | @@ -0,0 +1,108 @@ |
| 1 | const binding = require('../binding') | |
| 2 | const constants = require('./constants') | |
| 3 | const { BloomFilterPolicy } = require('./filter-policy') | |
| 4 | ||
| 5 | class RocksDBColumnFamily { | |
| 6 | constructor(name, opts = {}) { | |
| 7 | const { | |
| 8 | // Blob options | |
| 9 | enableBlobFiles = false, | |
| 10 | minBlobSize = 0, | |
| 11 | blobFileSize = 0, | |
| 12 | enableBlobGarbageCollection = true, | |
| 13 | // Block table options | |
| 14 | tableBlockSize = 8192, | |
| 15 | tableCacheIndexAndFilterBlocks = true, | |
| 16 | tableFormatVersion = 6, | |
| 17 | optimizeFiltersForMemory = false, | |
| 18 | blockCache = true, | |
| 19 | filterPolicy = new BloomFilterPolicy(10), | |
| 20 | topLevelIndexPinningTier = constants.pinningTier.ALL, | |
| 21 | partitionPinningTier = constants.pinningTier.ALL, | |
| 22 | unpartitionedPinningTier = constants.pinningTier.ALL, | |
| 23 | optimizeFiltersForHits = false, | |
| 24 | numLevels = 7, | |
| 25 | maxWriteBufferNumber = 2, | |
| 26 | blobGarbageCollectionAgeCutOff = 0.25, | |
| 27 | blobGarbageCollectionForceThreshold = 1.0 | |
| 28 | } = opts | |
| 29 | ||
| 30 | this._name = name | |
| 31 | this._flushing = null | |
| 32 | this._options = { | |
| 33 | enableBlobFiles, | |
| 34 | minBlobSize, | |
| 35 | blobFileSize, | |
| 36 | enableBlobGarbageCollection, | |
| 37 | tableBlockSize, | |
| 38 | tableCacheIndexAndFilterBlocks, | |
| 39 | tableFormatVersion, | |
| 40 | optimizeFiltersForMemory, | |
| 41 | blockCache, | |
| 42 | filterPolicy, | |
| 43 | topLevelIndexPinningTier, | |
| 44 | partitionPinningTier, | |
| 45 | unpartitionedPinningTier, | |
| 46 | optimizeFiltersForHits, | |
| 47 | numLevels, | |
| 48 | maxWriteBufferNumber | |
| 49 | } | |
| 50 | ||
| 51 | const filterPolicyArguments = [0, 0, 0] | |
| 52 | ||
| 53 | if (filterPolicy !== null) { | |
| 54 | filterPolicyArguments[0] = filterPolicy.type | |
| 55 | ||
| 56 | switch (filterPolicy.type) { | |
| 57 | case 1: // Bloom filter policy | |
| 58 | filterPolicyArguments[1] = filterPolicy.bitsPerKey | |
| 59 | break | |
| 60 | case 2: // Ribbon filter policy | |
| 61 | filterPolicyArguments[1] = filterPolicy.bloomEquivalentBitsPerKey | |
| 62 | filterPolicyArguments[2] = filterPolicy.bloomBeforeLevel | |
| 63 | ||
| 64 | break | |
| 65 | } | |
| 66 | } | |
| 67 | ||
| 68 | this._handle = binding.columnFamilyInit( | |
| 69 | name, | |
| 70 | enableBlobFiles, | |
| 71 | minBlobSize, | |
| 72 | blobFileSize, | |
| 73 | enableBlobGarbageCollection, | |
| 74 | tableBlockSize, | |
| 75 | tableCacheIndexAndFilterBlocks, | |
| 76 | tableFormatVersion, | |
| 77 | optimizeFiltersForMemory, | |
| 78 | blockCache === false, | |
| 79 | ...filterPolicyArguments, | |
| 80 | topLevelIndexPinningTier, | |
| 81 | partitionPinningTier, | |
| 82 | unpartitionedPinningTier, | |
| 83 | optimizeFiltersForHits, | |
| 84 | numLevels, | |
| 85 | maxWriteBufferNumber, | |
| 86 | blobGarbageCollectionAgeCutOff, | |
| 87 | blobGarbageCollectionForceThreshold | |
| 88 | ) | |
| 89 | } | |
| 90 | ||
| 91 | cloneSettings(name) { | |
| 92 | return new RocksDBColumnFamily(name, this._options) | |
| 93 | } | |
| 94 | ||
| 95 | get name() { | |
| 96 | return this._name | |
| 97 | } | |
| 98 | ||
| 99 | destroy() { | |
| 100 | if (this._handle === null) return | |
| 101 | ||
| 102 | binding.columnFamilyDestroy(this._handle) | |
| 103 | ||
| 104 | this._handle = null | |
| 105 | } | |
| 106 | } | |
| 107 | ||
| 108 | module.exports = RocksDBColumnFamily |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | module.exports = { | |
| 2 | pinningTier: { | |
| 3 | NONE: 0, | |
| 4 | FLUSHED_AND_SIMILAR: 1, | |
| 5 | ALL: 2 | |
| 6 | }, | |
| 7 | garbageCollectionPolicy: { | |
| 8 | DEFAULT: 0, | |
| 9 | FORCE: 1, | |
| 10 | DISABLE: 2 | |
| 11 | }, | |
| 12 | bottommostLevelCompaction: { | |
| 13 | NONE: 0, | |
| 14 | SKIP: 1, | |
| 15 | FORCE: 2 | |
| 16 | }, | |
| 17 | walRecoveryMode: { | |
| 18 | TOLERATE_CORRUPTED_TAIL_RECORDS: 0, | |
| 19 | ABSOLUTE_CONSISTENCY: 1, | |
| 20 | POINT_IN_TIME: 2, | |
| 21 | SKIP_ANY_CORRUPTED_RECORDS: 3 | |
| 22 | } | |
| 23 | } |
| … | … | @@ -0,0 +1,20 @@ |
| 1 | exports.BloomFilterPolicy = class RocksDBBloomFilterPolicy { | |
| 2 | get type() { | |
| 3 | return 1 | |
| 4 | } | |
| 5 | ||
| 6 | constructor(bitsPerKey) { | |
| 7 | this.bitsPerKey = bitsPerKey | |
| 8 | } | |
| 9 | } | |
| 10 | ||
| 11 | exports.RibbonFilterPolicy = class RocksDBRibbonFilterPolicy { | |
| 12 | get type() { | |
| 13 | return 2 | |
| 14 | } | |
| 15 | ||
| 16 | constructor(bloomEquivalentBitsPerKey, bloomBeforeLevel = 0) { | |
| 17 | this.bloomEquivalentBitsPerKey = bloomEquivalentBitsPerKey | |
| 18 | this.bloomBeforeLevel = bloomBeforeLevel | |
| 19 | } | |
| 20 | } |
| … | … | @@ -0,0 +1,208 @@ |
| 1 | const { Readable } = require('streamx') | |
| 2 | const c = require('compact-encoding') | |
| 3 | const binding = require('../binding') | |
| 4 | ||
| 5 | const empty = Buffer.alloc(0) | |
| 6 | ||
| 7 | module.exports = class RocksDBIterator extends Readable { | |
| 8 | constructor(db, opts = {}) { | |
| 9 | const { | |
| 10 | gt = null, | |
| 11 | gte = null, | |
| 12 | lt = null, | |
| 13 | lte = null, | |
| 14 | reverse = false, | |
| 15 | values = true, | |
| 16 | limit = Infinity, | |
| 17 | capacity = 8 | |
| 18 | } = opts | |
| 19 | ||
| 20 | super() | |
| 21 | ||
| 22 | db._ref() | |
| 23 | ||
| 24 | this._db = db | |
| 25 | ||
| 26 | this._gt = gt ? this._encodeKey(gt) : empty | |
| 27 | this._gte = gte ? this._encodeKey(gte) : empty | |
| 28 | this._lt = lt ? this._encodeKey(lt) : empty | |
| 29 | this._lte = lte ? this._encodeKey(lte) : empty | |
| 30 | ||
| 31 | this._reverse = reverse | |
| 32 | this._values = values | |
| 33 | this._limit = limit < 0 ? Infinity : limit | |
| 34 | this._capacity = capacity | |
| 35 | this._opened = false | |
| 36 | ||
| 37 | this._pendingOpen = null | |
| 38 | this._pendingRead = null | |
| 39 | this._pendingDestroy = null | |
| 40 | ||
| 41 | this._buffer = null | |
| 42 | this._handle = null | |
| 43 | ||
| 44 | if (this._db._state.opened === true) this.ready() | |
| 45 | } | |
| 46 | ||
| 47 | _onopen(err) { | |
| 48 | const cb = this._pendingOpen | |
| 49 | this._pendingOpen = null | |
| 50 | this._opened = true | |
| 51 | this._db._state.io.dec() | |
| 52 | cb(err) | |
| 53 | } | |
| 54 | ||
| 55 | _onread(err, keys, values) { | |
| 56 | const cb = this._pendingRead | |
| 57 | this._pendingRead = null | |
| 58 | this._db._state.io.dec() | |
| 59 | if (err) return cb(err) | |
| 60 | ||
| 61 | const n = keys.length | |
| 62 | ||
| 63 | this._limit -= n | |
| 64 | ||
| 65 | for (let i = 0; i < n; i++) { | |
| 66 | this.push({ | |
| 67 | key: this._decodeKey(Buffer.from(keys[i])), | |
| 68 | value: this._values ? this._decodeValue(Buffer.from(values[i])) : null | |
| 69 | }) | |
| 70 | } | |
| 71 | ||
| 72 | if (n < this._capacity) this.push(null) | |
| 73 | ||
| 74 | cb(null) | |
| 75 | } | |
| 76 | ||
| 77 | _onclose(err) { | |
| 78 | const cb = this._pendingDestroy | |
| 79 | this._pendingDestroy = null | |
| 80 | this._db._state.io.dec() | |
| 81 | this._db._unref() | |
| 82 | cb(err) | |
| 83 | } | |
| 84 | ||
| 85 | _resize() { | |
| 86 | if (this._handle !== null) { | |
| 87 | this._buffer = binding.iteratorBuffer(this._handle, this._capacity) | |
| 88 | } | |
| 89 | } | |
| 90 | ||
| 91 | async ready() { | |
| 92 | if (this._handle !== null) return | |
| 93 | ||
| 94 | if (this._db._state.opened === false) await this._db._state.ready() | |
| 95 | ||
| 96 | this._init() | |
| 97 | } | |
| 98 | ||
| 99 | _init() { | |
| 100 | this._handle = binding.iteratorInit() | |
| 101 | this._buffer = binding.iteratorBuffer(this._handle, this._capacity) | |
| 102 | } | |
| 103 | ||
| 104 | async _open(cb) { | |
| 105 | await this.ready() | |
| 106 | ||
| 107 | this._db._state.io.inc() | |
| 108 | ||
| 109 | if (this._db._state.resumed !== null) { | |
| 110 | const resumed = await this._db._state.resumed.promise | |
| 111 | ||
| 112 | if (!resumed) { | |
| 113 | this._db._state.io.dec() | |
| 114 | ||
| 115 | return cb(new Error('RocksDB session is closed')) | |
| 116 | } | |
| 117 | } | |
| 118 | ||
| 119 | this._pendingOpen = cb | |
| 120 | ||
| 121 | try { | |
| 122 | binding.iteratorOpen( | |
| 123 | this._db._state._handle, | |
| 124 | this._handle, | |
| 125 | this._db._columnFamily._handle, | |
| 126 | this._gt, | |
| 127 | this._gte, | |
| 128 | this._lt, | |
| 129 | this._lte, | |
| 130 | this._reverse, | |
| 131 | !this._values, // Keys only | |
| 132 | this._db._snapshot ? this._db._snapshot._handle : undefined, | |
| 133 | this, | |
| 134 | this._onopen, | |
| 135 | this._onclose, | |
| 136 | this._onread | |
| 137 | ) | |
| 138 | } catch (err) { | |
| 139 | this._db._state.io.dec() | |
| 140 | ||
| 141 | cb(err) | |
| 142 | } | |
| 143 | } | |
| 144 | ||
| 145 | async _read(cb) { | |
| 146 | this._db._state.io.inc() | |
| 147 | ||
| 148 | if (this._db._state.resumed !== null) { | |
| 149 | const resumed = await this._db._state.resumed.promise | |
| 150 | ||
| 151 | if (!resumed) { | |
| 152 | this._db._state.io.dec() | |
| 153 | ||
| 154 | return cb(new Error('RocksDB session is closed')) | |
| 155 | } | |
| 156 | } | |
| 157 | ||
| 158 | this._pendingRead = cb | |
| 159 | ||
| 160 | try { | |
| 161 | binding.iteratorRead(this._handle, Math.min(this._capacity, this._limit)) | |
| 162 | } catch (err) { | |
| 163 | this._db._state.io.dec() | |
| 164 | ||
| 165 | cb(err) | |
| 166 | } | |
| 167 | } | |
| 168 | ||
| 169 | async _destroy(cb) { | |
| 170 | await this.ready() | |
| 171 | ||
| 172 | this._db._state.io.inc() | |
| 173 | ||
| 174 | if (this._opened === false) { | |
| 175 | this._db._state.io.dec() | |
| 176 | this._db._unref() | |
| 177 | ||
| 178 | return cb(null) | |
| 179 | } | |
| 180 | ||
| 181 | this._pendingDestroy = cb | |
| 182 | ||
| 183 | try { | |
| 184 | binding.iteratorClose(this._handle) | |
| 185 | } catch (err) { | |
| 186 | this._db._state.io.dec() | |
| 187 | this._db._unref() | |
| 188 | ||
| 189 | cb(err) | |
| 190 | } | |
| 191 | } | |
| 192 | ||
| 193 | _encodeKey(k) { | |
| 194 | if (this._db._keyEncoding !== null) return c.encode(this._db._keyEncoding, k) | |
| 195 | if (typeof k === 'string') return Buffer.from(k) | |
| 196 | return k | |
| 197 | } | |
| 198 | ||
| 199 | _decodeKey(b) { | |
| 200 | if (this._db._keyEncoding !== null) return c.decode(this._db._keyEncoding, b) | |
| 201 | return b | |
| 202 | } | |
| 203 | ||
| 204 | _decodeValue(b) { | |
| 205 | if (this._db._valueEncoding !== null) return c.decode(this._db._valueEncoding, b) | |
| 206 | return b | |
| 207 | } | |
| 208 | } |
| … | … | @@ -0,0 +1,30 @@ |
| 1 | const binding = require('../binding') | |
| 2 | ||
| 3 | module.exports = class RocksDBSnapshot { | |
| 4 | constructor(state) { | |
| 5 | this._state = state | |
| 6 | ||
| 7 | this._handle = null | |
| 8 | this._refs = 0 | |
| 9 | ||
| 10 | if (state.deferSnapshotInit === false) this._init() | |
| 11 | } | |
| 12 | ||
| 13 | _init() { | |
| 14 | this._handle = binding.snapshotCreate(this._state._handle) | |
| 15 | } | |
| 16 | ||
| 17 | ref() { | |
| 18 | this._refs++ | |
| 19 | } | |
| 20 | ||
| 21 | unref() { | |
| 22 | if (--this._refs > 0) return | |
| 23 | ||
| 24 | if (this._handle === null) return | |
| 25 | ||
| 26 | binding.snapshotDestroy(this._handle) | |
| 27 | ||
| 28 | this._handle = null | |
| 29 | } | |
| 30 | } |
| … | … | @@ -0,0 +1,435 @@ |
| 1 | const ReadyResource = require('ready-resource') | |
| 2 | const RefCounter = require('refcounter') | |
| 3 | const rrp = require('resolve-reject-promise') | |
| 4 | const SignalPromise = require('signal-promise') | |
| 5 | const c = require('compact-encoding') | |
| 6 | const { ReadBatch, WriteBatch } = require('./batch') | |
| 7 | const ColumnFamily = require('./column-family') | |
| 8 | const binding = require('../binding') | |
| 9 | const constants = require('./constants') | |
| 10 | ||
| 11 | const MAX_BATCH_REUSE = 64 | |
| 12 | const empty = Buffer.alloc(0) | |
| 13 | ||
| 14 | module.exports = class RocksDBState extends ReadyResource { | |
| 15 | constructor(db, path, opts) { | |
| 16 | super() | |
| 17 | ||
| 18 | const { | |
| 19 | columnFamily = new ColumnFamily('default', opts), | |
| 20 | columnFamilies = [], | |
| 21 | readOnly = false, | |
| 22 | createIfMissing = true, | |
| 23 | createMissingColumnFamilies = true, | |
| 24 | maxBackgroundJobs = 6, | |
| 25 | bytesPerSync = 1048576, | |
| 26 | maxOpenFiles = -1, | |
| 27 | useDirectReads = false, | |
| 28 | avoidUnnecessaryBlockingIO = false, | |
| 29 | skipStatsUpdateOnOpen = false, | |
| 30 | useDirectIOForFlushAndCompaction = false, | |
| 31 | maxFileOpeningThreads = 16, | |
| 32 | lock = null, | |
| 33 | walRecoveryMode = constants.walRecoveryMode.POINT_IN_TIME, | |
| 34 | bestEffortsRecovery = false | |
| 35 | } = opts | |
| 36 | ||
| 37 | this.path = path | |
| 38 | this.db = db | |
| 39 | this.handles = new RefCounter() | |
| 40 | this.io = new RefCounter() | |
| 41 | this.sessions = [] | |
| 42 | this.columnFamilies = [columnFamily] | |
| 43 | this.deferSnapshotInit = true | |
| 44 | this.resumed = null | |
| 45 | this.stats = { | |
| 46 | gets: 0, | |
| 47 | puts: 0, | |
| 48 | deletes: 0, | |
| 49 | rangeDeletes: 0, | |
| 50 | readBatches: 0, | |
| 51 | writeBatches: 0 | |
| 52 | } | |
| 53 | ||
| 54 | this._suspended = false | |
| 55 | this._suspending = false | |
| 56 | this._updating = false | |
| 57 | this._updatingSignal = new SignalPromise() | |
| 58 | this._columnsFlushed = false | |
| 59 | this._lock = lock | |
| 60 | this._readBatches = [] | |
| 61 | this._writeBatches = [] | |
| 62 | ||
| 63 | for (const columnFamily of columnFamilies) { | |
| 64 | this.columnFamilies.push( | |
| 65 | typeof columnFamily === 'string' ? new ColumnFamily(columnFamily, opts) : columnFamily | |
| 66 | ) | |
| 67 | } | |
| 68 | ||
| 69 | this._handle = binding.init( | |
| 70 | readOnly, | |
| 71 | createIfMissing, | |
| 72 | createMissingColumnFamilies, | |
| 73 | maxBackgroundJobs, | |
| 74 | bytesPerSync, | |
| 75 | maxOpenFiles, | |
| 76 | useDirectReads, | |
| 77 | avoidUnnecessaryBlockingIO, | |
| 78 | skipStatsUpdateOnOpen, | |
| 79 | useDirectIOForFlushAndCompaction, | |
| 80 | maxFileOpeningThreads, | |
| 81 | walRecoveryMode, | |
| 82 | bestEffortsRecovery | |
| 83 | ) | |
| 84 | } | |
| 85 | ||
| 86 | createReadBatch(db, opts) { | |
| 87 | if (this._readBatches.length === 0) return new ReadBatch(db, opts) | |
| 88 | const batch = this._readBatches.pop() | |
| 89 | batch._reuse(db, opts) | |
| 90 | return batch | |
| 91 | } | |
| 92 | ||
| 93 | createWriteBatch(db, opts) { | |
| 94 | if (this._writeBatches.length === 0) return new WriteBatch(db, opts) | |
| 95 | const batch = this._writeBatches.pop() | |
| 96 | batch._reuse(db, opts) | |
| 97 | return batch | |
| 98 | } | |
| 99 | ||
| 100 | freeBatch(batch, writable) { | |
| 101 | if (batch._capacity > 16) return | |
| 102 | const queue = writable ? this._writeBatches : this._readBatches | |
| 103 | if (queue.length >= MAX_BATCH_REUSE) return | |
| 104 | queue.push(batch) | |
| 105 | } | |
| 106 | ||
| 107 | addSession(db) { | |
| 108 | db._index = this.sessions.push(db) - 1 | |
| 109 | if (db._snapshot) db._snapshot.ref() | |
| 110 | } | |
| 111 | ||
| 112 | removeSession(db) { | |
| 113 | const head = this.sessions.pop() | |
| 114 | if (head !== db) this.sessions[(head._index = db._index)] = head | |
| 115 | db._index = -1 | |
| 116 | if (db._snapshot) db._snapshot.unref() | |
| 117 | } | |
| 118 | ||
| 119 | upsertColumnFamily(c) { | |
| 120 | if (typeof c === 'string') { | |
| 121 | let col = this.getColumnFamilyByName(c) | |
| 122 | if (col) return col | |
| 123 | col = this.columnFamilies[0].cloneSettings(c) | |
| 124 | this.columnFamilies.push(col) | |
| 125 | return col | |
| 126 | } | |
| 127 | ||
| 128 | if (this.columnFamilies.includes(c)) return c | |
| 129 | this.columnFamilies.push(c) | |
| 130 | return c | |
| 131 | } | |
| 132 | ||
| 133 | getColumnFamily(c) { | |
| 134 | if (!c) return this.columnFamilies[0] | |
| 135 | if (!this._columnsFlushed) return this.upsertColumnFamily(c) | |
| 136 | ||
| 137 | if (typeof c !== 'string') return c | |
| 138 | ||
| 139 | const col = this.getColumnFamilyByName(c) | |
| 140 | if (col === null) throw new Error('Unknown column family') | |
| 141 | return col | |
| 142 | } | |
| 143 | ||
| 144 | getColumnFamilyByName(name) { | |
| 145 | for (const col of this.columnFamilies) { | |
| 146 | if (col.name === name) return col | |
| 147 | } | |
| 148 | return null | |
| 149 | } | |
| 150 | ||
| 151 | async _open() { | |
| 152 | await Promise.resolve() // Allow column families to populate if on-demand | |
| 153 | ||
| 154 | if (this._lock) await this._lock.ready() | |
| 155 | ||
| 156 | const req = { resolve: null, reject: null, handle: null } | |
| 157 | ||
| 158 | const promise = new Promise((resolve, reject) => { | |
| 159 | req.resolve = resolve | |
| 160 | req.reject = reject | |
| 161 | }) | |
| 162 | ||
| 163 | this._columnsFlushed = true | |
| 164 | ||
| 165 | const lock = this._lock === null ? -1 : this._lock.transfer() | |
| 166 | ||
| 167 | req.handle = binding.open( | |
| 168 | this._handle, | |
| 169 | this, | |
| 170 | this.path, | |
| 171 | this.columnFamilies.map((c) => c._handle), | |
| 172 | lock, | |
| 173 | req, | |
| 174 | onopen | |
| 175 | ) | |
| 176 | ||
| 177 | await promise | |
| 178 | ||
| 179 | this.deferSnapshotInit = false | |
| 180 | ||
| 181 | for (const session of this.sessions) { | |
| 182 | if (session._snapshot) session._snapshot._init() | |
| 183 | } | |
| 184 | ||
| 185 | function onopen(err) { | |
| 186 | if (err) req.reject(err) | |
| 187 | else req.resolve() | |
| 188 | } | |
| 189 | } | |
| 190 | ||
| 191 | async _close() { | |
| 192 | while (this._updating) await this._updatingSignal.wait() | |
| 193 | if (this.resumed) this.resumed.resolve(false) | |
| 194 | ||
| 195 | while (!this.io.isIdle()) await this.io.idle() | |
| 196 | while (!this.handles.isIdle()) await this.handles.idle() | |
| 197 | ||
| 198 | while (this.sessions.length > 0) { | |
| 199 | await this.sessions[this.sessions.length - 1].close() | |
| 200 | } | |
| 201 | ||
| 202 | for (const columnFamily of this.columnFamilies) columnFamily.destroy() | |
| 203 | ||
| 204 | const req = { resolve: null, reject: null, handle: null } | |
| 205 | ||
| 206 | const promise = new Promise((resolve, reject) => { | |
| 207 | req.resolve = resolve | |
| 208 | req.reject = reject | |
| 209 | }) | |
| 210 | ||
| 211 | req.handle = binding.close(this._handle, req, onclose) | |
| 212 | ||
| 213 | try { | |
| 214 | await promise | |
| 215 | } finally { | |
| 216 | if (this._lock) await this._lock.close() | |
| 217 | } | |
| 218 | ||
| 219 | function onclose(err) { | |
| 220 | if (err) req.reject(err) | |
| 221 | else req.resolve() | |
| 222 | } | |
| 223 | } | |
| 224 | ||
| 225 | async flush(db, opts) { | |
| 226 | if (this.opened === false) await this.ready() | |
| 227 | ||
| 228 | this.io.inc() | |
| 229 | ||
| 230 | if (this.resumed !== null) { | |
| 231 | const resumed = await this.resumed.promise | |
| 232 | ||
| 233 | if (!resumed) { | |
| 234 | this.io.dec() | |
| 235 | ||
| 236 | throw new Error('RocksDB session is closed') | |
| 237 | } | |
| 238 | } | |
| 239 | ||
| 240 | const req = { resolve: null, reject: null, handle: null } | |
| 241 | ||
| 242 | const promise = new Promise((resolve, reject) => { | |
| 243 | req.resolve = resolve | |
| 244 | req.reject = reject | |
| 245 | }) | |
| 246 | ||
| 247 | try { | |
| 248 | req.handle = binding.flush(this._handle, db._columnFamily._handle, req, onflush) | |
| 249 | ||
| 250 | await promise | |
| 251 | } finally { | |
| 252 | this.io.dec() | |
| 253 | } | |
| 254 | ||
| 255 | function onflush(err) { | |
| 256 | if (err) req.reject(err) | |
| 257 | else req.resolve() | |
| 258 | } | |
| 259 | } | |
| 260 | ||
| 261 | suspend() { | |
| 262 | this._suspending = true | |
| 263 | return this.update() | |
| 264 | } | |
| 265 | ||
| 266 | resume() { | |
| 267 | this._suspending = false | |
| 268 | return this.update() | |
| 269 | } | |
| 270 | ||
| 271 | async update() { | |
| 272 | while (this._updating) await this._updatingSignal.wait() | |
| 273 | if (this._suspending === this._suspended || this.closing) return | |
| 274 | this._updating = true | |
| 275 | try { | |
| 276 | if (this._suspending) await this._suspend() | |
| 277 | else await this._resume() | |
| 278 | } finally { | |
| 279 | this._updating = false | |
| 280 | this._updatingSignal.notify() | |
| 281 | } | |
| 282 | } | |
| 283 | ||
| 284 | async _suspend() { | |
| 285 | if (this._suspended === true) return | |
| 286 | ||
| 287 | while (!this.io.isIdle()) await this.io.idle() | |
| 288 | ||
| 289 | this.io.inc() | |
| 290 | this.resumed = rrp() | |
| 291 | ||
| 292 | const req = { resolve: null, reject: null, handle: null } | |
| 293 | ||
| 294 | const promise = new Promise((resolve, reject) => { | |
| 295 | req.resolve = resolve | |
| 296 | req.reject = reject | |
| 297 | }) | |
| 298 | ||
| 299 | try { | |
| 300 | req.handle = binding.suspend(this._handle, req, onsuspend) | |
| 301 | ||
| 302 | await promise | |
| 303 | ||
| 304 | this._suspended = true | |
| 305 | } finally { | |
| 306 | this.io.dec() | |
| 307 | } | |
| 308 | ||
| 309 | function onsuspend(err) { | |
| 310 | if (err) req.reject(err) | |
| 311 | else req.resolve() | |
| 312 | } | |
| 313 | } | |
| 314 | ||
| 315 | async _resume() { | |
| 316 | if (this._suspended === false) return | |
| 317 | ||
| 318 | const req = { resolve: null, reject: null, handle: null } | |
| 319 | ||
| 320 | const promise = new Promise((resolve, reject) => { | |
| 321 | req.resolve = resolve | |
| 322 | req.reject = reject | |
| 323 | }) | |
| 324 | ||
| 325 | req.handle = binding.resume(this._handle, req, onresume) | |
| 326 | ||
| 327 | await promise | |
| 328 | ||
| 329 | this._suspended = false | |
| 330 | ||
| 331 | const resumed = this.resumed | |
| 332 | this.resumed = null | |
| 333 | resumed.resolve(true) | |
| 334 | ||
| 335 | function onresume(err) { | |
| 336 | if (err) req.reject(err) | |
| 337 | else req.resolve() | |
| 338 | } | |
| 339 | } | |
| 340 | ||
| 341 | async compactRange(db, start, end, opts) { | |
| 342 | if (this.opened === false) await this.ready() | |
| 343 | ||
| 344 | this.io.inc() | |
| 345 | ||
| 346 | const { | |
| 347 | exclusive = false, | |
| 348 | blobGarbageCollectionPolicy = constants.garbageCollectionPolicy.DEFAULT, | |
| 349 | blobGarbageCollectionAgeCutoff = 0.25, | |
| 350 | bottommostLevelCompaction = constants.bottommostLevelCompaction.NONE | |
| 351 | } = opts | |
| 352 | ||
| 353 | start = this._encodeKey(start) | |
| 354 | end = this._encodeKey(end) | |
| 355 | ||
| 356 | const req = { resolve: null, reject: null, handle: null, start, end } | |
| 357 | ||
| 358 | const promise = new Promise((resolve, reject) => { | |
| 359 | req.resolve = resolve | |
| 360 | req.reject = reject | |
| 361 | }) | |
| 362 | ||
| 363 | try { | |
| 364 | req.handle = binding.compactRange( | |
| 365 | this._handle, | |
| 366 | db._columnFamily._handle, | |
| 367 | start, | |
| 368 | end, | |
| 369 | exclusive, | |
| 370 | blobGarbageCollectionPolicy, | |
| 371 | blobGarbageCollectionAgeCutoff, | |
| 372 | bottommostLevelCompaction, | |
| 373 | req, | |
| 374 | oncompactrange | |
| 375 | ) | |
| 376 | ||
| 377 | await promise | |
| 378 | } finally { | |
| 379 | this.io.dec() | |
| 380 | } | |
| 381 | ||
| 382 | function oncompactrange(err) { | |
| 383 | if (err) req.reject(err) | |
| 384 | else req.resolve() | |
| 385 | } | |
| 386 | } | |
| 387 | ||
| 388 | async approximateSize(db, start, end, opts) { | |
| 389 | if (this.opened === false) await this.ready() | |
| 390 | ||
| 391 | this.io.inc() | |
| 392 | ||
| 393 | const { includeMemtables = false, includeFiles = true, filesSizeErrorMargin = -1 } = opts | |
| 394 | ||
| 395 | start = this._encodeKey(start) | |
| 396 | end = this._encodeKey(end) | |
| 397 | ||
| 398 | const req = { resolve: null, reject: null, handle: null, start, end } | |
| 399 | ||
| 400 | const promise = new Promise((resolve, reject) => { | |
| 401 | req.resolve = resolve | |
| 402 | req.reject = reject | |
| 403 | }) | |
| 404 | ||
| 405 | try { | |
| 406 | req.handle = binding.approximateSize( | |
| 407 | this._handle, | |
| 408 | db._columnFamily._handle, | |
| 409 | start, | |
| 410 | end, | |
| 411 | includeMemtables, | |
| 412 | includeFiles, | |
| 413 | filesSizeErrorMargin, | |
| 414 | req, | |
| 415 | onapproximatesize | |
| 416 | ) | |
| 417 | ||
| 418 | return await promise | |
| 419 | } finally { | |
| 420 | this.io.dec() | |
| 421 | } | |
| 422 | ||
| 423 | function onapproximatesize(err, result) { | |
| 424 | if (err) req.reject(err) | |
| 425 | else req.resolve(result) | |
| 426 | } | |
| 427 | } | |
| 428 | ||
| 429 | _encodeKey(k) { | |
| 430 | if (this.db._keyEncoding) return c.encode(this.db._keyEncoding, k) | |
| 431 | if (typeof k === 'string') return Buffer.from(k) | |
| 432 | if (k === null) return empty | |
| 433 | return k | |
| 434 | } | |
| 435 | } |
| … | … | @@ -0,0 +1,77 @@ |
| 1 | { | |
| 2 | "name": "rocksdb-native", | |
| 3 | "version": "3.15.0", | |
| 4 | "description": "librocksdb bindings for JavaScript", | |
| 5 | "exports": { | |
| 6 | ".": "./index.js", | |
| 7 | "./package": "./package.json" | |
| 8 | }, | |
| 9 | "imports": { | |
| 10 | "crypto": { | |
| 11 | "bare": "bare-crypto", | |
| 12 | "default": "crypto" | |
| 13 | }, | |
| 14 | "fs": { | |
| 15 | "bare": "bare-fs", | |
| 16 | "default": "fs" | |
| 17 | }, | |
| 18 | "path": { | |
| 19 | "bare": "bare-path", | |
| 20 | "default": "path" | |
| 21 | } | |
| 22 | }, | |
| 23 | "files": [ | |
| 24 | "index.js", | |
| 25 | "binding.cc", | |
| 26 | "binding.js", | |
| 27 | "CMakeLists.txt", | |
| 28 | "lib", | |
| 29 | "prebuilds", | |
| 30 | "!prebuilds/android-*/**/*.node" | |
| 31 | ], | |
| 32 | "addon": true, | |
| 33 | "scripts": { | |
| 34 | "format": "prettier --write .", | |
| 35 | "lint": "prettier --check . && lunte", | |
| 36 | "test": "npm run test:node && npm run test:bare", | |
| 37 | "test:bare": "brittle-bare test.js", | |
| 38 | "test:node": "brittle-node test.js" | |
| 39 | }, | |
| 40 | "repository": { | |
| 41 | "type": "git", | |
| 42 | "url": "git+https://github.com/holepunchto/rocksdb-native.git" | |
| 43 | }, | |
| 44 | "author": "Holepunch Inc", | |
| 45 | "license": "Apache-2.0", | |
| 46 | "bugs": { | |
| 47 | "url": "https://github.com/holepunchto/rocksdb-native/issues" | |
| 48 | }, | |
| 49 | "homepage": "https://github.com/holepunchto/rocksdb-native", | |
| 50 | "engines": { | |
| 51 | "bare": ">=1.16.0" | |
| 52 | }, | |
| 53 | "dependencies": { | |
| 54 | "compact-encoding": "^2.15.0", | |
| 55 | "ready-resource": "^1.0.0", | |
| 56 | "refcounter": "^1.0.0", | |
| 57 | "require-addon": "^1.0.2", | |
| 58 | "resolve-reject-promise": "^1.1.0", | |
| 59 | "signal-promise": "^1.0.3", | |
| 60 | "streamx": "^2.16.1" | |
| 61 | }, | |
| 62 | "devDependencies": { | |
| 63 | "bare-compat-napi": "^1.3.0", | |
| 64 | "bare-crypto": "^1.12.0", | |
| 65 | "bare-fs": "^4.5.0", | |
| 66 | "bare-path": "^3.0.0", | |
| 67 | "brittle": "^3.5.0", | |
| 68 | "cmake-bare": "^1.1.14", | |
| 69 | "cmake-fetch": "^1.0.1", | |
| 70 | "cmake-napi": "^1.0.6", | |
| 71 | "fd-lock": "^2.0.0", | |
| 72 | "lunte": "^1.3.0", | |
| 73 | "paparam": "^1.9.0", | |
| 74 | "prettier": "^3.6.2", | |
| 75 | "prettier-config-holepunch": "^2.0.0" | |
| 76 | } | |
| 77 | } |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2021 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,29 @@ |
| 1 | # safety-catch | |
| 2 | ||
| 3 | Small module that makes sure your catch, caught an actual error and not a programming mistake or assertion. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install safety-catch | |
| 7 | ``` | |
| 8 | ||
| 9 | Triggers an unhandled error in the next tick, when you pass it an SyntaxError, TypeError, AssertionError and other builtins. | |
| 10 | ||
| 11 | ## Usage | |
| 12 | ||
| 13 | ``` js | |
| 14 | const safetyCatch = require('safety-catch') | |
| 15 | ||
| 16 | try { | |
| 17 | foo.bar() | |
| 18 | } catch (err) { | |
| 19 | // Was this a programming mistake and should you crash your program? | |
| 20 | // Ask the safety catch | |
| 21 | safetyCatch(err) | |
| 22 | ||
| 23 | console.log('Actual error') | |
| 24 | } | |
| 25 | ``` | |
| 26 | ||
| 27 | ## License | |
| 28 | ||
| 29 | MIT |
| … | … | @@ -0,0 +1,7 @@ |
| 1 | const safetyCatch = require('./') | |
| 2 | ||
| 3 | try { | |
| 4 | foo.bar() | |
| 5 | } catch (err) { | |
| 6 | safetyCatch(err) | |
| 7 | } |
| … | … | @@ -0,0 +1,24 @@ |
| 1 | module.exports = safetyCatch | |
| 2 | ||
| 3 | function isActuallyUncaught (err) { | |
| 4 | if (!err) return false | |
| 5 | return err instanceof TypeError || | |
| 6 | err instanceof SyntaxError || | |
| 7 | err instanceof ReferenceError || | |
| 8 | err instanceof EvalError || | |
| 9 | err instanceof RangeError || | |
| 10 | err instanceof URIError || | |
| 11 | err.code === 'ERR_ASSERTION' || | |
| 12 | err.name === 'AssertionError' | |
| 13 | } | |
| 14 | ||
| 15 | function throwErrorNT (err) { | |
| 16 | queueMicrotask(() => { throw err }) | |
| 17 | } | |
| 18 | ||
| 19 | function safetyCatch (err) { | |
| 20 | if (isActuallyUncaught(err)) { | |
| 21 | throwErrorNT(err) | |
| 22 | throw err | |
| 23 | } | |
| 24 | } |
| … | … | @@ -0,0 +1,18 @@ |
| 1 | { | |
| 2 | "name": "safety-catch", | |
| 3 | "version": "1.0.3", | |
| 4 | "description": "Small module that makes sure your catch, caught an actual error and not a programming mistake or assertion", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": {}, | |
| 7 | "devDependencies": {}, | |
| 8 | "repository": { | |
| 9 | "type": "git", | |
| 10 | "url": "https://github.com/mafintosh/safety-catch.git" | |
| 11 | }, | |
| 12 | "author": "Mathias Buus (@mafintosh)", | |
| 13 | "license": "MIT", | |
| 14 | "bugs": { | |
| 15 | "url": "https://github.com/mafintosh/safety-catch/issues" | |
| 16 | }, | |
| 17 | "homepage": "https://github.com/mafintosh/safety-catch" | |
| 18 | } |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | name: Build Status | |
| 2 | on: | |
| 3 | push: | |
| 4 | branches: | |
| 5 | - master | |
| 6 | pull_request: | |
| 7 | branches: | |
| 8 | - master | |
| 9 | jobs: | |
| 10 | build: | |
| 11 | strategy: | |
| 12 | matrix: | |
| 13 | node-version: [lts/*] | |
| 14 | os: [ubuntu-latest, macos-latest, windows-latest] | |
| 15 | runs-on: ${{ matrix.os }} | |
| 16 | steps: | |
| 17 | - uses: actions/checkout@v2 | |
| 18 | - name: Use Node.js ${{ matrix.node-version }} | |
| 19 | uses: actions/setup-node@v2 | |
| 20 | with: | |
| 21 | node-version: ${{ matrix.node-version }} | |
| 22 | - run: npm install | |
| 23 | - run: npm test |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2022 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | # same-data | |
| 2 | ||
| 3 | Deep equal with no deps and only for "data" objects, ie basic objects, arrays, primitives and typed arrays | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install same-data | |
| 7 | ``` | |
| 8 | ||
| 9 | Designed to be simple and for data. Does NOT do anything magic for functions, symbols, recursive objects | |
| 10 | ||
| 11 | ## Usage | |
| 12 | ||
| 13 | ``` js | |
| 14 | const sameData = require('same-data') | |
| 15 | ||
| 16 | console.log(sameData(1, 1)) // true | |
| 17 | console.log(sameData(1, 2)) // false | |
| 18 | console.log(sameData({ foo: 1 }, { foo: 1 })) // true | |
| 19 | console.log(sameData({ foo: 1 }, { foo: 1, bar: true })) // false | |
| 20 | console.log(sameData({ foo: 1, nested: { a: 1 } }, { foo: 1, nested: { a: 1 } })) // true | |
| 21 | console.log(sameData([{ a: 1 }, { b: 1 }], [{ a: 1 }, { b: 1 }])) // true | |
| 22 | ``` | |
| 23 | ||
| 24 | ## License | |
| 25 | ||
| 26 | MIT |
| … | … | @@ -0,0 +1,58 @@ |
| 1 | module.exports = sameData | |
| 2 | ||
| 3 | function type (o) { | |
| 4 | const t = typeof o | |
| 5 | ||
| 6 | return t === 'object' | |
| 7 | ? Array.isArray(o) | |
| 8 | ? 'array' | |
| 9 | : isTypedArray(o) | |
| 10 | ? (typeof o.equals === 'function') ? 'buffer' : 'array' | |
| 11 | : (o === null ? 'null' : 'object') | |
| 12 | : t | |
| 13 | } | |
| 14 | ||
| 15 | function isTypedArray (a) { | |
| 16 | return !!a && typeof a.length === 'number' && ArrayBuffer.isView(a.array) | |
| 17 | } | |
| 18 | ||
| 19 | function sameData (a, b) { | |
| 20 | if (a === b) return true | |
| 21 | ||
| 22 | const ta = type(a) | |
| 23 | const tb = type(b) | |
| 24 | ||
| 25 | if (ta !== tb) return false | |
| 26 | ||
| 27 | if (ta === 'buffer') return a.equals(b) | |
| 28 | ||
| 29 | if (ta === 'array') { | |
| 30 | if (a.length !== b.length) return false | |
| 31 | ||
| 32 | for (let i = 0; i < a.length; i++) { | |
| 33 | if (!sameData(a[i], b[i])) return false | |
| 34 | } | |
| 35 | ||
| 36 | return true | |
| 37 | } | |
| 38 | ||
| 39 | if (ta !== 'object') return false | |
| 40 | ||
| 41 | const ea = Object.entries(a) | |
| 42 | const eb = Object.entries(b) | |
| 43 | ||
| 44 | if (ea.length !== eb.length) return false | |
| 45 | ||
| 46 | ea.sort(cmp) | |
| 47 | eb.sort(cmp) | |
| 48 | ||
| 49 | for (let i = 0; i < ea.length; i++) { | |
| 50 | if (ea[i][0] !== eb[i][0] || !sameData(ea[i][1], eb[i][1])) return false | |
| 51 | } | |
| 52 | ||
| 53 | return true | |
| 54 | } | |
| 55 | ||
| 56 | function cmp (a, b) { | |
| 57 | return a[0] === b[0] ? 0 : a[0] < b[0] ? -1 : 1 | |
| 58 | } |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | { | |
| 2 | "name": "same-data", | |
| 3 | "version": "1.0.0", | |
| 4 | "description": "Deep equal with no deps and only for 'data' objects, ie basic objects, arrays, primitives and typed arrays", | |
| 5 | "main": "index.js", | |
| 6 | "scripts": { | |
| 7 | "test": "standard && brittle test.js" | |
| 8 | }, | |
| 9 | "devDependencies": { | |
| 10 | "brittle": "^3.1.1", | |
| 11 | "standard": "^17.0.0" | |
| 12 | }, | |
| 13 | "repository": { | |
| 14 | "type": "git", | |
| 15 | "url": "https://github.com/mafintosh/same-data.git" | |
| 16 | }, | |
| 17 | "author": "Mathias Buus (@mafintosh)", | |
| 18 | "license": "MIT", | |
| 19 | "bugs": { | |
| 20 | "url": "https://github.com/mafintosh/same-data/issues" | |
| 21 | }, | |
| 22 | "homepage": "https://github.com/mafintosh/same-data" | |
| 23 | } |
| … | … | @@ -0,0 +1,28 @@ |
| 1 | const test = require('brittle') | |
| 2 | const sameData = require('./') | |
| 3 | ||
| 4 | test('basic', function (t) { | |
| 5 | t.is(sameData(1, 1), true) | |
| 6 | t.is(sameData(1, 2), false) | |
| 7 | t.is(sameData(), true) // undef undef | |
| 8 | t.is(sameData(true, true), true) | |
| 9 | t.is(sameData('true', true), false) | |
| 10 | t.is(sameData('true', 'true'), true) | |
| 11 | }) | |
| 12 | ||
| 13 | test('objects', function (t) { | |
| 14 | t.is(sameData({ foo: 1 }, { foo: 1 }), true) | |
| 15 | t.is(sameData({ foo: 1 }, { foo: 1, bar: true }), false) | |
| 16 | t.is(sameData({ foo: 1, nested: { a: 1 } }, { foo: 1, nested: { a: 1 } }), true) | |
| 17 | t.is(sameData([{ a: 1 }, { b: 1 }], [{ a: 1 }, { b: 1 }]), true) | |
| 18 | }) | |
| 19 | ||
| 20 | test('typed arrays', function (t) { | |
| 21 | t.is(sameData(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 3])), true) | |
| 22 | t.is(sameData(new Uint8Array([1, 2, 1]), new Uint8Array([1, 2, 3])), false) | |
| 23 | }) | |
| 24 | ||
| 25 | test('buffers', function (t) { | |
| 26 | t.is(sameData(Buffer.from([1, 2, 3]), Buffer.from([1, 2, 3])), true) | |
| 27 | t.is(sameData(Buffer.from([1, 2, 1]), Buffer.from([1, 2, 3])), false) | |
| 28 | }) |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2020 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,45 @@ |
| 1 | # signal-promise | |
| 2 | ||
| 3 | Simple wait/notify promise with optional max wait time | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install signal-promise | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const Signal = require('signal-promise') | |
| 13 | ||
| 14 | const input = new Signal() | |
| 15 | ||
| 16 | while (await input.wait()) { | |
| 17 | console.log('someone typed!') | |
| 18 | } | |
| 19 | ||
| 20 | process.stdin.on('stdin', function () { | |
| 21 | input.notify() | |
| 22 | }) | |
| 23 | ``` | |
| 24 | ||
| 25 | ## API | |
| 26 | ||
| 27 | #### `s = new Signal()` | |
| 28 | ||
| 29 | Make a new signal | |
| 30 | ||
| 31 | #### `await s.wait([maxWaitTime])` | |
| 32 | ||
| 33 | Wait for someone to call notify. If you specify `maxWaitTime` | |
| 34 | the promise will resolve after `maxWaitTime` ms if no notify call has happened. | |
| 35 | ||
| 36 | As a convenience the promise resolves to `true` so it's easy to use it as a condition. | |
| 37 | ||
| 38 | #### `s.notify([error])` | |
| 39 | ||
| 40 | Notify everyone waiting. If you pass an error the wait promise | |
| 41 | will reject with that error. | |
| 42 | ||
| 43 | ## License | |
| 44 | ||
| 45 | MIT |
| … | … | @@ -0,0 +1,16 @@ |
| 1 | const Signal = require('./') | |
| 2 | ||
| 3 | const s = new Signal() | |
| 4 | ||
| 5 | main().catch(console.error) | |
| 6 | ||
| 7 | async function main () { | |
| 8 | while (await s.wait(2000)) { | |
| 9 | console.log('nu!') | |
| 10 | } | |
| 11 | console.log('efter') | |
| 12 | } | |
| 13 | ||
| 14 | process.stdin.on('data', function () { | |
| 15 | s.notify() | |
| 16 | }) |
| … | … | @@ -0,0 +1,57 @@ |
| 1 | module.exports = class Signal { | |
| 2 | constructor () { | |
| 3 | this._resolve = null | |
| 4 | this._reject = null | |
| 5 | this._promise = null | |
| 6 | this._bind = bind.bind(this) | |
| 7 | this._onerror = clear.bind(this) | |
| 8 | this._onsuccess = clear.bind(this, null) | |
| 9 | this._timers = new Set() | |
| 10 | } | |
| 11 | ||
| 12 | wait (max) { | |
| 13 | if (!this._promise) { | |
| 14 | this._promise = new Promise(this._bind) | |
| 15 | this._promise.then(this._onsuccess).catch(this._onerror) | |
| 16 | } | |
| 17 | if (max) return this._sleep(max) | |
| 18 | return this._promise | |
| 19 | } | |
| 20 | ||
| 21 | _sleep (max) { | |
| 22 | const s = new Promise((resolve, reject) => { | |
| 23 | const done = () => { | |
| 24 | this._timers.delete(state) | |
| 25 | resolve(true) | |
| 26 | } | |
| 27 | const id = setTimeout(done, max) | |
| 28 | const state = { id, resolve, reject } | |
| 29 | this._timers.add(state) | |
| 30 | }) | |
| 31 | ||
| 32 | return s | |
| 33 | } | |
| 34 | ||
| 35 | notify (err) { | |
| 36 | if (!this._promise) return | |
| 37 | const resolve = this._resolve | |
| 38 | const reject = this._reject | |
| 39 | this._promise = null | |
| 40 | if (err) reject(err) | |
| 41 | else resolve(true) | |
| 42 | } | |
| 43 | } | |
| 44 | ||
| 45 | function clear (err) { | |
| 46 | for (const { id, resolve, reject } of this._timers) { | |
| 47 | clearTimeout(id) | |
| 48 | if (err) reject(err) | |
| 49 | else resolve(true) | |
| 50 | } | |
| 51 | this._timers.clear() | |
| 52 | } | |
| 53 | ||
| 54 | function bind (resolve, reject) { | |
| 55 | this._resolve = resolve | |
| 56 | this._reject = reject | |
| 57 | } |
| … | … | @@ -0,0 +1,18 @@ |
| 1 | { | |
| 2 | "name": "signal-promise", | |
| 3 | "version": "1.0.3", | |
| 4 | "description": "Simple wait/notify promise with optional max wait time", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": {}, | |
| 7 | "devDependencies": {}, | |
| 8 | "repository": { | |
| 9 | "type": "git", | |
| 10 | "url": "https://github.com/mafintosh/signal-promise.git" | |
| 11 | }, | |
| 12 | "author": "Mathias Buus (@mafintosh)", | |
| 13 | "license": "MIT", | |
| 14 | "bugs": { | |
| 15 | "url": "https://github.com/mafintosh/signal-promise/issues" | |
| 16 | }, | |
| 17 | "homepage": "https://github.com/mafintosh/signal-promise" | |
| 18 | } |
| … | … | @@ -0,0 +1,22 @@ |
| 1 | Copyright (c) 2014 Dominic Tarr | |
| 2 | ||
| 3 | Permission is hereby granted, free of charge, | |
| 4 | to any person obtaining a copy of this software and | |
| 5 | associated documentation files (the "Software"), to | |
| 6 | deal in the Software without restriction, including | |
| 7 | without limitation the rights to use, copy, modify, | |
| 8 | merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom | |
| 10 | the Software is furnished to do so, | |
| 11 | subject to the following conditions: | |
| 12 | ||
| 13 | The above copyright notice and this permission notice | |
| 14 | shall be included in all copies or substantial portions of the Software. | |
| 15 | ||
| 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
| 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
| 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR | |
| 20 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
| 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
| 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| … | … | @@ -0,0 +1,28 @@ |
| 1 | # signed-varint | |
| 2 | ||
| 3 | store signed integers efficiently, as per protocol-buffers. | |
| 4 | ||
| 5 | For unsigned integers use | |
| 6 | [varint](https://github.com/chrisdickinson/varint). | |
| 7 | ||
| 8 | Integers are mapped to positive integers, so that positive integers | |
| 9 | become positive even numbers (n*2) | |
| 10 | and negative integers become positive odd numbers. (n*-2 - 1) | |
| 11 | ||
| 12 | This is the same as moving the sign bit from the most significant | |
| 13 | possition to the least significant. Otherwise, varint will encode | |
| 14 | negative numbers as large integers. | |
| 15 | ||
| 16 | ``` js | |
| 17 | var varint = require('varint') | |
| 18 | var svarint = require('signed-varint') | |
| 19 | ||
| 20 | console.log('unsigned', varint.encode(-1)) | |
| 21 | console.log('signed', svarint.encode(-1)) | |
| 22 | ||
| 23 | //=> unsigned [255,255,255, 15] | |
| 24 | // signed [1] | |
| 25 | ``` | |
| 26 | ## License | |
| 27 | ||
| 28 | MIT |
| … | … | @@ -0,0 +1,16 @@ |
| 1 | var varint = require('varint') | |
| 2 | exports.encode = function encode (v, b, o) { | |
| 3 | v = v >= 0 ? v*2 : v*-2 - 1 | |
| 4 | var r = varint.encode(v, b, o) | |
| 5 | encode.bytes = varint.encode.bytes | |
| 6 | return r | |
| 7 | } | |
| 8 | exports.decode = function decode (b, o) { | |
| 9 | var v = varint.decode(b, o) | |
| 10 | decode.bytes = varint.decode.bytes | |
| 11 | return v & 1 ? (v+1) / -2 : v / 2 | |
| 12 | } | |
| 13 | ||
| 14 | exports.encodingLength = function (v) { | |
| 15 | return varint.encodingLength(v >= 0 ? v*2 : v*-2 - 1) | |
| 16 | } |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | { | |
| 2 | "name": "signed-varint", | |
| 3 | "description": "efficiently store signed integers in varint", | |
| 4 | "version": "2.0.1", | |
| 5 | "homepage": "https://github.com/dominictarr/signed-varint", | |
| 6 | "repository": { | |
| 7 | "type": "git", | |
| 8 | "url": "git://github.com/dominictarr/signed-varint.git" | |
| 9 | }, | |
| 10 | "dependencies": { | |
| 11 | "varint": "~5.0.0" | |
| 12 | }, | |
| 13 | "devDependencies": { | |
| 14 | "tape": "~2.12.3" | |
| 15 | }, | |
| 16 | "scripts": { | |
| 17 | "test": "node test.js" | |
| 18 | }, | |
| 19 | "author": "Dominic Tarr <dominic.tarr@gmail.com> (http://dominictarr.com)", | |
| 20 | "license": "MIT" | |
| 21 | } |
| … | … | @@ -0,0 +1,49 @@ |
| 1 | var tape = require('tape') | |
| 2 | ||
| 3 | var svi = require('./') | |
| 4 | ||
| 5 | function encodeDecode (t, v, bytes) { | |
| 6 | var b = svi.encode(v) | |
| 7 | t.equal(b.length, bytes) | |
| 8 | t.equal(svi.decode(b), v) | |
| 9 | t.equal(svi.encode.bytes, bytes) | |
| 10 | t.equal(svi.decode.bytes, bytes) | |
| 11 | } | |
| 12 | ||
| 13 | tape('single byte', function (t) { | |
| 14 | encodeDecode(t, 1, 1) | |
| 15 | encodeDecode(t, -1, 1) | |
| 16 | encodeDecode(t, 63, 1) | |
| 17 | encodeDecode(t, -64, 1) | |
| 18 | t.end() | |
| 19 | }) | |
| 20 | tape('double byte', function (t) { | |
| 21 | encodeDecode(t, 64, 2) | |
| 22 | encodeDecode(t, -65, 2) | |
| 23 | encodeDecode(t, 127, 2) | |
| 24 | encodeDecode(t, -128, 2) | |
| 25 | encodeDecode(t, 128, 2) | |
| 26 | encodeDecode(t, -129, 2) | |
| 27 | encodeDecode(t, 255, 2) | |
| 28 | encodeDecode(t, -256, 2) | |
| 29 | t.end() | |
| 30 | }) | |
| 31 | tape('tripple', function (t) { | |
| 32 | encodeDecode(t, 0x4000, 3) | |
| 33 | encodeDecode(t, -0x4001, 3) | |
| 34 | encodeDecode(t, 1048574, 3) | |
| 35 | encodeDecode(t, -1048575, 3) | |
| 36 | t.end() | |
| 37 | }) | |
| 38 | ||
| 39 | tape('quad', function (t) { | |
| 40 | encodeDecode(t, 134217726, 4) | |
| 41 | encodeDecode(t, -134217727, 4) | |
| 42 | t.end() | |
| 43 | }) | |
| 44 | ||
| 45 | tape('large int', function (t) { | |
| 46 | encodeDecode(t, 0x80000000000, 7) | |
| 47 | encodeDecode(t, -0x80000000000, 7) | |
| 48 | t.end() | |
| 49 | }) |
| … | … | @@ -0,0 +1,64 @@ |
| 1 | cmake_minimum_required(VERSION 3.25) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) | |
| 5 | find_package(cmake-napi REQUIRED PATHS node_modules/cmake-napi) | |
| 6 | ||
| 7 | project(simdle_native C CXX) | |
| 8 | ||
| 9 | bare_target(target) | |
| 10 | ||
| 11 | if(target MATCHES "win32") | |
| 12 | add_compile_options(/MT$<$<CONFIG:Debug>:d>) | |
| 13 | endif() | |
| 14 | ||
| 15 | fetch_package("github:holepunchto/libsimdle#014ad4e") | |
| 16 | fetch_package("github:holepunchto/libjstl#098664c") | |
| 17 | ||
| 18 | add_bare_module(simdle_native_bare) | |
| 19 | ||
| 20 | target_sources( | |
| 21 | ${simdle_native_bare} | |
| 22 | PRIVATE | |
| 23 | binding.cc | |
| 24 | ) | |
| 25 | ||
| 26 | target_link_libraries( | |
| 27 | ${simdle_native_bare} | |
| 28 | PRIVATE | |
| 29 | $<TARGET_OBJECTS:simdle> | |
| 30 | jstl | |
| 31 | PUBLIC | |
| 32 | simdle | |
| 33 | ) | |
| 34 | ||
| 35 | add_napi_module(simdle_native_node) | |
| 36 | ||
| 37 | target_sources( | |
| 38 | ${simdle_native_node} | |
| 39 | PRIVATE | |
| 40 | binding.cc | |
| 41 | ) | |
| 42 | ||
| 43 | target_link_libraries( | |
| 44 | ${simdle_native_node} | |
| 45 | PRIVATE | |
| 46 | $<TARGET_OBJECTS:simdle> | |
| 47 | jstl | |
| 48 | PUBLIC | |
| 49 | simdle | |
| 50 | ) | |
| 51 | ||
| 52 | target_compile_definitions( | |
| 53 | ${simdle_native_node} | |
| 54 | PRIVATE | |
| 55 | NAPI_VERSION=9 | |
| 56 | ) | |
| 57 | ||
| 58 | resolve_node_module(bare-compat-napi compat) | |
| 59 | ||
| 60 | target_include_directories( | |
| 61 | ${simdle_native_node} | |
| 62 | PRIVATE | |
| 63 | "${compat}/include" | |
| 64 | ) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,76 @@ |
| 1 | # simdle-native | |
| 2 | ||
| 3 | https://github.com/holepunchto/libsimdle JavaScript bindings for Node.js. | |
| 4 | ||
| 5 | ```sh | |
| 6 | npm install simdle-native | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const { cnt } = require('simdle-native') | |
| 13 | ||
| 14 | cnt(Buffer.alloc(16, 0xff)) | |
| 15 | // <Buffer 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08> | |
| 16 | ``` | |
| 17 | ||
| 18 | ## API | |
| 19 | ||
| 20 | The lane width of all APIs is determined by the input buffer. That is, if providing a `Uint8Array` the lane width will be 8 bits, if providing a `Uint16Array` the lane width will be 16 bits and so on. | |
| 21 | ||
| 22 | #### `const result = allo(buffer)` | |
| 23 | ||
| 24 | Check if the buffer contains only ones. | |
| 25 | ||
| 26 | #### `const result = allz(buffer)` | |
| 27 | ||
| 28 | Check if the buffer contains only zeros. | |
| 29 | ||
| 30 | #### `const result = and(a, b[, result])` | |
| 31 | ||
| 32 | Compute the bitwise AND of `a` and `b`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 33 | ||
| 34 | #### `const result = clear(a, b[, result])` | |
| 35 | ||
| 36 | Clear the bits in `b` from `a`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 37 | ||
| 38 | #### `const result = clo(buffer[, result])` | |
| 39 | ||
| 40 | Count the number of leading ones in `buffer`, storing the result in `result`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 41 | ||
| 42 | #### `const result = clz(buffer[, result])` | |
| 43 | ||
| 44 | Count the number of leading zeroes in `buffer`, storing the result in `result`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 45 | ||
| 46 | #### `const result = cnt(buffer[, result])` | |
| 47 | ||
| 48 | Compute the population count in `buffer`, storing the result in `result`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 49 | ||
| 50 | #### `const result = cto(buffer[, result])` | |
| 51 | ||
| 52 | Count the number of trailing ones in `buffer`, storing the result in `result`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 53 | ||
| 54 | #### `const result = ctz(buffer[, result])` | |
| 55 | ||
| 56 | Count the number of trailing zeroes in `buffer`, storing the result in `result`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 57 | ||
| 58 | #### `const result = not(buffer[, result])` | |
| 59 | ||
| 60 | Compute the bitwise NOT in `buffer`, storing the result in `result`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 61 | ||
| 62 | #### `const result = or(a, b[, result])` | |
| 63 | ||
| 64 | Compute the bitwise OR of `a` and `b`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 65 | ||
| 66 | #### `const result = sum(buffer)` | |
| 67 | ||
| 68 | Compute the sum of `buffer` as a `bigint`. | |
| 69 | ||
| 70 | #### `const result = xor(a, b[, result])` | |
| 71 | ||
| 72 | Compute the bitwise XOR of `a` and `b`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 73 | ||
| 74 | ## License | |
| 75 | ||
| 76 | Apache-2.0 |
| … | … | @@ -0,0 +1,219 @@ |
| 1 | #include <bare.h> | |
| 2 | #include <js.h> | |
| 3 | #include <jstl.h> | |
| 4 | #include <simdle.h> | |
| 5 | #include <stdint.h> | |
| 6 | ||
| 7 | template <auto fn, typename T> | |
| 8 | struct simdle_native_op_t { | |
| 9 | static bool | |
| 10 | predicate( | |
| 11 | js_env_t *env, | |
| 12 | js_receiver_t, | |
| 13 | js_typedarray_t<T> buf | |
| 14 | ) { | |
| 15 | int err; | |
| 16 | ||
| 17 | simdle_v128_t *buf_data; | |
| 18 | size_t buf_len; | |
| 19 | err = js_get_typedarray_info(env, buf, reinterpret_cast<T *&>(buf_data), buf_len); | |
| 20 | assert(err == 0); | |
| 21 | ||
| 22 | for (size_t i = 0, n = buf_len * sizeof(T) / 16; i < n; i++) { | |
| 23 | if (!fn(buf_data[i])) return false; | |
| 24 | } | |
| 25 | ||
| 26 | return true; | |
| 27 | } | |
| 28 | ||
| 29 | static void | |
| 30 | unary( | |
| 31 | js_env_t *env, | |
| 32 | js_receiver_t, | |
| 33 | js_typedarray_t<T> buf, | |
| 34 | js_typedarray_t<T> result | |
| 35 | ) { | |
| 36 | int err; | |
| 37 | ||
| 38 | simdle_v128_t *buf_data; | |
| 39 | size_t buf_len; | |
| 40 | err = js_get_typedarray_info(env, buf, reinterpret_cast<T *&>(buf_data), buf_len); | |
| 41 | assert(err == 0); | |
| 42 | ||
| 43 | simdle_v128_t *result_data; | |
| 44 | size_t result_len; | |
| 45 | err = js_get_typedarray_info(env, result, reinterpret_cast<T *&>(result_data), result_len); | |
| 46 | assert(err == 0); | |
| 47 | ||
| 48 | for (size_t i = 0, n = buf_len * sizeof(T) / 16; i < n; i++) { | |
| 49 | result_data[i] = fn(buf_data[i]); | |
| 50 | } | |
| 51 | } | |
| 52 | ||
| 53 | static void | |
| 54 | binary( | |
| 55 | js_env_t *env, | |
| 56 | js_receiver_t, | |
| 57 | js_typedarray_t<T> a_buf, | |
| 58 | js_typedarray_t<T> b_buf, | |
| 59 | js_typedarray_t<T> result_buf | |
| 60 | ) { | |
| 61 | int err; | |
| 62 | ||
| 63 | size_t len; | |
| 64 | ||
| 65 | simdle_v128_t *a; | |
| 66 | err = js_get_typedarray_info(env, a_buf, reinterpret_cast<T *&>(a), len); | |
| 67 | assert(err == 0); | |
| 68 | ||
| 69 | simdle_v128_t *b; | |
| 70 | err = js_get_typedarray_info(env, b_buf, reinterpret_cast<T *&>(b), len); | |
| 71 | assert(err == 0); | |
| 72 | ||
| 73 | simdle_v128_t *result; | |
| 74 | err = js_get_typedarray_info(env, result_buf, reinterpret_cast<T *&>(result), len); | |
| 75 | assert(err == 0); | |
| 76 | ||
| 77 | for (size_t i = 0, n = len * sizeof(T) / 16; i < n; i++) { | |
| 78 | result[i] = fn(a[i], b[i]); | |
| 79 | } | |
| 80 | } | |
| 81 | ||
| 82 | static int64_t | |
| 83 | reduce( | |
| 84 | js_env_t *env, | |
| 85 | js_receiver_t, | |
| 86 | js_typedarray_t<T> buf | |
| 87 | ) { | |
| 88 | simdle_v128_t *buf_data; | |
| 89 | size_t buf_len; | |
| 90 | int err = js_get_typedarray_info(env, buf, reinterpret_cast<T *&>(buf_data), buf_len); | |
| 91 | assert(err == 0); | |
| 92 | ||
| 93 | uint64_t result = 0; | |
| 94 | ||
| 95 | for (size_t i = 0, n = buf_len * sizeof(T) / 16; i < n; i++) { | |
| 96 | result += fn(buf_data[i]); | |
| 97 | } | |
| 98 | ||
| 99 | return result; | |
| 100 | } | |
| 101 | }; | |
| 102 | ||
| 103 | constexpr auto simdle_native_allo_v128_u8 = simdle_native_op_t<simdle_allo_v128, uint8_t>::predicate; | |
| 104 | constexpr auto simdle_native_allo_v128_u16 = simdle_native_op_t<simdle_allo_v128, uint16_t>::predicate; | |
| 105 | constexpr auto simdle_native_allo_v128_u32 = simdle_native_op_t<simdle_allo_v128, uint32_t>::predicate; | |
| 106 | ||
| 107 | constexpr auto simdle_native_allz_v128_u8 = simdle_native_op_t<simdle_allz_v128, uint8_t>::predicate; | |
| 108 | constexpr auto simdle_native_allz_v128_u16 = simdle_native_op_t<simdle_allz_v128, uint16_t>::predicate; | |
| 109 | constexpr auto simdle_native_allz_v128_u32 = simdle_native_op_t<simdle_allz_v128, uint32_t>::predicate; | |
| 110 | ||
| 111 | constexpr auto simdle_native_cnt_v128_u8 = simdle_native_op_t<simdle_cnt_v128_u8, uint8_t>::unary; | |
| 112 | constexpr auto simdle_native_cnt_v128_u16 = simdle_native_op_t<simdle_cnt_v128_u16, uint16_t>::unary; | |
| 113 | constexpr auto simdle_native_cnt_v128_u32 = simdle_native_op_t<simdle_cnt_v128_u32, uint32_t>::unary; | |
| 114 | ||
| 115 | constexpr auto simdle_native_clo_v128_u8 = simdle_native_op_t<simdle_clo_v128_u8, uint8_t>::unary; | |
| 116 | constexpr auto simdle_native_clo_v128_u16 = simdle_native_op_t<simdle_clo_v128_u16, uint16_t>::unary; | |
| 117 | constexpr auto simdle_native_clo_v128_u32 = simdle_native_op_t<simdle_clo_v128_u32, uint32_t>::unary; | |
| 118 | ||
| 119 | constexpr auto simdle_native_clz_v128_u8 = simdle_native_op_t<simdle_clz_v128_u8, uint8_t>::unary; | |
| 120 | constexpr auto simdle_native_clz_v128_u16 = simdle_native_op_t<simdle_clz_v128_u16, uint16_t>::unary; | |
| 121 | constexpr auto simdle_native_clz_v128_u32 = simdle_native_op_t<simdle_clz_v128_u32, uint32_t>::unary; | |
| 122 | ||
| 123 | constexpr auto simdle_native_cto_v128_u8 = simdle_native_op_t<simdle_cto_v128_u8, uint8_t>::unary; | |
| 124 | constexpr auto simdle_native_cto_v128_u16 = simdle_native_op_t<simdle_cto_v128_u16, uint16_t>::unary; | |
| 125 | constexpr auto simdle_native_cto_v128_u32 = simdle_native_op_t<simdle_cto_v128_u32, uint32_t>::unary; | |
| 126 | ||
| 127 | constexpr auto simdle_native_ctz_v128_u8 = simdle_native_op_t<simdle_ctz_v128_u8, uint8_t>::unary; | |
| 128 | constexpr auto simdle_native_ctz_v128_u16 = simdle_native_op_t<simdle_ctz_v128_u16, uint16_t>::unary; | |
| 129 | constexpr auto simdle_native_ctz_v128_u32 = simdle_native_op_t<simdle_ctz_v128_u32, uint32_t>::unary; | |
| 130 | ||
| 131 | constexpr auto simdle_native_not_v128_u8 = simdle_native_op_t<simdle_not_v128_u8, uint8_t>::unary; | |
| 132 | constexpr auto simdle_native_not_v128_u16 = simdle_native_op_t<simdle_not_v128_u16, uint16_t>::unary; | |
| 133 | constexpr auto simdle_native_not_v128_u32 = simdle_native_op_t<simdle_not_v128_u32, uint32_t>::unary; | |
| 134 | ||
| 135 | constexpr auto simdle_native_and_v128_u8 = simdle_native_op_t<simdle_and_v128_u8, uint8_t>::binary; | |
| 136 | constexpr auto simdle_native_and_v128_u16 = simdle_native_op_t<simdle_and_v128_u16, uint16_t>::binary; | |
| 137 | constexpr auto simdle_native_and_v128_u32 = simdle_native_op_t<simdle_and_v128_u32, uint32_t>::binary; | |
| 138 | ||
| 139 | constexpr auto simdle_native_clear_v128_u8 = simdle_native_op_t<simdle_clear_v128_u8, uint8_t>::binary; | |
| 140 | constexpr auto simdle_native_clear_v128_u16 = simdle_native_op_t<simdle_clear_v128_u16, uint16_t>::binary; | |
| 141 | constexpr auto simdle_native_clear_v128_u32 = simdle_native_op_t<simdle_clear_v128_u32, uint32_t>::binary; | |
| 142 | ||
| 143 | constexpr auto simdle_native_or_v128_u8 = simdle_native_op_t<simdle_or_v128_u8, uint8_t>::binary; | |
| 144 | constexpr auto simdle_native_or_v128_u16 = simdle_native_op_t<simdle_or_v128_u16, uint16_t>::binary; | |
| 145 | constexpr auto simdle_native_or_v128_u32 = simdle_native_op_t<simdle_or_v128_u32, uint32_t>::binary; | |
| 146 | ||
| 147 | constexpr auto simdle_native_xor_v128_u8 = simdle_native_op_t<simdle_xor_v128_u8, uint8_t>::binary; | |
| 148 | constexpr auto simdle_native_xor_v128_u16 = simdle_native_op_t<simdle_xor_v128_u16, uint16_t>::binary; | |
| 149 | constexpr auto simdle_native_xor_v128_u32 = simdle_native_op_t<simdle_xor_v128_u32, uint32_t>::binary; | |
| 150 | ||
| 151 | constexpr auto simdle_native_sum_v128_u8 = simdle_native_op_t<simdle_sum_v128_u8, uint8_t>::reduce; | |
| 152 | constexpr auto simdle_native_sum_v128_u16 = simdle_native_op_t<simdle_sum_v128_u16, uint16_t>::reduce; | |
| 153 | constexpr auto simdle_native_sum_v128_u32 = simdle_native_op_t<simdle_sum_v128_u32, uint32_t>::reduce; | |
| 154 | ||
| 155 | static js_value_t * | |
| 156 | simdle_native_exports(js_env_t *env, js_value_t *exports) { | |
| 157 | int err; | |
| 158 | ||
| 159 | #define V(fn) \ | |
| 160 | err = js_set_property<fn>(env, exports, #fn); \ | |
| 161 | assert(err == 0); | |
| 162 | ||
| 163 | V(simdle_native_allo_v128_u8); | |
| 164 | V(simdle_native_allo_v128_u16); | |
| 165 | V(simdle_native_allo_v128_u32); | |
| 166 | ||
| 167 | V(simdle_native_allz_v128_u8); | |
| 168 | V(simdle_native_allz_v128_u16); | |
| 169 | V(simdle_native_allz_v128_u32); | |
| 170 | ||
| 171 | V(simdle_native_cnt_v128_u8); | |
| 172 | V(simdle_native_cnt_v128_u16); | |
| 173 | V(simdle_native_cnt_v128_u32); | |
| 174 | ||
| 175 | V(simdle_native_clz_v128_u8); | |
| 176 | V(simdle_native_clz_v128_u16); | |
| 177 | V(simdle_native_clz_v128_u32); | |
| 178 | ||
| 179 | V(simdle_native_and_v128_u8); | |
| 180 | V(simdle_native_and_v128_u16); | |
| 181 | V(simdle_native_and_v128_u32); | |
| 182 | ||
| 183 | V(simdle_native_clear_v128_u8); | |
| 184 | V(simdle_native_clear_v128_u16); | |
| 185 | V(simdle_native_clear_v128_u32); | |
| 186 | ||
| 187 | V(simdle_native_clo_v128_u8); | |
| 188 | V(simdle_native_clo_v128_u16); | |
| 189 | V(simdle_native_clo_v128_u32); | |
| 190 | ||
| 191 | V(simdle_native_cto_v128_u8); | |
| 192 | V(simdle_native_cto_v128_u16); | |
| 193 | V(simdle_native_cto_v128_u32); | |
| 194 | ||
| 195 | V(simdle_native_ctz_v128_u8); | |
| 196 | V(simdle_native_ctz_v128_u16); | |
| 197 | V(simdle_native_ctz_v128_u32); | |
| 198 | ||
| 199 | V(simdle_native_not_v128_u8); | |
| 200 | V(simdle_native_not_v128_u16); | |
| 201 | V(simdle_native_not_v128_u32); | |
| 202 | ||
| 203 | V(simdle_native_or_v128_u8); | |
| 204 | V(simdle_native_or_v128_u16); | |
| 205 | V(simdle_native_or_v128_u32); | |
| 206 | ||
| 207 | V(simdle_native_sum_v128_u8); | |
| 208 | V(simdle_native_sum_v128_u16); | |
| 209 | V(simdle_native_sum_v128_u32); | |
| 210 | ||
| 211 | V(simdle_native_xor_v128_u8); | |
| 212 | V(simdle_native_xor_v128_u16); | |
| 213 | V(simdle_native_xor_v128_u32); | |
| 214 | #undef V | |
| 215 | ||
| 216 | return exports; | |
| 217 | } | |
| 218 | ||
| 219 | BARE_MODULE(simdle_native, simdle_native_exports) |
| … | … | @@ -0,0 +1,3 @@ |
| 1 | require.addon = require('require-addon') | |
| 2 | ||
| 3 | module.exports = require.addon('.', __filename) |
| … | … | @@ -0,0 +1,148 @@ |
| 1 | const binding = require('./binding') | |
| 2 | const b4a = require('b4a') | |
| 3 | ||
| 4 | function predicate(u8, u16, u32) { | |
| 5 | return function predicate(buf) { | |
| 6 | if (buf.byteLength % 16 !== 0) { | |
| 7 | throw new Error('Buffer length must be a multiple of 16') | |
| 8 | } | |
| 9 | ||
| 10 | const n = buf.BYTES_PER_ELEMENT | |
| 11 | ||
| 12 | if (n === 1) return u8(buf) | |
| 13 | if (n === 2) return u16(buf) | |
| 14 | return u32(buf) | |
| 15 | } | |
| 16 | } | |
| 17 | ||
| 18 | function unary(u8, u16, u32) { | |
| 19 | return function unary(buf, result = b4a.allocUnsafe(buf.byteLength)) { | |
| 20 | if (buf.byteLength % 16 !== 0) { | |
| 21 | throw new Error('Buffer length must be a multiple of 16') | |
| 22 | } | |
| 23 | ||
| 24 | if (buf.byteLength !== result.byteLength) { | |
| 25 | throw new Error('Length of result buffer is insufficient') | |
| 26 | } | |
| 27 | ||
| 28 | const n = buf.BYTES_PER_ELEMENT | |
| 29 | ||
| 30 | if (n === 1) u8(buf, result) | |
| 31 | else if (n === 2) u16(buf, result) | |
| 32 | else u32(buf, result) | |
| 33 | ||
| 34 | return result | |
| 35 | } | |
| 36 | } | |
| 37 | ||
| 38 | function binary(u8, u16, u32) { | |
| 39 | return function binary(a, b, result = b4a.allocUnsafe(a.byteLength)) { | |
| 40 | if (a.byteLength % 16 !== 0) { | |
| 41 | throw new Error('Buffer length must be a multiple of 16') | |
| 42 | } | |
| 43 | ||
| 44 | if (a.byteLength !== b.byteLength || a.byteLength !== result.byteLength) { | |
| 45 | throw new Error('Buffers must be the same length') | |
| 46 | } | |
| 47 | ||
| 48 | const n = a.BYTES_PER_ELEMENT | |
| 49 | ||
| 50 | if (n === 1) u8(a, b, result) | |
| 51 | else if (n === 2) u16(a, b, result) | |
| 52 | else u32(a, b, result) | |
| 53 | ||
| 54 | return result | |
| 55 | } | |
| 56 | } | |
| 57 | ||
| 58 | function reduce(u8, u16, u32) { | |
| 59 | return function reduce(buf) { | |
| 60 | if (buf.byteLength % 16 !== 0) { | |
| 61 | throw new Error('Buffer length must be a multiple of 16') | |
| 62 | } | |
| 63 | ||
| 64 | const n = buf.BYTES_PER_ELEMENT | |
| 65 | ||
| 66 | if (n === 1) return u8(buf) | |
| 67 | if (n === 2) return u16(buf) | |
| 68 | return u32(buf) | |
| 69 | } | |
| 70 | } | |
| 71 | ||
| 72 | exports.allo = predicate( | |
| 73 | binding.simdle_native_allo_v128_u8, | |
| 74 | binding.simdle_native_allo_v128_u16, | |
| 75 | binding.simdle_native_allo_v128_u32 | |
| 76 | ) | |
| 77 | ||
| 78 | exports.allz = predicate( | |
| 79 | binding.simdle_native_allz_v128_u8, | |
| 80 | binding.simdle_native_allz_v128_u16, | |
| 81 | binding.simdle_native_allz_v128_u32 | |
| 82 | ) | |
| 83 | ||
| 84 | exports.and = binary( | |
| 85 | binding.simdle_native_and_v128_u8, | |
| 86 | binding.simdle_native_and_v128_u16, | |
| 87 | binding.simdle_native_and_v128_u32 | |
| 88 | ) | |
| 89 | ||
| 90 | exports.clear = binary( | |
| 91 | binding.simdle_native_clear_v128_u8, | |
| 92 | binding.simdle_native_clear_v128_u16, | |
| 93 | binding.simdle_native_clear_v128_u32 | |
| 94 | ) | |
| 95 | ||
| 96 | exports.clo = unary( | |
| 97 | binding.simdle_native_clo_v128_u8, | |
| 98 | binding.simdle_native_clo_v128_u16, | |
| 99 | binding.simdle_native_clo_v128_u32 | |
| 100 | ) | |
| 101 | ||
| 102 | exports.clz = unary( | |
| 103 | binding.simdle_native_clz_v128_u8, | |
| 104 | binding.simdle_native_clz_v128_u16, | |
| 105 | binding.simdle_native_clz_v128_u32 | |
| 106 | ) | |
| 107 | ||
| 108 | exports.cnt = unary( | |
| 109 | binding.simdle_native_cnt_v128_u8, | |
| 110 | binding.simdle_native_cnt_v128_u16, | |
| 111 | binding.simdle_native_cnt_v128_u32 | |
| 112 | ) | |
| 113 | ||
| 114 | exports.cto = unary( | |
| 115 | binding.simdle_native_cto_v128_u8, | |
| 116 | binding.simdle_native_cto_v128_u16, | |
| 117 | binding.simdle_native_cto_v128_u32 | |
| 118 | ) | |
| 119 | ||
| 120 | exports.ctz = unary( | |
| 121 | binding.simdle_native_ctz_v128_u8, | |
| 122 | binding.simdle_native_ctz_v128_u16, | |
| 123 | binding.simdle_native_ctz_v128_u32 | |
| 124 | ) | |
| 125 | ||
| 126 | exports.not = unary( | |
| 127 | binding.simdle_native_not_v128_u8, | |
| 128 | binding.simdle_native_not_v128_u16, | |
| 129 | binding.simdle_native_not_v128_u32 | |
| 130 | ) | |
| 131 | ||
| 132 | exports.or = binary( | |
| 133 | binding.simdle_native_or_v128_u8, | |
| 134 | binding.simdle_native_or_v128_u16, | |
| 135 | binding.simdle_native_or_v128_u32 | |
| 136 | ) | |
| 137 | ||
| 138 | exports.sum = reduce( | |
| 139 | binding.simdle_native_sum_v128_u8, | |
| 140 | binding.simdle_native_sum_v128_u16, | |
| 141 | binding.simdle_native_sum_v128_u32 | |
| 142 | ) | |
| 143 | ||
| 144 | exports.xor = binary( | |
| 145 | binding.simdle_native_xor_v128_u8, | |
| 146 | binding.simdle_native_xor_v128_u16, | |
| 147 | binding.simdle_native_xor_v128_u32 | |
| 148 | ) |
| … | … | @@ -0,0 +1,44 @@ |
| 1 | { | |
| 2 | "name": "simdle-native", | |
| 3 | "version": "1.3.9", | |
| 4 | "description": "libsimdle JavaScript bindings for Node.js", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js", | |
| 8 | "binding.cc", | |
| 9 | "binding.js", | |
| 10 | "CMakeLists.txt", | |
| 11 | "prebuilds" | |
| 12 | ], | |
| 13 | "addon": true, | |
| 14 | "scripts": { | |
| 15 | "format": "prettier --write .", | |
| 16 | "lint": "prettier --check .", | |
| 17 | "test": "npm run lint && npm run test:bare && npm run test:node", | |
| 18 | "test:bare": "bare test.mjs", | |
| 19 | "test:node": "node test.mjs" | |
| 20 | }, | |
| 21 | "repository": { | |
| 22 | "type": "git", | |
| 23 | "url": "git+https://github.com/holepunchto/simdle-native.git" | |
| 24 | }, | |
| 25 | "author": "Holepunch", | |
| 26 | "license": "Apache-2.0", | |
| 27 | "bugs": { | |
| 28 | "url": "https://github.com/holepunchto/simdle-native/issues" | |
| 29 | }, | |
| 30 | "homepage": "https://github.com/holepunchto/simdle-native#readme", | |
| 31 | "dependencies": { | |
| 32 | "b4a": "^1.6.0", | |
| 33 | "require-addon": "^1.1.0" | |
| 34 | }, | |
| 35 | "devDependencies": { | |
| 36 | "bare-compat-napi": "^1.3.2", | |
| 37 | "brittle": "^3.1.0", | |
| 38 | "cmake-bare": "^1.1.7", | |
| 39 | "cmake-fetch": "^1.1.0", | |
| 40 | "cmake-napi": "^1.0.2", | |
| 41 | "prettier": "^3.6.2", | |
| 42 | "prettier-config-holepunch": "^1.0.0" | |
| 43 | } | |
| 44 | } |
| … | … | @@ -0,0 +1,15 @@ |
| 1 | ISC License | |
| 2 | ||
| 3 | Copyright (c) 2022 Contributors | |
| 4 | ||
| 5 | Permission to use, copy, modify, and/or distribute this software for any | |
| 6 | purpose with or without fee is hereby granted, provided that the above | |
| 7 | copyright notice and this permission notice appear in all copies. | |
| 8 | ||
| 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | |
| 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | |
| 11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | |
| 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | |
| 13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | |
| 14 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
| 15 | PERFORMANCE OF THIS SOFTWARE. |
| … | … | @@ -0,0 +1,76 @@ |
| 1 | # simdle-universal | |
| 2 | ||
| 3 | Universal wrapper for https://github.com/holepunchto/libsimdle with a JavaScript fallback. | |
| 4 | ||
| 5 | ```sh | |
| 6 | npm install simdle-universal | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const { cnt } = require('simdle-universal') | |
| 13 | ||
| 14 | cnt(Buffer.alloc(16, 0xff)) | |
| 15 | // <Buffer 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08> | |
| 16 | ``` | |
| 17 | ||
| 18 | ## API | |
| 19 | ||
| 20 | The lane width of all APIs is determined by the input buffer. That is, if providing a `Uint8Array` the lane width will be 8 bits, if providing a `Uint16Array` the lane width will be 16 bits and so on. | |
| 21 | ||
| 22 | #### `const result = allo(buffer)` | |
| 23 | ||
| 24 | Check if the buffer contains only ones. | |
| 25 | ||
| 26 | #### `const result = allz(buffer)` | |
| 27 | ||
| 28 | Check if the buffer contains only zeros. | |
| 29 | ||
| 30 | #### `const result = and(a, b[, result])` | |
| 31 | ||
| 32 | Compute the bitwise AND of `a` and `b`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 33 | ||
| 34 | #### `const result = clear(a, b[, result])` | |
| 35 | ||
| 36 | Clear the bits in `b` from `a`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 37 | ||
| 38 | #### `const result = clo(buffer[, result])` | |
| 39 | ||
| 40 | Count the number of leading ones in `buffer`, storing the result in `result`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 41 | ||
| 42 | #### `const result = clz(buffer[, result])` | |
| 43 | ||
| 44 | Count the number of leading zeroes in `buffer`, storing the result in `result`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 45 | ||
| 46 | #### `const result = cnt(buffer[, result])` | |
| 47 | ||
| 48 | Compute the population count in `buffer`, storing the result in `result`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 49 | ||
| 50 | #### `const result = cto(buffer[, result])` | |
| 51 | ||
| 52 | Count the number of trailing ones in `buffer`, storing the result in `result`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 53 | ||
| 54 | #### `const result = ctz(buffer[, result])` | |
| 55 | ||
| 56 | Count the number of trailing zeroes in `buffer`, storing the result in `result`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 57 | ||
| 58 | #### `const result = not(buffer[, result])` | |
| 59 | ||
| 60 | Compute the bitwise NOT in `buffer`, storing the result in `result`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 61 | ||
| 62 | #### `const result = or(a, b[, result])` | |
| 63 | ||
| 64 | Compute the bitwise OR of `a` and `b`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 65 | ||
| 66 | #### `const result = sum(buffer)` | |
| 67 | ||
| 68 | Compute the sum of `buffer` as a `bigint`. | |
| 69 | ||
| 70 | #### `const result = xor(a, b[, result])` | |
| 71 | ||
| 72 | Compute the bitwise XOR of `a` and `b`. If `result` is not provided, a fresh buffer is allocated. The `result` buffer is returned to the caller. | |
| 73 | ||
| 74 | ## License | |
| 75 | ||
| 76 | ISC |
| … | … | @@ -0,0 +1,236 @@ |
| 1 | const b4a = require('b4a') | |
| 2 | const scalar = require('./scalar') | |
| 3 | ||
| 4 | function view (buf, n) { | |
| 5 | if (n === buf.BYTES_PER_ELEMENT) return buf | |
| 6 | ||
| 7 | let TypedArray | |
| 8 | ||
| 9 | if (n === 1) TypedArray = Uint8Array | |
| 10 | else if (n === 2) TypedArray = Uint16Array | |
| 11 | else TypedArray = Uint32Array | |
| 12 | ||
| 13 | return new TypedArray(buf.buffer, buf.byteOffset, buf.byteLength / n) | |
| 14 | } | |
| 15 | ||
| 16 | function unary (u8, u16 = u8, u32 = u16) { | |
| 17 | return function unary (buf, result = b4a.allocUnsafe(buf.byteLength)) { | |
| 18 | if (buf.byteLength % 16 !== 0) { | |
| 19 | throw new Error('Buffer length must be a multiple of 16') | |
| 20 | } | |
| 21 | ||
| 22 | if (buf.byteLength !== result.byteLength) { | |
| 23 | throw new Error('Length of result buffer is insufficient') | |
| 24 | } | |
| 25 | ||
| 26 | const n = buf.BYTES_PER_ELEMENT | |
| 27 | ||
| 28 | if (n === 1) u8(buf, view(result, n)) | |
| 29 | else if (n === 2) u16(buf, view(result, n)) | |
| 30 | else u32(buf, view(result, n)) | |
| 31 | ||
| 32 | return result | |
| 33 | } | |
| 34 | } | |
| 35 | ||
| 36 | function binary (u8, u16 = u8, u32 = u16) { | |
| 37 | return function binary (a, b, result = b4a.allocUnsafe(a.byteLength)) { | |
| 38 | if (a.byteLength % 16 !== 0) { | |
| 39 | throw new Error('Buffer length must be a multiple of 16') | |
| 40 | } | |
| 41 | ||
| 42 | if (a.byteLength !== b.byteLength || a.byteLength !== result.byteLength) { | |
| 43 | throw new Error('Buffers must be the same length') | |
| 44 | } | |
| 45 | ||
| 46 | const n = a.BYTES_PER_ELEMENT | |
| 47 | ||
| 48 | if (n === 1) u8(a, b, view(result, n)) | |
| 49 | else if (n === 2) u16(a, b, view(result, n)) | |
| 50 | else u32(a, b, view(result, n)) | |
| 51 | ||
| 52 | return result | |
| 53 | } | |
| 54 | } | |
| 55 | ||
| 56 | function reduce (u8, u16 = u8, u32 = u16) { | |
| 57 | return function reduce (buf) { | |
| 58 | if (buf.byteLength % 16 !== 0) { | |
| 59 | throw new Error('Buffer length must be a multiple of 16') | |
| 60 | } | |
| 61 | ||
| 62 | const n = buf.BYTES_PER_ELEMENT | |
| 63 | ||
| 64 | if (n === 1) return u8(buf) | |
| 65 | if (n === 2) return u16(buf) | |
| 66 | return u32(buf) | |
| 67 | } | |
| 68 | } | |
| 69 | ||
| 70 | exports.allo = function allo (buf) { | |
| 71 | if (buf.byteLength % 16 !== 0) { | |
| 72 | throw new Error('Buffer length must be a multiple of 16') | |
| 73 | } | |
| 74 | ||
| 75 | const m = 2 ** (buf.BYTES_PER_ELEMENT * 8) - 1 | |
| 76 | ||
| 77 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 78 | if (buf[i] !== m) return false | |
| 79 | } | |
| 80 | ||
| 81 | return true | |
| 82 | } | |
| 83 | ||
| 84 | exports.allz = function allz (buf) { | |
| 85 | if (buf.byteLength % 16 !== 0) { | |
| 86 | throw new Error('Buffer length must be a multiple of 16') | |
| 87 | } | |
| 88 | ||
| 89 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 90 | if (buf[i] !== 0) return false | |
| 91 | } | |
| 92 | ||
| 93 | return true | |
| 94 | } | |
| 95 | ||
| 96 | exports.and = binary( | |
| 97 | (a, b, result) => { | |
| 98 | for (let i = 0, n = result.length; i < n; i++) { | |
| 99 | result[i] = a[i] & b[i] | |
| 100 | } | |
| 101 | } | |
| 102 | ) | |
| 103 | ||
| 104 | exports.clear = binary( | |
| 105 | (a, b, result) => { | |
| 106 | for (let i = 0, n = result.length; i < n; i++) { | |
| 107 | result[i] = a[i] & ~b[i] | |
| 108 | } | |
| 109 | } | |
| 110 | ) | |
| 111 | ||
| 112 | exports.clo = unary( | |
| 113 | (buf, result) => { | |
| 114 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 115 | result[i] = 24 - scalar.clo(buf[i]) | |
| 116 | } | |
| 117 | }, | |
| 118 | (buf, result) => { | |
| 119 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 120 | result[i] = 16 - scalar.clo(buf[i]) | |
| 121 | } | |
| 122 | }, | |
| 123 | (buf, result) => { | |
| 124 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 125 | result[i] = scalar.clo(buf[i]) | |
| 126 | } | |
| 127 | } | |
| 128 | ) | |
| 129 | ||
| 130 | exports.clz = unary( | |
| 131 | (buf, result) => { | |
| 132 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 133 | result[i] = 24 - scalar.clz(buf[i]) | |
| 134 | } | |
| 135 | }, | |
| 136 | (buf, result) => { | |
| 137 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 138 | result[i] = 16 - scalar.clz(buf[i]) | |
| 139 | } | |
| 140 | }, | |
| 141 | (buf, result) => { | |
| 142 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 143 | result[i] = scalar.clz(buf[i]) | |
| 144 | } | |
| 145 | } | |
| 146 | ) | |
| 147 | ||
| 148 | exports.cnt = unary( | |
| 149 | (buf, result) => { | |
| 150 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 151 | result[i] = scalar.cnt(buf[i]) & 0xff | |
| 152 | } | |
| 153 | }, | |
| 154 | (buf, result) => { | |
| 155 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 156 | result[i] = scalar.cnt(buf[i]) & 0xffff | |
| 157 | } | |
| 158 | }, | |
| 159 | (buf, result) => { | |
| 160 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 161 | result[i] = scalar.cnt(buf[i]) | |
| 162 | } | |
| 163 | } | |
| 164 | ) | |
| 165 | ||
| 166 | exports.cto = unary( | |
| 167 | (buf, result) => { | |
| 168 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 169 | result[i] = Math.min(scalar.cto(buf[i]), 8) | |
| 170 | } | |
| 171 | }, | |
| 172 | (buf, result) => { | |
| 173 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 174 | result[i] = Math.min(scalar.cto(buf[i]), 16) | |
| 175 | } | |
| 176 | }, | |
| 177 | (buf, result) => { | |
| 178 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 179 | result[i] = scalar.cto(buf[i]) | |
| 180 | } | |
| 181 | } | |
| 182 | ) | |
| 183 | ||
| 184 | exports.ctz = unary( | |
| 185 | (buf, result) => { | |
| 186 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 187 | result[i] = Math.min(scalar.ctz(buf[i]), 8) | |
| 188 | } | |
| 189 | }, | |
| 190 | (buf, result) => { | |
| 191 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 192 | result[i] = Math.min(scalar.ctz(buf[i]), 16) | |
| 193 | } | |
| 194 | }, | |
| 195 | (buf, result) => { | |
| 196 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 197 | result[i] = scalar.ctz(buf[i]) | |
| 198 | } | |
| 199 | } | |
| 200 | ) | |
| 201 | ||
| 202 | exports.not = unary( | |
| 203 | (buf, result) => { | |
| 204 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 205 | result[i] = ~buf[i] | |
| 206 | } | |
| 207 | } | |
| 208 | ) | |
| 209 | ||
| 210 | exports.or = binary( | |
| 211 | (a, b, result) => { | |
| 212 | for (let i = 0, n = result.length; i < n; i++) { | |
| 213 | result[i] = a[i] | b[i] | |
| 214 | } | |
| 215 | } | |
| 216 | ) | |
| 217 | ||
| 218 | exports.sum = reduce( | |
| 219 | (buf) => { | |
| 220 | let result = 0n | |
| 221 | ||
| 222 | for (let i = 0, n = buf.length; i < n; i++) { | |
| 223 | result += BigInt(buf[i]) | |
| 224 | } | |
| 225 | ||
| 226 | return result | |
| 227 | } | |
| 228 | ) | |
| 229 | ||
| 230 | exports.xor = binary( | |
| 231 | (a, b, result) => { | |
| 232 | for (let i = 0, n = result.length; i < n; i++) { | |
| 233 | result[i] = a[i] ^ b[i] | |
| 234 | } | |
| 235 | } | |
| 236 | ) |
| … | … | @@ -0,0 +1,5 @@ |
| 1 | try { | |
| 2 | module.exports = require('simdle-native') | |
| 3 | } catch { | |
| 4 | module.exports = require('./fallback') | |
| 5 | } |
| … | … | @@ -0,0 +1,37 @@ |
| 1 | { | |
| 2 | "name": "simdle-universal", | |
| 3 | "version": "1.1.2", | |
| 4 | "description": "Universal wrapper for libsimdle with a JavaScript fallback", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "fallback.js", | |
| 8 | "index.js", | |
| 9 | "scalar.js" | |
| 10 | ], | |
| 11 | "browser": { | |
| 12 | "./index.js": "./fallback.js" | |
| 13 | }, | |
| 14 | "scripts": { | |
| 15 | "test": "standard && brittle test.mjs" | |
| 16 | }, | |
| 17 | "repository": { | |
| 18 | "type": "git", | |
| 19 | "url": "git+https://github.com/holepunchto/simdle-universal.git" | |
| 20 | }, | |
| 21 | "author": "Kasper Isager Dalsgarð <kasper@funktionel.co>", | |
| 22 | "license": "ISC", | |
| 23 | "bugs": { | |
| 24 | "url": "https://github.com/holepunchto/simdle-universal/issues" | |
| 25 | }, | |
| 26 | "homepage": "https://github.com/holepunchto/simdle-universal#readme", | |
| 27 | "dependencies": { | |
| 28 | "b4a": "^1.6.0" | |
| 29 | }, | |
| 30 | "optionalDependencies": { | |
| 31 | "simdle-native": "^1.1.1" | |
| 32 | }, | |
| 33 | "devDependencies": { | |
| 34 | "brittle": "^3.1.0", | |
| 35 | "standard": "^17.0.0" | |
| 36 | } | |
| 37 | } |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | const clz = exports.clz = function clz (n) { | |
| 2 | return Math.clz32(n) | |
| 3 | } | |
| 4 | ||
| 5 | exports.clo = function clo (n) { | |
| 6 | return clz(~n) | |
| 7 | } | |
| 8 | ||
| 9 | const ctz = exports.ctz = function ctz (n) { | |
| 10 | return 32 - (n === 0 ? 0 : (clz(n & -n) + 1)) | |
| 11 | } | |
| 12 | ||
| 13 | exports.cto = function cto (n) { | |
| 14 | return ctz(~n) | |
| 15 | } | |
| 16 | ||
| 17 | exports.cnt = function cnt (n) { | |
| 18 | n = n - ((n >>> 1) & 0x55555555) | |
| 19 | n = (n & 0x33333333) + ((n >>> 2) & 0x33333333) | |
| 20 | n = (n + (n >>> 4)) & 0x0f0f0f0f | |
| 21 | n = (n * 0x01010101) >>> 24 | |
| 22 | return n | |
| 23 | } |
| … | … | @@ -0,0 +1,249 @@ |
| 1 | cmake_minimum_required(VERSION 3.25) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) | |
| 5 | find_package(cmake-napi REQUIRED PATHS node_modules/cmake-napi) | |
| 6 | ||
| 7 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) | |
| 8 | ||
| 9 | project(sodium_native C ASM) | |
| 10 | ||
| 11 | fetch_package("github:jedisct1/libsodium#stable" SOURCE_DIR sodium) | |
| 12 | ||
| 13 | bare_target(target) | |
| 14 | ||
| 15 | if(target MATCHES "win32") | |
| 16 | add_compile_options(/MT$<$<CONFIG:Debug>:d>) | |
| 17 | endif() | |
| 18 | ||
| 19 | file(COPY_FILE "${sodium}/builds/msvc/version.h" "${sodium}/src/libsodium/include/sodium/version.h") | |
| 20 | ||
| 21 | file(GLOB_RECURSE sodium_headers CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.h") | |
| 22 | file(GLOB_RECURSE sodium_sources CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.c") | |
| 23 | file(GLOB_RECURSE sodium_asm_sources CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.S") | |
| 24 | ||
| 25 | add_library(sodium OBJECT) | |
| 26 | ||
| 27 | target_sources( | |
| 28 | sodium | |
| 29 | INTERFACE | |
| 30 | ${sodium_headers} | |
| 31 | PRIVATE | |
| 32 | ${sodium_sources} | |
| 33 | ) | |
| 34 | ||
| 35 | target_include_directories( | |
| 36 | sodium | |
| 37 | INTERFACE | |
| 38 | "${sodium}/src/libsodium/include" | |
| 39 | PRIVATE | |
| 40 | "${sodium}/src/libsodium/include/sodium" | |
| 41 | ) | |
| 42 | ||
| 43 | if(NOT target MATCHES "win32") | |
| 44 | target_compile_options( | |
| 45 | sodium | |
| 46 | PRIVATE | |
| 47 | -fvisibility=hidden | |
| 48 | -fno-strict-aliasing | |
| 49 | -fwrapv | |
| 50 | -flax-vector-conversions | |
| 51 | ) | |
| 52 | endif() | |
| 53 | ||
| 54 | target_compile_definitions( | |
| 55 | sodium | |
| 56 | PUBLIC | |
| 57 | SODIUM_STATIC=1 | |
| 58 | PRIVATE | |
| 59 | _GNU_SOURCE=1 | |
| 60 | CONFIGURED=1 | |
| 61 | DEV_MODE=1 | |
| 62 | HAVE_ATOMIC_OPS=1 | |
| 63 | HAVE_C11_MEMORY_FENCES=1 | |
| 64 | HAVE_CET_H=1 | |
| 65 | HAVE_GCC_MEMORY_FENCES=1 | |
| 66 | HAVE_INLINE_ASM=1 | |
| 67 | HAVE_INTTYPES_H=1 | |
| 68 | HAVE_STDINT_H=1 | |
| 69 | HAVE_TI_MODE=1 | |
| 70 | ) | |
| 71 | ||
| 72 | if(target MATCHES "darwin|ios") | |
| 73 | target_compile_definitions( | |
| 74 | sodium | |
| 75 | PRIVATE | |
| 76 | ASM_HIDE_SYMBOL=.private_extern | |
| 77 | TLS=_Thread_local | |
| 78 | HAVE_ARC4RANDOM=1 | |
| 79 | HAVE_ARC4RANDOM_BUF=1 | |
| 80 | HAVE_CATCHABLE_ABRT=1 | |
| 81 | HAVE_CATCHABLE_SEGV=1 | |
| 82 | HAVE_CLOCK_GETTIME=1 | |
| 83 | HAVE_GETPID=1 | |
| 84 | HAVE_MADVISE=1 | |
| 85 | HAVE_MEMSET_S=1 | |
| 86 | HAVE_MLOCK=1 | |
| 87 | HAVE_MMAP=1 | |
| 88 | HAVE_MPROTECT=1 | |
| 89 | HAVE_NANOSLEEP=1 | |
| 90 | HAVE_POSIX_MEMALIGN=1 | |
| 91 | HAVE_PTHREAD=1 | |
| 92 | HAVE_PTHREAD_PRIO_INHERIT=1 | |
| 93 | HAVE_RAISE=1 | |
| 94 | HAVE_SYSCONF=1 | |
| 95 | HAVE_SYS_MMAN_H=1 | |
| 96 | HAVE_SYS_PARAM_H=1 | |
| 97 | HAVE_WEAK_SYMBOLS=1 | |
| 98 | ) | |
| 99 | ||
| 100 | if(NOT target MATCHES "ios") | |
| 101 | target_compile_definitions( | |
| 102 | sodium | |
| 103 | PRIVATE | |
| 104 | HAVE_GETENTROPY=1 | |
| 105 | HAVE_SYS_RANDOM_H=1 | |
| 106 | ) | |
| 107 | endif() | |
| 108 | endif() | |
| 109 | ||
| 110 | if(target MATCHES "linux") | |
| 111 | target_compile_definitions( | |
| 112 | sodium | |
| 113 | PRIVATE | |
| 114 | ASM_HIDE_SYMBOL=.hidden | |
| 115 | TLS=_Thread_local | |
| 116 | HAVE_CATCHABLE_ABRT=1 | |
| 117 | HAVE_CATCHABLE_SEGV=1 | |
| 118 | HAVE_CLOCK_GETTIME=1 | |
| 119 | HAVE_GETPID=1 | |
| 120 | HAVE_MADVISE=1 | |
| 121 | HAVE_MLOCK=1 | |
| 122 | HAVE_MMAP=1 | |
| 123 | HAVE_MPROTECT=1 | |
| 124 | HAVE_NANOSLEEP=1 | |
| 125 | HAVE_POSIX_MEMALIGN=1 | |
| 126 | HAVE_PTHREAD_PRIO_INHERIT=1 | |
| 127 | HAVE_PTHREAD=1 | |
| 128 | HAVE_RAISE=1 | |
| 129 | HAVE_SYSCONF=1 | |
| 130 | HAVE_SYS_AUXV_H=1 | |
| 131 | HAVE_SYS_MMAN_H=1 | |
| 132 | HAVE_SYS_PARAM_H=1 | |
| 133 | HAVE_SYS_RANDOM_H=1 | |
| 134 | HAVE_WEAK_SYMBOLS=1 | |
| 135 | ) | |
| 136 | endif() | |
| 137 | ||
| 138 | if(target MATCHES "win32") | |
| 139 | target_compile_definitions( | |
| 140 | sodium | |
| 141 | PRIVATE | |
| 142 | _CRT_SECURE_NO_WARNINGS=1 | |
| 143 | HAVE_RAISE=1 | |
| 144 | ) | |
| 145 | endif() | |
| 146 | ||
| 147 | if(target MATCHES "x64") | |
| 148 | target_compile_definitions( | |
| 149 | sodium | |
| 150 | PRIVATE | |
| 151 | HAVE_CPUID=1 | |
| 152 | HAVE_RDRAND=1 | |
| 153 | HAVE_EMMINTRIN_H=1 # SSE2 | |
| 154 | HAVE_PMMINTRIN_H=1 # SSE3 | |
| 155 | HAVE_TMMINTRIN_H=1 # SSSE3 | |
| 156 | HAVE_SMMINTRIN_H=1 # SSE4.1 | |
| 157 | HAVE_WMMINTRIN_H=1 # AES | |
| 158 | HAVE_AVXINTRIN_H=1 # AVX | |
| 159 | HAVE_AVX2INTRIN_H=1 # AVX2 | |
| 160 | HAVE_AVX512FINTRIN_H # AVX512F | |
| 161 | ) | |
| 162 | ||
| 163 | if(NOT target MATCHES "win32") | |
| 164 | target_compile_definitions( | |
| 165 | sodium | |
| 166 | PRIVATE | |
| 167 | HAVE_AMD64_ASM=1 | |
| 168 | HAVE_AVX_ASM=1 | |
| 169 | ) | |
| 170 | ||
| 171 | target_sources( | |
| 172 | sodium | |
| 173 | PRIVATE | |
| 174 | ${sodium_asm_sources} | |
| 175 | ) | |
| 176 | endif() | |
| 177 | endif() | |
| 178 | ||
| 179 | if(target MATCHES "arm64") | |
| 180 | target_compile_definitions( | |
| 181 | sodium | |
| 182 | PRIVATE | |
| 183 | HAVE_ARMCRYPTO=1 | |
| 184 | ) | |
| 185 | endif() | |
| 186 | ||
| 187 | if(CMAKE_C_BYTE_ORDER MATCHES "BIG_ENDIAN") | |
| 188 | target_compile_definitions( | |
| 189 | sodium | |
| 190 | PRIVATE | |
| 191 | NATIVE_BIG_ENDIAN=1 | |
| 192 | ) | |
| 193 | else() | |
| 194 | target_compile_definitions( | |
| 195 | sodium | |
| 196 | PRIVATE | |
| 197 | NATIVE_LITTLE_ENDIAN=1 | |
| 198 | ) | |
| 199 | endif() | |
| 200 | ||
| 201 | if(target MATCHES "linux|android") | |
| 202 | target_link_options( | |
| 203 | sodium | |
| 204 | PUBLIC | |
| 205 | -Wl,-z,noexecstack | |
| 206 | ) | |
| 207 | endif() | |
| 208 | ||
| 209 | add_bare_module(sodium_native_bare) | |
| 210 | ||
| 211 | target_sources( | |
| 212 | ${sodium_native_bare} | |
| 213 | PRIVATE | |
| 214 | binding.c | |
| 215 | macros.h | |
| 216 | extensions/tweak/tweak.c | |
| 217 | extensions/tweak/tweak.h | |
| 218 | extensions/pbkdf2/pbkdf2.c | |
| 219 | extensions/pbkdf2/pbkdf2.h | |
| 220 | ) | |
| 221 | ||
| 222 | target_link_libraries( | |
| 223 | ${sodium_native_bare} | |
| 224 | PRIVATE | |
| 225 | $<TARGET_OBJECTS:sodium> | |
| 226 | PUBLIC | |
| 227 | sodium | |
| 228 | ) | |
| 229 | ||
| 230 | add_napi_module(sodium_native_node) | |
| 231 | ||
| 232 | target_sources( | |
| 233 | ${sodium_native_node} | |
| 234 | PRIVATE | |
| 235 | binding.c | |
| 236 | macros.h | |
| 237 | extensions/tweak/tweak.c | |
| 238 | extensions/tweak/tweak.h | |
| 239 | extensions/pbkdf2/pbkdf2.c | |
| 240 | extensions/pbkdf2/pbkdf2.h | |
| 241 | ) | |
| 242 | ||
| 243 | target_link_libraries( | |
| 244 | ${sodium_native_node} | |
| 245 | PRIVATE | |
| 246 | $<TARGET_OBJECTS:sodium> | |
| 247 | PUBLIC | |
| 248 | sodium | |
| 249 | ) |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2016 Mathias Buus and Emil Bay | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,51 @@ |
| 1 | # sodium-native | |
| 2 | ||
| 3 | Low level bindings for [libsodium](https://github.com/jedisct1/libsodium). | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install sodium-native | |
| 7 | ``` | |
| 8 | ||
| 9 | The goal of this project is to be thin, stable, unopionated wrapper around libsodium. | |
| 10 | ||
| 11 | All methods exposed are more or less a direct translation of the libsodium c-api. | |
| 12 | This means that most data types are buffers and you have to manage allocating return values and passing them in as arguments instead of receiving them as return values. | |
| 13 | ||
| 14 | This makes this API harder to use than other libsodium wrappers out there, but also means that you'll be able to get a lot of perf / memory improvements as you can do stuff like inline encryption / decryption, reuse buffers etc. | |
| 15 | ||
| 16 | This also makes this library useful as a foundation for more high level crypto abstractions that you want to make. | |
| 17 | ||
| 18 | ## Usage | |
| 19 | ||
| 20 | ``` js | |
| 21 | var sodium = require('sodium-native') | |
| 22 | ||
| 23 | var nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES) | |
| 24 | var key = sodium.sodium_malloc(sodium.crypto_secretbox_KEYBYTES) // secure buffer | |
| 25 | var message = Buffer.from('Hello, World!') | |
| 26 | var ciphertext = Buffer.alloc(message.length + sodium.crypto_secretbox_MACBYTES) | |
| 27 | ||
| 28 | sodium.randombytes_buf(nonce) // insert random data into nonce | |
| 29 | sodium.randombytes_buf(key) // insert random data into key | |
| 30 | ||
| 31 | // encrypted message is stored in ciphertext. | |
| 32 | sodium.crypto_secretbox_easy(ciphertext, message, nonce, key) | |
| 33 | ||
| 34 | console.log('Encrypted message:', ciphertext) | |
| 35 | ||
| 36 | var plainText = Buffer.alloc(ciphertext.length - sodium.crypto_secretbox_MACBYTES) | |
| 37 | ||
| 38 | if (!sodium.crypto_secretbox_open_easy(plainText, ciphertext, nonce, key)) { | |
| 39 | console.log('Decryption failed!') | |
| 40 | } else { | |
| 41 | console.log('Decrypted message:', plainText, '(' + plainText.toString() + ')') | |
| 42 | } | |
| 43 | ``` | |
| 44 | ||
| 45 | ## Documentation | |
| 46 | ||
| 47 | Complete documentation may be found on the [sodium-friends website](https://sodium-friends.github.io/docs/docs/getstarted) | |
| 48 | ||
| 49 | ## License | |
| 50 | ||
| 51 | MIT |
| … | … | @@ -0,0 +1,3470 @@ |
| 1 | #include <node_api.h> | |
| 2 | #include <uv.h> | |
| 3 | #include <string.h> | |
| 4 | #include <sodium.h> | |
| 5 | #include "macros.h" | |
| 6 | #include "extensions/tweak/tweak.h" | |
| 7 | #include "extensions/pbkdf2/pbkdf2.h" | |
| 8 | ||
| 9 | static uint8_t typedarray_width (napi_typedarray_type type) { | |
| 10 | switch (type) { | |
| 11 | case napi_int8_array: return 1; | |
| 12 | case napi_uint8_array: return 1; | |
| 13 | case napi_uint8_clamped_array: return 1; | |
| 14 | case napi_int16_array: return 2; | |
| 15 | case napi_uint16_array: return 2; | |
| 16 | case napi_int32_array: return 4; | |
| 17 | case napi_uint32_array: return 4; | |
| 18 | case napi_float32_array: return 4; | |
| 19 | case napi_float64_array: return 8; | |
| 20 | case napi_bigint64_array: return 8; | |
| 21 | case napi_biguint64_array: return 8; | |
| 22 | default: return 0; | |
| 23 | } | |
| 24 | } | |
| 25 | ||
| 26 | static void sn_sodium_free_finalise (napi_env env, void *finalise_data, void *finalise_hint) { | |
| 27 | napi_adjust_external_memory(env, -4 * 4096, NULL); | |
| 28 | sodium_free(finalise_data); | |
| 29 | } | |
| 30 | ||
| 31 | napi_value sn_sodium_memzero (napi_env env, napi_callback_info info) { | |
| 32 | SN_ARGV(1, sodium_memzero) | |
| 33 | ||
| 34 | SN_ARGV_TYPEDARRAY(buf, 0) | |
| 35 | ||
| 36 | sodium_memzero(buf_data, buf_size); | |
| 37 | ||
| 38 | return NULL; | |
| 39 | } | |
| 40 | ||
| 41 | napi_value sn_sodium_mlock (napi_env env, napi_callback_info info) { | |
| 42 | SN_ARGV(1, sodium_mlock) | |
| 43 | ||
| 44 | SN_ARGV_TYPEDARRAY(buf, 0) | |
| 45 | ||
| 46 | SN_RETURN(sodium_mlock(buf_data, buf_size), "memory lock failed") | |
| 47 | } | |
| 48 | ||
| 49 | napi_value sn_sodium_munlock (napi_env env, napi_callback_info info) { | |
| 50 | SN_ARGV(1, sodium_munlock) | |
| 51 | ||
| 52 | SN_ARGV_TYPEDARRAY(buf, 0) | |
| 53 | ||
| 54 | SN_RETURN(sodium_munlock(buf_data, buf_size), "memory unlock failed") | |
| 55 | } | |
| 56 | ||
| 57 | napi_value sn_sodium_free (napi_env env, napi_callback_info info) { | |
| 58 | SN_ARGV(1, sodium_free) | |
| 59 | ||
| 60 | SN_ARGV_TYPEDARRAY_PTR(buf, 0) | |
| 61 | ||
| 62 | if (buf_data == NULL) return NULL; | |
| 63 | ||
| 64 | napi_value array_buf; | |
| 65 | ||
| 66 | SN_STATUS_THROWS(napi_get_named_property(env, argv[0], "buffer", &array_buf), "failed to get arraybuffer"); | |
| 67 | SN_STATUS_THROWS(napi_remove_wrap(env, array_buf, NULL), "failed to remove wrap"); // remove the finalizer | |
| 68 | SN_STATUS_THROWS(napi_detach_arraybuffer(env, array_buf), "failed to detach array buffer"); | |
| 69 | napi_adjust_external_memory(env, -4 * 4096, NULL); | |
| 70 | sodium_free(buf_data); | |
| 71 | ||
| 72 | return NULL; | |
| 73 | } | |
| 74 | ||
| 75 | napi_value sn_sodium_malloc (napi_env env, napi_callback_info info) { | |
| 76 | SN_ARGV(1, sodium_malloc); | |
| 77 | ||
| 78 | SN_ARGV_UINT32(size, 0) | |
| 79 | ||
| 80 | void *ptr = sodium_malloc(size); | |
| 81 | ||
| 82 | SN_THROWS(ptr == NULL, "ENOMEM") | |
| 83 | napi_adjust_external_memory(env, 4 * 4096, NULL); | |
| 84 | ||
| 85 | SN_THROWS(ptr == NULL, "sodium_malloc failed"); | |
| 86 | ||
| 87 | napi_value buf, value, array_buf; | |
| 88 | ||
| 89 | SN_STATUS_THROWS(napi_create_external_buffer(env, size, ptr, NULL, NULL, &buf), "failed to create a n-api buffer") | |
| 90 | SN_STATUS_THROWS(napi_get_boolean(env, true, &value), "failed to create boolean") | |
| 91 | ||
| 92 | SN_STATUS_THROWS(napi_get_named_property(env, buf, "buffer", &array_buf), "failed to get arraybuffer") | |
| 93 | SN_STATUS_THROWS(napi_set_named_property(env, buf, "secure", value), "failed to set secure property") | |
| 94 | ||
| 95 | SN_STATUS_THROWS(napi_wrap(env, array_buf, ptr, sn_sodium_free_finalise, NULL, NULL), "failed to wrap") | |
| 96 | ||
| 97 | return buf; | |
| 98 | } | |
| 99 | ||
| 100 | napi_value sn_sodium_mprotect_noaccess (napi_env env, napi_callback_info info) { | |
| 101 | SN_ARGV(1, sodium_mprotect_noaccess); | |
| 102 | ||
| 103 | SN_ARGV_TYPEDARRAY_PTR(buf, 0) | |
| 104 | ||
| 105 | SN_RETURN(sodium_mprotect_noaccess(buf_data), "failed to lock buffer") | |
| 106 | } | |
| 107 | ||
| 108 | ||
| 109 | napi_value sn_sodium_mprotect_readonly (napi_env env, napi_callback_info info) { | |
| 110 | SN_ARGV(1, sodium_readonly); | |
| 111 | ||
| 112 | SN_ARGV_TYPEDARRAY_PTR(buf, 0) | |
| 113 | ||
| 114 | SN_RETURN(sodium_mprotect_readonly(buf_data), "failed to unlock buffer") | |
| 115 | } | |
| 116 | ||
| 117 | ||
| 118 | napi_value sn_sodium_mprotect_readwrite (napi_env env, napi_callback_info info) { | |
| 119 | SN_ARGV(1, sodium_readwrite); | |
| 120 | ||
| 121 | SN_ARGV_TYPEDARRAY_PTR(buf, 0) | |
| 122 | ||
| 123 | SN_RETURN(sodium_mprotect_readwrite(buf_data), "failed to unlock buffer") | |
| 124 | } | |
| 125 | ||
| 126 | napi_value sn_randombytes_random (napi_env env, napi_callback_info info) { | |
| 127 | napi_value result; | |
| 128 | ||
| 129 | napi_create_uint32(env, randombytes_random(), &result); | |
| 130 | return result; | |
| 131 | } | |
| 132 | ||
| 133 | napi_value sn_randombytes_uniform (napi_env env, napi_callback_info info) { | |
| 134 | SN_ARGV(1, randombytes_uniform); | |
| 135 | ||
| 136 | SN_ARGV_UINT32(upper_bound, 0) | |
| 137 | ||
| 138 | napi_value result; | |
| 139 | napi_create_uint32(env, randombytes_uniform(upper_bound), &result); | |
| 140 | return result; | |
| 141 | } | |
| 142 | ||
| 143 | napi_value sn_randombytes_buf (napi_env env, napi_callback_info info) { | |
| 144 | SN_ARGV(1, randombytes_buf) | |
| 145 | ||
| 146 | SN_ARGV_TYPEDARRAY(buf, 0) | |
| 147 | ||
| 148 | randombytes_buf(buf_data, buf_size); | |
| 149 | ||
| 150 | return NULL; | |
| 151 | } | |
| 152 | ||
| 153 | napi_value sn_randombytes_buf_deterministic (napi_env env, napi_callback_info info) { | |
| 154 | SN_ARGV(2, randombytes_buf) | |
| 155 | ||
| 156 | SN_ARGV_TYPEDARRAY(buf, 0) | |
| 157 | SN_ARGV_TYPEDARRAY(seed, 1) | |
| 158 | ||
| 159 | SN_ASSERT_LENGTH(seed_size, randombytes_SEEDBYTES, "seed") | |
| 160 | ||
| 161 | randombytes_buf_deterministic(buf_data, buf_size, seed_data); | |
| 162 | ||
| 163 | return NULL; | |
| 164 | } | |
| 165 | ||
| 166 | napi_value sn_sodium_memcmp(napi_env env, napi_callback_info info) { | |
| 167 | SN_ARGV(2, sodium_memcmp); | |
| 168 | ||
| 169 | SN_ARGV_TYPEDARRAY(b1, 0) | |
| 170 | SN_ARGV_TYPEDARRAY(b2, 1) | |
| 171 | ||
| 172 | SN_THROWS(b1_size != b2_size, "buffers must be of same length") | |
| 173 | ||
| 174 | SN_RETURN_BOOLEAN(sodium_memcmp(b1_data, b2_data, b1_size)) | |
| 175 | } | |
| 176 | ||
| 177 | napi_value sn_sodium_increment(napi_env env, napi_callback_info info) { | |
| 178 | SN_ARGV(1, sodium_increment); | |
| 179 | SN_ARGV_TYPEDARRAY(n, 0) | |
| 180 | ||
| 181 | sodium_increment(n_data, n_size); | |
| 182 | ||
| 183 | return NULL; | |
| 184 | } | |
| 185 | ||
| 186 | napi_value sn_sodium_add(napi_env env, napi_callback_info info) { | |
| 187 | SN_ARGV(2, sodium_add); | |
| 188 | ||
| 189 | SN_ARGV_TYPEDARRAY(a, 0) | |
| 190 | SN_ARGV_TYPEDARRAY(b, 1) | |
| 191 | ||
| 192 | SN_THROWS(a_size != b_size, "buffers must be of same length") | |
| 193 | sodium_add(a_data, b_data, a_size); | |
| 194 | ||
| 195 | return NULL; | |
| 196 | } | |
| 197 | ||
| 198 | napi_value sn_sodium_sub(napi_env env, napi_callback_info info) { | |
| 199 | SN_ARGV(2, sodium_sub); | |
| 200 | ||
| 201 | SN_ARGV_TYPEDARRAY(a, 0) | |
| 202 | SN_ARGV_TYPEDARRAY(b, 1) | |
| 203 | ||
| 204 | SN_THROWS(a_size != b_size, "buffers must be of same length") | |
| 205 | sodium_sub(a_data, b_data, a_size); | |
| 206 | ||
| 207 | return NULL; | |
| 208 | } | |
| 209 | ||
| 210 | napi_value sn_sodium_compare(napi_env env, napi_callback_info info) { | |
| 211 | SN_ARGV(2, sodium_compare); | |
| 212 | ||
| 213 | SN_ARGV_TYPEDARRAY(a, 0) | |
| 214 | SN_ARGV_TYPEDARRAY(b, 1) | |
| 215 | ||
| 216 | SN_THROWS(a_size != b_size, "buffers must be of same length") | |
| 217 | int cmp = sodium_compare(a_data, b_data, a_size); | |
| 218 | ||
| 219 | napi_value result; | |
| 220 | napi_create_int32(env, cmp, &result); | |
| 221 | ||
| 222 | return result; | |
| 223 | } | |
| 224 | ||
| 225 | napi_value sn_sodium_is_zero(napi_env env, napi_callback_info info) { | |
| 226 | SN_ARGV_OPTS(1, 2, sodium_is_zero); | |
| 227 | ||
| 228 | SN_ARGV_TYPEDARRAY(a, 0) | |
| 229 | ||
| 230 | size_t a_full = a_size; | |
| 231 | ||
| 232 | if (argc == 2) { | |
| 233 | SN_OPT_ARGV_UINT32(a_size, 1) | |
| 234 | SN_THROWS(a_size > a_full, "len must be shorter than 'buf.byteLength'") | |
| 235 | } | |
| 236 | ||
| 237 | SN_RETURN_BOOLEAN_FROM_1(sodium_is_zero(a_data, a_size)) | |
| 238 | } | |
| 239 | ||
| 240 | napi_value sn_sodium_pad(napi_env env, napi_callback_info info) { | |
| 241 | SN_ARGV(3, sodium_pad); | |
| 242 | ||
| 243 | SN_ARGV_TYPEDARRAY(buf, 0) | |
| 244 | SN_ARGV_UINT32(unpadded_buflen, 1) | |
| 245 | SN_ARGV_UINT32(blocksize, 2) | |
| 246 | ||
| 247 | SN_THROWS(unpadded_buflen > buf_size, "unpadded length cannot exceed buffer length") | |
| 248 | SN_THROWS(blocksize > buf_size, "block size cannot exceed buffer length") | |
| 249 | SN_THROWS(blocksize < 1, "block sizemust be at least 1 byte") | |
| 250 | SN_THROWS(buf_size < unpadded_buflen + (blocksize - (unpadded_buflen % blocksize)), "buf not long enough") | |
| 251 | ||
| 252 | napi_value result; | |
| 253 | size_t padded_buflen; | |
| 254 | sodium_pad(&padded_buflen, buf_data, unpadded_buflen, blocksize, buf_size); | |
| 255 | napi_create_uint32(env, padded_buflen, &result); | |
| 256 | return result; | |
| 257 | } | |
| 258 | ||
| 259 | napi_value sn_sodium_unpad(napi_env env, napi_callback_info info) { | |
| 260 | SN_ARGV(3, sodium_unpad); | |
| 261 | ||
| 262 | SN_ARGV_TYPEDARRAY(buf, 0) | |
| 263 | SN_ARGV_UINT32(padded_buflen, 1) | |
| 264 | SN_ARGV_UINT32(blocksize, 2) | |
| 265 | ||
| 266 | SN_THROWS(padded_buflen > buf_size, "unpadded length cannot exceed buffer length") | |
| 267 | SN_THROWS(blocksize > buf_size, "block size cannot exceed buffer length") | |
| 268 | SN_THROWS(blocksize < 1, "block size must be at least 1 byte") | |
| 269 | ||
| 270 | napi_value result; | |
| 271 | size_t unpadded_buflen; | |
| 272 | sodium_unpad(&unpadded_buflen, buf_data, padded_buflen, blocksize); | |
| 273 | napi_create_uint32(env, unpadded_buflen, &result); | |
| 274 | return result; | |
| 275 | } | |
| 276 | ||
| 277 | napi_value sn_crypto_sign_keypair(napi_env env, napi_callback_info info) { | |
| 278 | SN_ARGV(2, crypto_sign_keypair) | |
| 279 | ||
| 280 | SN_ARGV_TYPEDARRAY(pk, 0) | |
| 281 | SN_ARGV_TYPEDARRAY(sk, 1) | |
| 282 | ||
| 283 | SN_ASSERT_LENGTH(pk_size, crypto_sign_PUBLICKEYBYTES, "pk") | |
| 284 | SN_ASSERT_LENGTH(sk_size, crypto_sign_SECRETKEYBYTES, "sk") | |
| 285 | ||
| 286 | SN_RETURN(crypto_sign_keypair(pk_data, sk_data), "keypair generation failed") | |
| 287 | } | |
| 288 | ||
| 289 | napi_value sn_crypto_sign_seed_keypair(napi_env env, napi_callback_info info) { | |
| 290 | SN_ARGV(3, crypto_sign_seed_keypair) | |
| 291 | ||
| 292 | SN_ARGV_TYPEDARRAY(pk, 0) | |
| 293 | SN_ARGV_TYPEDARRAY(sk, 1) | |
| 294 | SN_ARGV_TYPEDARRAY(seed, 2) | |
| 295 | ||
| 296 | SN_ASSERT_LENGTH(pk_size, crypto_sign_PUBLICKEYBYTES, "pk") | |
| 297 | SN_ASSERT_LENGTH(sk_size, crypto_sign_SECRETKEYBYTES, "sk") | |
| 298 | SN_ASSERT_LENGTH(seed_size, crypto_sign_SEEDBYTES, "seed") | |
| 299 | ||
| 300 | SN_RETURN(crypto_sign_seed_keypair(pk_data, sk_data, seed_data), "keypair generation failed") | |
| 301 | } | |
| 302 | ||
| 303 | napi_value sn_crypto_sign(napi_env env, napi_callback_info info) { | |
| 304 | SN_ARGV(3, crypto_sign) | |
| 305 | ||
| 306 | SN_ARGV_TYPEDARRAY(sm, 0) | |
| 307 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 308 | SN_ARGV_TYPEDARRAY(sk, 2) | |
| 309 | ||
| 310 | SN_THROWS(sm_size != crypto_sign_BYTES + m_size, "sm must be 'm.byteLength + crypto_sign_BYTES' bytes") | |
| 311 | SN_ASSERT_LENGTH(sk_size, crypto_sign_SECRETKEYBYTES, "sk") | |
| 312 | ||
| 313 | SN_RETURN(crypto_sign(sm_data, NULL, m_data, m_size, sk_data), "signature failed") | |
| 314 | } | |
| 315 | ||
| 316 | napi_value sn_crypto_sign_open(napi_env env, napi_callback_info info) { | |
| 317 | SN_ARGV(3, crypto_sign_open) | |
| 318 | ||
| 319 | SN_ARGV_TYPEDARRAY(m, 0) | |
| 320 | SN_ARGV_TYPEDARRAY(sm, 1) | |
| 321 | SN_ARGV_TYPEDARRAY(pk, 2) | |
| 322 | ||
| 323 | SN_THROWS(m_size != sm_size - crypto_sign_BYTES, "m must be 'sm.byteLength - crypto_sign_BYTES' bytes") | |
| 324 | SN_ASSERT_MIN_LENGTH(sm_size, crypto_sign_BYTES, "sm") | |
| 325 | SN_ASSERT_LENGTH(pk_size, crypto_sign_PUBLICKEYBYTES, "pk") | |
| 326 | ||
| 327 | SN_RETURN_BOOLEAN(crypto_sign_open(m_data, NULL, sm_data, sm_size, pk_data)) | |
| 328 | } | |
| 329 | ||
| 330 | napi_value sn_crypto_sign_detached(napi_env env, napi_callback_info info) { | |
| 331 | SN_ARGV(3, crypto_sign_detached) | |
| 332 | ||
| 333 | SN_ARGV_TYPEDARRAY(sig, 0) | |
| 334 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 335 | SN_ARGV_TYPEDARRAY(sk, 2) | |
| 336 | ||
| 337 | SN_ASSERT_LENGTH(sig_size, crypto_sign_BYTES, "sm") | |
| 338 | SN_ASSERT_LENGTH(sk_size, crypto_sign_SECRETKEYBYTES, "sk") | |
| 339 | ||
| 340 | SN_RETURN(crypto_sign_detached(sig_data, NULL, m_data, m_size, sk_data), "signature failed") | |
| 341 | } | |
| 342 | ||
| 343 | napi_value sn_crypto_sign_verify_detached(napi_env env, napi_callback_info info) { | |
| 344 | SN_ARGV(3, crypto_sign_verify_detached) | |
| 345 | ||
| 346 | SN_ARGV_TYPEDARRAY(sig, 0) | |
| 347 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 348 | SN_ARGV_TYPEDARRAY(pk, 2) | |
| 349 | ||
| 350 | SN_ASSERT_MIN_LENGTH(sig_size, crypto_sign_BYTES, "sig") | |
| 351 | SN_ASSERT_LENGTH(pk_size, crypto_sign_PUBLICKEYBYTES, "pk") | |
| 352 | ||
| 353 | SN_RETURN_BOOLEAN(crypto_sign_verify_detached(sig_data, m_data, m_size, pk_data)) | |
| 354 | } | |
| 355 | ||
| 356 | napi_value sn_crypto_sign_ed25519_sk_to_pk(napi_env env, napi_callback_info info) { | |
| 357 | SN_ARGV(2, crypto_sign_ed25519_sk_to_pk) | |
| 358 | ||
| 359 | SN_ARGV_TYPEDARRAY(pk, 0) | |
| 360 | SN_ARGV_TYPEDARRAY(sk, 1) | |
| 361 | ||
| 362 | SN_ASSERT_LENGTH(pk_size, crypto_sign_PUBLICKEYBYTES, "pk") | |
| 363 | SN_ASSERT_LENGTH(sk_size, crypto_sign_SECRETKEYBYTES, "sk") | |
| 364 | ||
| 365 | SN_RETURN(crypto_sign_ed25519_sk_to_pk(pk_data, sk_data), "public key generation failed") | |
| 366 | } | |
| 367 | ||
| 368 | napi_value sn_crypto_sign_ed25519_pk_to_curve25519(napi_env env, napi_callback_info info) { | |
| 369 | SN_ARGV(2, crypto_sign_ed25519_sk_to_pk) | |
| 370 | ||
| 371 | SN_ARGV_TYPEDARRAY(x25519_pk, 0) | |
| 372 | SN_ARGV_TYPEDARRAY(ed25519_pk, 1) | |
| 373 | ||
| 374 | SN_ASSERT_LENGTH(x25519_pk_size, crypto_box_PUBLICKEYBYTES, "x25519_pk") | |
| 375 | SN_ASSERT_LENGTH(ed25519_pk_size, crypto_sign_PUBLICKEYBYTES, "ed25519_pk") | |
| 376 | ||
| 377 | SN_RETURN(crypto_sign_ed25519_pk_to_curve25519(x25519_pk_data, ed25519_pk_data), "public key conversion failed") | |
| 378 | } | |
| 379 | ||
| 380 | napi_value sn_crypto_sign_ed25519_sk_to_curve25519(napi_env env, napi_callback_info info) { | |
| 381 | SN_ARGV(2, crypto_sign_ed25519_sk_to_pk) | |
| 382 | ||
| 383 | SN_ARGV_TYPEDARRAY(x25519_sk, 0) | |
| 384 | SN_ARGV_TYPEDARRAY(ed25519_sk, 1) | |
| 385 | ||
| 386 | SN_ASSERT_LENGTH(x25519_sk_size, crypto_box_SECRETKEYBYTES, "x25519_sk") | |
| 387 | SN_THROWS(ed25519_sk_size != crypto_sign_SECRETKEYBYTES && ed25519_sk_size != crypto_box_SECRETKEYBYTES, "ed25519_sk should either be 'crypto_sign_SECRETKEYBYTES' bytes or 'crypto_sign_SECRETKEYBYTES - crypto_sign_PUBLICKEYBYTES' bytes") | |
| 388 | ||
| 389 | SN_RETURN(crypto_sign_ed25519_sk_to_curve25519(x25519_sk_data, ed25519_sk_data), "secret key conversion failed") | |
| 390 | } | |
| 391 | ||
| 392 | napi_value sn_crypto_generichash(napi_env env, napi_callback_info info) { | |
| 393 | SN_ARGV_OPTS(2, 3, crypto_generichash) | |
| 394 | ||
| 395 | SN_ARGV_TYPEDARRAY(out, 0) | |
| 396 | SN_ARGV_TYPEDARRAY(in, 1) | |
| 397 | ||
| 398 | SN_ASSERT_MIN_LENGTH(out_size, crypto_generichash_BYTES_MIN, "out") | |
| 399 | SN_ASSERT_MAX_LENGTH(out_size, crypto_generichash_BYTES_MAX, "out") | |
| 400 | ||
| 401 | void *key_data = NULL; | |
| 402 | size_t key_size = 0; | |
| 403 | ||
| 404 | if (argc == 3) { | |
| 405 | SN_OPT_ARGV_TYPEDARRAY(key, 2) | |
| 406 | SN_THROWS(key_size < crypto_generichash_KEYBYTES_MIN, "key") | |
| 407 | SN_ASSERT_MIN_LENGTH(key_size, crypto_generichash_KEYBYTES_MIN, "key") | |
| 408 | SN_ASSERT_MAX_LENGTH(key_size, crypto_generichash_KEYBYTES_MAX, "key") | |
| 409 | } | |
| 410 | ||
| 411 | SN_RETURN(crypto_generichash(out_data, out_size, in_data, in_size, key_data, key_size), "hash failed") | |
| 412 | } | |
| 413 | ||
| 414 | napi_value sn_crypto_generichash_batch(napi_env env, napi_callback_info info) { | |
| 415 | SN_ARGV_OPTS(2, 3, crypto_generichash) | |
| 416 | ||
| 417 | SN_ARGV_TYPEDARRAY(out, 0) | |
| 418 | ||
| 419 | uint32_t batch_length; | |
| 420 | napi_get_array_length(env, argv[1], &batch_length); | |
| 421 | ||
| 422 | SN_ASSERT_MIN_LENGTH(out_size, crypto_generichash_BYTES_MIN, "out") | |
| 423 | SN_ASSERT_MAX_LENGTH(out_size, crypto_generichash_BYTES_MAX, "out") | |
| 424 | ||
| 425 | void *key_data = NULL; | |
| 426 | size_t key_size = 0; | |
| 427 | ||
| 428 | if (argc == 3) { | |
| 429 | SN_OPT_ARGV_TYPEDARRAY(key, 2) | |
| 430 | SN_ASSERT_MIN_LENGTH(key_size, crypto_generichash_KEYBYTES_MIN, "key") | |
| 431 | SN_ASSERT_MAX_LENGTH(key_size, crypto_generichash_KEYBYTES_MAX, "key") | |
| 432 | } | |
| 433 | ||
| 434 | crypto_generichash_state state; | |
| 435 | crypto_generichash_init(&state, key_data, key_size, out_size); | |
| 436 | for (uint32_t i = 0; i < batch_length; i++) { | |
| 437 | napi_value element; | |
| 438 | napi_get_element(env, argv[1], i, &element); | |
| 439 | SN_TYPEDARRAY_ASSERT(buf, element, "batch element should be passed as a TypedArray") | |
| 440 | SN_TYPEDARRAY(buf, element) | |
| 441 | crypto_generichash_update(&state, buf_data, buf_size); | |
| 442 | } | |
| 443 | ||
| 444 | SN_RETURN(crypto_generichash_final(&state, out_data, out_size), "batch failed") | |
| 445 | } | |
| 446 | ||
| 447 | napi_value sn_crypto_generichash_keygen(napi_env env, napi_callback_info info) { | |
| 448 | SN_ARGV(1, crypto_generichash_keygen) | |
| 449 | ||
| 450 | SN_ARGV_TYPEDARRAY(key, 0) | |
| 451 | ||
| 452 | SN_ASSERT_LENGTH(key_size, crypto_generichash_KEYBYTES, "key") | |
| 453 | ||
| 454 | crypto_generichash_keygen(key_data); | |
| 455 | ||
| 456 | return NULL; | |
| 457 | } | |
| 458 | ||
| 459 | napi_value sn_crypto_generichash_init(napi_env env, napi_callback_info info) { | |
| 460 | SN_ARGV(3, crypto_generichash_init) | |
| 461 | ||
| 462 | SN_ARGV_BUFFER_CAST(crypto_generichash_state *, state, 0) | |
| 463 | SN_ARGV_OPTS_TYPEDARRAY(key, 1) | |
| 464 | SN_ARGV_UINT32(outlen, 2) | |
| 465 | ||
| 466 | SN_THROWS(state_size != sizeof(crypto_generichash_state), "state must be 'crypto_generichash_STATEBYTES' bytes") | |
| 467 | ||
| 468 | if (key_data != NULL) { | |
| 469 | SN_ASSERT_MIN_LENGTH(key_size, crypto_generichash_KEYBYTES_MIN, "key") | |
| 470 | SN_ASSERT_MAX_LENGTH(key_size, crypto_generichash_KEYBYTES_MAX, "key") | |
| 471 | } | |
| 472 | ||
| 473 | SN_RETURN(crypto_generichash_init(state, key_data, key_size, outlen), "hash failed to initialise") | |
| 474 | } | |
| 475 | ||
| 476 | napi_value sn_crypto_generichash_update(napi_env env, napi_callback_info info) { | |
| 477 | SN_ARGV(2, crypto_generichash_update) | |
| 478 | ||
| 479 | SN_ARGV_BUFFER_CAST(crypto_generichash_state *, state, 0) | |
| 480 | SN_ARGV_TYPEDARRAY(in, 1) | |
| 481 | ||
| 482 | SN_THROWS(state_size != sizeof(crypto_generichash_state), "state must be 'crypto_generichash_STATEBYTES' bytes") | |
| 483 | ||
| 484 | SN_RETURN(crypto_generichash_update(state, in_data, in_size), "update failed") | |
| 485 | } | |
| 486 | ||
| 487 | napi_value sn_crypto_generichash_final(napi_env env, napi_callback_info info) { | |
| 488 | SN_ARGV(2, crypto_generichash_final) | |
| 489 | ||
| 490 | SN_ARGV_BUFFER_CAST(crypto_generichash_state *, state, 0) | |
| 491 | SN_ARGV_TYPEDARRAY(out, 1) | |
| 492 | ||
| 493 | SN_THROWS(state_size != sizeof(crypto_generichash_state), "state must be 'crypto_generichash_STATEBYTES' bytes") | |
| 494 | ||
| 495 | SN_RETURN(crypto_generichash_final(state, out_data, out_size), "digest failed") | |
| 496 | } | |
| 497 | ||
| 498 | napi_value sn_crypto_box_keypair(napi_env env, napi_callback_info info) { | |
| 499 | SN_ARGV(2, crypto_box_keypair) | |
| 500 | ||
| 501 | SN_ARGV_TYPEDARRAY(pk, 0) | |
| 502 | SN_ARGV_TYPEDARRAY(sk, 1) | |
| 503 | ||
| 504 | SN_ASSERT_LENGTH(pk_size, crypto_box_PUBLICKEYBYTES, "pk") | |
| 505 | SN_ASSERT_LENGTH(sk_size, crypto_box_SECRETKEYBYTES, "sk") | |
| 506 | ||
| 507 | SN_RETURN(crypto_box_keypair(pk_data, sk_data), "keypair generation failed") | |
| 508 | } | |
| 509 | ||
| 510 | napi_value sn_crypto_box_seed_keypair(napi_env env, napi_callback_info info) { | |
| 511 | SN_ARGV(3, crypto_box_seed_keypair) | |
| 512 | ||
| 513 | SN_ARGV_TYPEDARRAY(pk, 0) | |
| 514 | SN_ARGV_TYPEDARRAY(sk, 1) | |
| 515 | SN_ARGV_TYPEDARRAY(seed, 2) | |
| 516 | ||
| 517 | SN_ASSERT_LENGTH(pk_size, crypto_box_PUBLICKEYBYTES, "pk") | |
| 518 | SN_ASSERT_LENGTH(sk_size, crypto_box_SECRETKEYBYTES, "sk") | |
| 519 | SN_ASSERT_LENGTH(seed_size, crypto_box_SEEDBYTES, "seed") | |
| 520 | ||
| 521 | SN_RETURN(crypto_box_seed_keypair(pk_data, sk_data, seed_data), "keypair generation failed") | |
| 522 | } | |
| 523 | ||
| 524 | napi_value sn_crypto_box_easy(napi_env env, napi_callback_info info) { | |
| 525 | SN_ARGV(5, crypto_box_easy) | |
| 526 | ||
| 527 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 528 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 529 | SN_ARGV_TYPEDARRAY(n, 2) | |
| 530 | SN_ARGV_TYPEDARRAY(pk, 3) | |
| 531 | SN_ARGV_TYPEDARRAY(sk, 4) | |
| 532 | ||
| 533 | SN_THROWS(c_size != m_size + crypto_box_MACBYTES, "c must be 'm.byteLength + crypto_box_MACBYTES' bytes") | |
| 534 | SN_ASSERT_LENGTH(n_size, crypto_box_NONCEBYTES, "n") | |
| 535 | SN_ASSERT_LENGTH(sk_size, crypto_box_SECRETKEYBYTES, "sk") | |
| 536 | SN_ASSERT_LENGTH(pk_size, crypto_box_PUBLICKEYBYTES, "pk") | |
| 537 | ||
| 538 | SN_RETURN(crypto_box_easy(c_data, m_data, m_size, n_data, pk_data, sk_data), "crypto box failed") | |
| 539 | } | |
| 540 | ||
| 541 | napi_value sn_crypto_box_open_easy(napi_env env, napi_callback_info info) { | |
| 542 | SN_ARGV(5, crypto_box_open_easy) | |
| 543 | ||
| 544 | SN_ARGV_TYPEDARRAY(m, 0) | |
| 545 | SN_ARGV_TYPEDARRAY(c, 1) | |
| 546 | SN_ARGV_TYPEDARRAY(n, 2) | |
| 547 | SN_ARGV_TYPEDARRAY(pk, 3) | |
| 548 | SN_ARGV_TYPEDARRAY(sk, 4) | |
| 549 | ||
| 550 | SN_THROWS(m_size != c_size - crypto_box_MACBYTES, "m must be 'c.byteLength - crypto_box_MACBYTES' bytes") | |
| 551 | SN_ASSERT_MIN_LENGTH(c_size, crypto_box_MACBYTES, "c") | |
| 552 | SN_ASSERT_LENGTH(n_size, crypto_box_NONCEBYTES, "n") | |
| 553 | SN_ASSERT_LENGTH(sk_size, crypto_box_SECRETKEYBYTES, "sk") | |
| 554 | SN_ASSERT_LENGTH(pk_size, crypto_box_PUBLICKEYBYTES, "pk") | |
| 555 | ||
| 556 | SN_RETURN_BOOLEAN(crypto_box_open_easy(m_data, c_data, c_size, n_data, pk_data, sk_data)) | |
| 557 | } | |
| 558 | ||
| 559 | napi_value sn_crypto_box_detached(napi_env env, napi_callback_info info) { | |
| 560 | SN_ARGV(6, crypto_box_detached) | |
| 561 | ||
| 562 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 563 | SN_ARGV_TYPEDARRAY(mac, 1) | |
| 564 | SN_ARGV_TYPEDARRAY(m, 2) | |
| 565 | SN_ARGV_TYPEDARRAY(n, 3) | |
| 566 | SN_ARGV_TYPEDARRAY(pk, 4) | |
| 567 | SN_ARGV_TYPEDARRAY(sk, 5) | |
| 568 | ||
| 569 | SN_THROWS(c_size != m_size, "c must be 'm.byteLength' bytes") | |
| 570 | SN_ASSERT_LENGTH(mac_size, crypto_box_MACBYTES, "mac") | |
| 571 | SN_ASSERT_LENGTH(n_size, crypto_box_NONCEBYTES, "n") | |
| 572 | SN_ASSERT_LENGTH(sk_size, crypto_box_SECRETKEYBYTES, "sk") | |
| 573 | SN_ASSERT_LENGTH(pk_size, crypto_box_PUBLICKEYBYTES, "pk") | |
| 574 | ||
| 575 | SN_RETURN(crypto_box_detached(c_data, mac_data, m_data, m_size, n_data, pk_data, sk_data), "signature failed") | |
| 576 | } | |
| 577 | ||
| 578 | napi_value sn_crypto_box_open_detached(napi_env env, napi_callback_info info) { | |
| 579 | SN_ARGV(6, crypto_box_open_detached) | |
| 580 | ||
| 581 | SN_ARGV_TYPEDARRAY(m, 0) | |
| 582 | SN_ARGV_TYPEDARRAY(c, 1) | |
| 583 | SN_ARGV_TYPEDARRAY(mac, 2) | |
| 584 | SN_ARGV_TYPEDARRAY(n, 3) | |
| 585 | SN_ARGV_TYPEDARRAY(pk, 4) | |
| 586 | SN_ARGV_TYPEDARRAY(sk, 5) | |
| 587 | ||
| 588 | SN_THROWS(m_size != c_size, "m must be 'c.byteLength' bytes") | |
| 589 | SN_ASSERT_LENGTH(mac_size, crypto_box_MACBYTES, "mac") | |
| 590 | SN_ASSERT_LENGTH(n_size, crypto_box_NONCEBYTES, "n") | |
| 591 | SN_ASSERT_LENGTH(sk_size, crypto_box_SECRETKEYBYTES, "sk") | |
| 592 | SN_ASSERT_LENGTH(pk_size, crypto_box_PUBLICKEYBYTES, "pk") | |
| 593 | ||
| 594 | SN_RETURN_BOOLEAN(crypto_box_open_detached(m_data, c_data, mac_data, c_size, n_data, pk_data, sk_data)) | |
| 595 | } | |
| 596 | ||
| 597 | napi_value sn_crypto_box_seal(napi_env env, napi_callback_info info) { | |
| 598 | SN_ARGV(3, crypto_box_seal) | |
| 599 | ||
| 600 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 601 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 602 | SN_ARGV_TYPEDARRAY(pk, 2) | |
| 603 | ||
| 604 | SN_THROWS(c_size != m_size + crypto_box_SEALBYTES, "c must be 'm.byteLength + crypto_box_SEALBYTES' bytes") | |
| 605 | SN_ASSERT_LENGTH(pk_size, crypto_box_PUBLICKEYBYTES, "pk") | |
| 606 | ||
| 607 | SN_RETURN(crypto_box_seal(c_data, m_data, m_size, pk_data), "failed to create seal") | |
| 608 | } | |
| 609 | ||
| 610 | napi_value sn_crypto_box_seal_open(napi_env env, napi_callback_info info) { | |
| 611 | SN_ARGV(4, crypto_box_seal_open) | |
| 612 | ||
| 613 | SN_ARGV_TYPEDARRAY(m, 0) | |
| 614 | SN_ARGV_TYPEDARRAY(c, 1) | |
| 615 | SN_ARGV_TYPEDARRAY(pk, 2) | |
| 616 | SN_ARGV_TYPEDARRAY(sk, 3) | |
| 617 | ||
| 618 | SN_THROWS(m_size != c_size - crypto_box_SEALBYTES, "m must be 'c.byteLength - crypto_box_SEALBYTES' bytes") | |
| 619 | SN_ASSERT_MIN_LENGTH(c_size, crypto_box_SEALBYTES, "c") | |
| 620 | SN_ASSERT_LENGTH(sk_size, crypto_box_SECRETKEYBYTES, "sk") | |
| 621 | SN_ASSERT_LENGTH(pk_size, crypto_box_PUBLICKEYBYTES, "pk") | |
| 622 | ||
| 623 | SN_RETURN_BOOLEAN(crypto_box_seal_open(m_data, c_data, c_size, pk_data, sk_data)) | |
| 624 | } | |
| 625 | ||
| 626 | napi_value sn_crypto_secretbox_easy(napi_env env, napi_callback_info info) { | |
| 627 | SN_ARGV(4, crypto_secretbox_easy) | |
| 628 | ||
| 629 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 630 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 631 | SN_ARGV_TYPEDARRAY(n, 2) | |
| 632 | SN_ARGV_TYPEDARRAY(k, 3) | |
| 633 | ||
| 634 | SN_THROWS(c_size != m_size + crypto_secretbox_MACBYTES, "c must be 'm.byteLength + crypto_secretbox_MACBYTES' bytes") | |
| 635 | SN_ASSERT_LENGTH(n_size, crypto_secretbox_NONCEBYTES, "n") | |
| 636 | SN_ASSERT_LENGTH(k_size, crypto_secretbox_KEYBYTES, "k") | |
| 637 | ||
| 638 | SN_RETURN(crypto_secretbox_easy(c_data, m_data, m_size, n_data, k_data), "crypto secretbox failed") | |
| 639 | } | |
| 640 | ||
| 641 | napi_value sn_crypto_secretbox_open_easy(napi_env env, napi_callback_info info) { | |
| 642 | SN_ARGV(4, crypto_secretbox_open_easy) | |
| 643 | ||
| 644 | SN_ARGV_TYPEDARRAY(m, 0) | |
| 645 | SN_ARGV_TYPEDARRAY(c, 1) | |
| 646 | SN_ARGV_TYPEDARRAY(n, 2) | |
| 647 | SN_ARGV_TYPEDARRAY(k, 3) | |
| 648 | ||
| 649 | SN_THROWS(m_size != c_size - crypto_secretbox_MACBYTES, "m must be 'c - crypto_secretbox_MACBYTES' bytes") | |
| 650 | SN_ASSERT_MIN_LENGTH(c_size, crypto_secretbox_MACBYTES, "c") | |
| 651 | SN_ASSERT_LENGTH(n_size, crypto_secretbox_NONCEBYTES, "n") | |
| 652 | SN_ASSERT_LENGTH(k_size, crypto_secretbox_KEYBYTES, "k") | |
| 653 | ||
| 654 | SN_RETURN_BOOLEAN(crypto_secretbox_open_easy(m_data, c_data, c_size, n_data, k_data)) | |
| 655 | } | |
| 656 | ||
| 657 | napi_value sn_crypto_secretbox_detached(napi_env env, napi_callback_info info) { | |
| 658 | SN_ARGV(5, crypto_secretbox_detached) | |
| 659 | ||
| 660 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 661 | SN_ARGV_TYPEDARRAY(mac, 1) | |
| 662 | SN_ARGV_TYPEDARRAY(m, 2) | |
| 663 | SN_ARGV_TYPEDARRAY(n, 3) | |
| 664 | SN_ARGV_TYPEDARRAY(k, 4) | |
| 665 | ||
| 666 | SN_THROWS(c_size != m_size, "c must 'm.byteLength' bytes") | |
| 667 | SN_ASSERT_LENGTH(mac_size, crypto_secretbox_MACBYTES, "mac") | |
| 668 | SN_ASSERT_LENGTH(n_size, crypto_secretbox_NONCEBYTES, "n") | |
| 669 | SN_ASSERT_LENGTH(k_size, crypto_secretbox_KEYBYTES, "k") | |
| 670 | ||
| 671 | SN_RETURN(crypto_secretbox_detached(c_data, mac_data, m_data, m_size, n_data, k_data), "failed to open box") | |
| 672 | } | |
| 673 | ||
| 674 | napi_value sn_crypto_secretbox_open_detached(napi_env env, napi_callback_info info) { | |
| 675 | SN_ARGV(5, crypto_secretbox_open_detached) | |
| 676 | ||
| 677 | SN_ARGV_TYPEDARRAY(m, 0) | |
| 678 | SN_ARGV_TYPEDARRAY(c, 1) | |
| 679 | SN_ARGV_TYPEDARRAY(mac, 2) | |
| 680 | SN_ARGV_TYPEDARRAY(n, 3) | |
| 681 | SN_ARGV_TYPEDARRAY(k, 4) | |
| 682 | ||
| 683 | SN_THROWS(m_size != c_size, "m must be 'c.byteLength' bytes") | |
| 684 | SN_ASSERT_LENGTH(mac_size, crypto_secretbox_MACBYTES, "mac") | |
| 685 | SN_ASSERT_LENGTH(n_size, crypto_secretbox_NONCEBYTES, "n") | |
| 686 | SN_ASSERT_LENGTH(k_size, crypto_secretbox_KEYBYTES, "k") | |
| 687 | ||
| 688 | SN_RETURN_BOOLEAN(crypto_secretbox_open_detached(m_data, c_data, mac_data, c_size, n_data, k_data)) | |
| 689 | } | |
| 690 | ||
| 691 | napi_value sn_crypto_stream(napi_env env, napi_callback_info info) { | |
| 692 | SN_ARGV(3, crypto_stream) | |
| 693 | ||
| 694 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 695 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 696 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 697 | ||
| 698 | SN_ASSERT_LENGTH(n_size, crypto_stream_NONCEBYTES, "n") | |
| 699 | SN_ASSERT_LENGTH(k_size, crypto_stream_KEYBYTES, "k") | |
| 700 | ||
| 701 | SN_RETURN(crypto_stream(c_data, c_size, n_data, k_data), "stream encryption failed") | |
| 702 | } | |
| 703 | ||
| 704 | napi_value sn_crypto_stream_xor(napi_env env, napi_callback_info info) { | |
| 705 | SN_ARGV(4, crypto_stream_xor) | |
| 706 | ||
| 707 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 708 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 709 | SN_ARGV_TYPEDARRAY(n, 2) | |
| 710 | SN_ARGV_TYPEDARRAY(k, 3) | |
| 711 | ||
| 712 | SN_THROWS(c_size != m_size, "m must be 'c.byteLength' bytes") | |
| 713 | SN_ASSERT_LENGTH(n_size, crypto_stream_NONCEBYTES, "n") | |
| 714 | SN_ASSERT_LENGTH(k_size, crypto_stream_KEYBYTES, "k") | |
| 715 | ||
| 716 | SN_RETURN(crypto_stream_xor(c_data, m_data, m_size, n_data, k_data), "stream encryption failed") | |
| 717 | } | |
| 718 | ||
| 719 | napi_value sn_crypto_stream_chacha20(napi_env env, napi_callback_info info) { | |
| 720 | SN_ARGV(3, crypto_stream_chacha20) | |
| 721 | ||
| 722 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 723 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 724 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 725 | ||
| 726 | SN_ASSERT_LENGTH(n_size, crypto_stream_chacha20_NONCEBYTES, "n") | |
| 727 | SN_ASSERT_LENGTH(k_size, crypto_stream_chacha20_KEYBYTES, "k") | |
| 728 | ||
| 729 | SN_RETURN(crypto_stream_chacha20(c_data, c_size, n_data, k_data), "stream encryption failed") | |
| 730 | } | |
| 731 | ||
| 732 | napi_value sn_crypto_stream_chacha20_xor (napi_env env, napi_callback_info info) { | |
| 733 | SN_ARGV(4, crypto_stream_chacha20_xor) | |
| 734 | ||
| 735 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 736 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 737 | SN_ARGV_TYPEDARRAY(n, 2) | |
| 738 | SN_ARGV_TYPEDARRAY(k, 3) | |
| 739 | ||
| 740 | SN_THROWS(c_size != m_size, "m must be 'c.byteLength' bytes") | |
| 741 | SN_ASSERT_LENGTH(n_size, crypto_stream_chacha20_NONCEBYTES, "n") | |
| 742 | SN_ASSERT_LENGTH(k_size, crypto_stream_chacha20_KEYBYTES, "k") | |
| 743 | ||
| 744 | SN_RETURN(crypto_stream_chacha20_xor(c_data, m_data, m_size, n_data, k_data), "stream encryption failed") | |
| 745 | } | |
| 746 | ||
| 747 | napi_value sn_crypto_stream_chacha20_xor_ic(napi_env env, napi_callback_info info) { | |
| 748 | SN_ARGV(5, crypto_stream_chacha20_xor_ic) | |
| 749 | ||
| 750 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 751 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 752 | SN_ARGV_TYPEDARRAY(n, 2) | |
| 753 | SN_ARGV_UINT32(ic, 3) | |
| 754 | SN_ARGV_TYPEDARRAY(k, 4) | |
| 755 | ||
| 756 | SN_THROWS(c_size != m_size, "m must be 'c.byteLength' bytes") | |
| 757 | SN_ASSERT_LENGTH(n_size, crypto_stream_chacha20_NONCEBYTES, "n") | |
| 758 | SN_ASSERT_LENGTH(k_size, crypto_stream_chacha20_KEYBYTES, "k") | |
| 759 | ||
| 760 | SN_RETURN(crypto_stream_chacha20_xor_ic(c_data, m_data, m_size, n_data, ic, k_data), "stream encryption failed") | |
| 761 | } | |
| 762 | ||
| 763 | napi_value sn_crypto_stream_chacha20_ietf(napi_env env, napi_callback_info info) { | |
| 764 | SN_ARGV(3, crypto_stream_chacha20_ietf) | |
| 765 | ||
| 766 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 767 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 768 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 769 | ||
| 770 | SN_ASSERT_LENGTH(n_size, crypto_stream_chacha20_ietf_NONCEBYTES, "n") | |
| 771 | SN_ASSERT_LENGTH(k_size, crypto_stream_chacha20_ietf_KEYBYTES, "k") | |
| 772 | ||
| 773 | SN_RETURN(crypto_stream_chacha20_ietf(c_data, c_size, n_data, k_data), "stream encryption failed") | |
| 774 | } | |
| 775 | ||
| 776 | napi_value sn_crypto_stream_chacha20_ietf_xor(napi_env env, napi_callback_info info) { | |
| 777 | SN_ARGV(4, crypto_stream_chacha20_ietf_xor) | |
| 778 | ||
| 779 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 780 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 781 | SN_ARGV_TYPEDARRAY(n, 2) | |
| 782 | SN_ARGV_TYPEDARRAY(k, 3) | |
| 783 | ||
| 784 | SN_THROWS(c_size != m_size, "m must be 'c.byteLength' bytes") | |
| 785 | SN_ASSERT_LENGTH(n_size, crypto_stream_chacha20_ietf_NONCEBYTES, "n") | |
| 786 | SN_ASSERT_LENGTH(k_size, crypto_stream_chacha20_ietf_KEYBYTES, "k") | |
| 787 | ||
| 788 | SN_RETURN(crypto_stream_chacha20_ietf_xor(c_data, m_data, m_size, n_data, k_data), "stream encryption failed") | |
| 789 | } | |
| 790 | ||
| 791 | napi_value sn_crypto_stream_chacha20_ietf_xor_ic(napi_env env, napi_callback_info info) { | |
| 792 | SN_ARGV(5, crypto_stream_chacha20_ietf_xor_ic) | |
| 793 | ||
| 794 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 795 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 796 | SN_ARGV_TYPEDARRAY(n, 2) | |
| 797 | SN_ARGV_UINT32(ic, 3) | |
| 798 | SN_ARGV_TYPEDARRAY(k, 4) | |
| 799 | ||
| 800 | SN_THROWS(c_size != m_size, "m must be 'c.byteLength' bytes") | |
| 801 | SN_ASSERT_LENGTH(n_size, crypto_stream_chacha20_ietf_NONCEBYTES, "n") | |
| 802 | SN_ASSERT_LENGTH(k_size, crypto_stream_chacha20_ietf_KEYBYTES, "k") | |
| 803 | ||
| 804 | SN_RETURN(crypto_stream_chacha20_ietf_xor_ic(c_data, m_data, m_size, n_data, ic, k_data), "stream encryption failed") | |
| 805 | } | |
| 806 | ||
| 807 | napi_value sn_crypto_stream_xchacha20(napi_env env, napi_callback_info info) { | |
| 808 | SN_ARGV(3, crypto_stream_xchacha20) | |
| 809 | ||
| 810 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 811 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 812 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 813 | ||
| 814 | SN_ASSERT_LENGTH(n_size, crypto_stream_xchacha20_NONCEBYTES, "n") | |
| 815 | SN_ASSERT_LENGTH(k_size, crypto_stream_xchacha20_KEYBYTES, "k") | |
| 816 | ||
| 817 | SN_RETURN(crypto_stream_xchacha20(c_data, c_size, n_data, k_data), "stream encryption failed") | |
| 818 | } | |
| 819 | ||
| 820 | napi_value sn_crypto_stream_xchacha20_xor (napi_env env, napi_callback_info info) { | |
| 821 | SN_ARGV(4, crypto_stream_xchacha20_xor) | |
| 822 | ||
| 823 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 824 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 825 | SN_ARGV_TYPEDARRAY(n, 2) | |
| 826 | SN_ARGV_TYPEDARRAY(k, 3) | |
| 827 | ||
| 828 | SN_THROWS(c_size != m_size, "m must be 'c.byteLength' bytes") | |
| 829 | SN_ASSERT_LENGTH(n_size, crypto_stream_xchacha20_NONCEBYTES, "n") | |
| 830 | SN_ASSERT_LENGTH(k_size, crypto_stream_xchacha20_KEYBYTES, "k") | |
| 831 | ||
| 832 | SN_RETURN(crypto_stream_xchacha20_xor(c_data, m_data, m_size, n_data, k_data), "stream encryption failed") | |
| 833 | } | |
| 834 | ||
| 835 | napi_value sn_crypto_stream_xchacha20_xor_ic(napi_env env, napi_callback_info info) { | |
| 836 | SN_ARGV(5, crypto_stream_xchacha20_xor_ic) | |
| 837 | ||
| 838 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 839 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 840 | SN_ARGV_TYPEDARRAY(n, 2) | |
| 841 | SN_ARGV_UINT32(ic, 3) | |
| 842 | SN_ARGV_TYPEDARRAY(k, 4) | |
| 843 | ||
| 844 | SN_THROWS(c_size != m_size, "m must be 'c.byteLength' bytes") | |
| 845 | SN_ASSERT_LENGTH(n_size, crypto_stream_xchacha20_NONCEBYTES, "n") | |
| 846 | SN_ASSERT_LENGTH(k_size, crypto_stream_xchacha20_KEYBYTES, "k") | |
| 847 | ||
| 848 | SN_RETURN(crypto_stream_xchacha20_xor_ic(c_data, m_data, m_size, n_data, ic, k_data), "stream encryption failed") | |
| 849 | } | |
| 850 | ||
| 851 | napi_value sn_crypto_stream_salsa20(napi_env env, napi_callback_info info) { | |
| 852 | SN_ARGV(3, crypto_stream_salsa20) | |
| 853 | ||
| 854 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 855 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 856 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 857 | ||
| 858 | SN_ASSERT_LENGTH(n_size, crypto_stream_salsa20_NONCEBYTES, "n") | |
| 859 | SN_ASSERT_LENGTH(k_size, crypto_stream_salsa20_KEYBYTES, "k") | |
| 860 | ||
| 861 | SN_RETURN(crypto_stream_salsa20(c_data, c_size, n_data, k_data), "stream encryption failed") | |
| 862 | } | |
| 863 | ||
| 864 | napi_value sn_crypto_stream_salsa20_xor (napi_env env, napi_callback_info info) { | |
| 865 | SN_ARGV(4, crypto_stream_salsa20_xor) | |
| 866 | ||
| 867 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 868 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 869 | SN_ARGV_TYPEDARRAY(n, 2) | |
| 870 | SN_ARGV_TYPEDARRAY(k, 3) | |
| 871 | ||
| 872 | SN_THROWS(c_size != m_size, "m must be 'c.byteLength' bytes") | |
| 873 | SN_ASSERT_LENGTH(n_size, crypto_stream_salsa20_NONCEBYTES, "n") | |
| 874 | SN_ASSERT_LENGTH(k_size, crypto_stream_salsa20_KEYBYTES, "k") | |
| 875 | ||
| 876 | SN_RETURN(crypto_stream_salsa20_xor(c_data, m_data, m_size, n_data, k_data), "stream encryption failed") | |
| 877 | } | |
| 878 | ||
| 879 | napi_value sn_crypto_stream_salsa20_xor_ic(napi_env env, napi_callback_info info) { | |
| 880 | SN_ARGV(5, crypto_stream_salsa20_xor_ic) | |
| 881 | ||
| 882 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 883 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 884 | SN_ARGV_TYPEDARRAY(n, 2) | |
| 885 | SN_ARGV_UINT32(ic, 3) | |
| 886 | SN_ARGV_TYPEDARRAY(k, 4) | |
| 887 | ||
| 888 | SN_THROWS(c_size != m_size, "m must be 'c.byteLength' bytes") | |
| 889 | SN_ASSERT_LENGTH(n_size, crypto_stream_salsa20_NONCEBYTES, "n") | |
| 890 | SN_ASSERT_LENGTH(k_size, crypto_stream_salsa20_KEYBYTES, "k") | |
| 891 | ||
| 892 | SN_RETURN(crypto_stream_salsa20_xor_ic(c_data, m_data, m_size, n_data, ic, k_data), "stream encryption failed") | |
| 893 | } | |
| 894 | ||
| 895 | napi_value sn_crypto_auth (napi_env env, napi_callback_info info) { | |
| 896 | SN_ARGV(3, crypto_auth) | |
| 897 | ||
| 898 | SN_ARGV_TYPEDARRAY(out, 0) | |
| 899 | SN_ARGV_TYPEDARRAY(in, 1) | |
| 900 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 901 | ||
| 902 | SN_ASSERT_LENGTH(out_size, crypto_auth_BYTES, "out") | |
| 903 | SN_ASSERT_LENGTH(k_size, crypto_auth_KEYBYTES, "k") | |
| 904 | ||
| 905 | SN_RETURN(crypto_auth(out_data, in_data, in_size, k_data), "failed to generate authentication tag") | |
| 906 | } | |
| 907 | ||
| 908 | napi_value sn_crypto_auth_verify (napi_env env, napi_callback_info info) { | |
| 909 | SN_ARGV(3, crypto_auth_verify) | |
| 910 | ||
| 911 | SN_ARGV_TYPEDARRAY(h, 0) | |
| 912 | SN_ARGV_TYPEDARRAY(in, 1) | |
| 913 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 914 | ||
| 915 | SN_ASSERT_LENGTH(h_size, crypto_auth_BYTES, "h") | |
| 916 | SN_ASSERT_LENGTH(k_size, crypto_auth_KEYBYTES, "k") | |
| 917 | ||
| 918 | SN_RETURN_BOOLEAN(crypto_auth_verify(h_data, in_data, in_size, k_data)) | |
| 919 | } | |
| 920 | ||
| 921 | napi_value sn_crypto_onetimeauth (napi_env env, napi_callback_info info) { | |
| 922 | SN_ARGV(3, crypto_onetimeauth) | |
| 923 | ||
| 924 | SN_ARGV_TYPEDARRAY(out, 0) | |
| 925 | SN_ARGV_TYPEDARRAY(in, 1) | |
| 926 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 927 | ||
| 928 | SN_ASSERT_LENGTH(out_size, crypto_onetimeauth_BYTES, "out") | |
| 929 | SN_ASSERT_LENGTH(k_size, crypto_onetimeauth_KEYBYTES, "k") | |
| 930 | ||
| 931 | SN_RETURN(crypto_onetimeauth(out_data, in_data, in_size, k_data), "failed to generate onetime authentication tag") | |
| 932 | } | |
| 933 | ||
| 934 | napi_value sn_crypto_onetimeauth_init (napi_env env, napi_callback_info info) { | |
| 935 | SN_ARGV(2, crypto_onetimeauth_init) | |
| 936 | ||
| 937 | SN_ARGV_BUFFER_CAST(crypto_onetimeauth_state *, state, 0) | |
| 938 | SN_ARGV_TYPEDARRAY(k, 1) | |
| 939 | ||
| 940 | SN_THROWS(state_size != sizeof(crypto_onetimeauth_state), "state must be 'crypto_onetimeauth_STATEBYTES' bytes") | |
| 941 | SN_ASSERT_LENGTH(k_size, crypto_onetimeauth_KEYBYTES, "k") | |
| 942 | ||
| 943 | SN_RETURN(crypto_onetimeauth_init(state, k_data), "failed to initialise onetime authentication") | |
| 944 | } | |
| 945 | ||
| 946 | napi_value sn_crypto_onetimeauth_update(napi_env env, napi_callback_info info) { | |
| 947 | SN_ARGV(2, crypto_onetimeauth_update) | |
| 948 | ||
| 949 | SN_ARGV_BUFFER_CAST(crypto_onetimeauth_state *, state, 0) | |
| 950 | SN_ARGV_TYPEDARRAY(in, 1) | |
| 951 | ||
| 952 | SN_THROWS(state_size != sizeof(crypto_onetimeauth_state), "state must be 'crypto_onetimeauth_STATEBYTES' bytes") | |
| 953 | ||
| 954 | SN_RETURN(crypto_onetimeauth_update(state, in_data, in_size), "update failed") | |
| 955 | } | |
| 956 | ||
| 957 | napi_value sn_crypto_onetimeauth_final(napi_env env, napi_callback_info info) { | |
| 958 | SN_ARGV(2, crypto_onetimeauth_final) | |
| 959 | ||
| 960 | SN_ARGV_BUFFER_CAST(crypto_onetimeauth_state *, state, 0) | |
| 961 | SN_ARGV_TYPEDARRAY(out, 1) | |
| 962 | ||
| 963 | SN_THROWS(state_size != sizeof(crypto_onetimeauth_state), "state must be 'crypto_onetimeauth_STATEBYTES' bytes") | |
| 964 | SN_ASSERT_LENGTH(out_size, crypto_onetimeauth_BYTES, "out") | |
| 965 | ||
| 966 | SN_RETURN(crypto_onetimeauth_final(state, out_data), "failed to generate authentication tag") | |
| 967 | } | |
| 968 | ||
| 969 | napi_value sn_crypto_onetimeauth_verify (napi_env env, napi_callback_info info) { | |
| 970 | SN_ARGV(3, crypto_onetimeauth_verify) | |
| 971 | ||
| 972 | SN_ARGV_TYPEDARRAY(h, 0) | |
| 973 | SN_ARGV_TYPEDARRAY(in, 1) | |
| 974 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 975 | ||
| 976 | SN_ASSERT_LENGTH(h_size, crypto_onetimeauth_BYTES, "h") | |
| 977 | SN_ASSERT_LENGTH(k_size, crypto_onetimeauth_KEYBYTES, "k") | |
| 978 | ||
| 979 | SN_RETURN_BOOLEAN(crypto_onetimeauth_verify(h_data, in_data, in_size, k_data)) | |
| 980 | } | |
| 981 | ||
| 982 | // CHECK: memlimit can be >32bit | |
| 983 | napi_value sn_crypto_pwhash (napi_env env, napi_callback_info info) { | |
| 984 | SN_ARGV(6, crypto_pwhash) | |
| 985 | ||
| 986 | SN_ARGV_TYPEDARRAY(out, 0) | |
| 987 | SN_ARGV_TYPEDARRAY(passwd, 1) | |
| 988 | SN_ARGV_TYPEDARRAY(salt, 2) | |
| 989 | SN_ARGV_UINT64(opslimit, 3) | |
| 990 | SN_ARGV_UINT64(memlimit, 4) | |
| 991 | SN_ARGV_UINT8(alg, 5) | |
| 992 | ||
| 993 | SN_ASSERT_MIN_LENGTH(out_size, crypto_pwhash_BYTES_MIN, "out") | |
| 994 | SN_ASSERT_MAX_LENGTH(out_size, crypto_pwhash_BYTES_MAX, "out") | |
| 995 | SN_ASSERT_LENGTH(salt_size, crypto_pwhash_SALTBYTES, "salt") | |
| 996 | SN_ASSERT_MIN_LENGTH(opslimit, crypto_pwhash_OPSLIMIT_MIN, "opslimit") | |
| 997 | SN_ASSERT_MAX_LENGTH(opslimit, crypto_pwhash_OPSLIMIT_MAX, "opslimit") | |
| 998 | SN_ASSERT_MIN_LENGTH(memlimit, crypto_pwhash_MEMLIMIT_MIN, "memlimit") | |
| 999 | SN_ASSERT_MAX_LENGTH(memlimit, (int64_t) crypto_pwhash_MEMLIMIT_MAX, "memlimit") | |
| 1000 | SN_THROWS(alg < 1 || alg > 2, "alg must be either Argon2i 1.3 or Argon2id 1.3") | |
| 1001 | ||
| 1002 | SN_RETURN(crypto_pwhash(out_data, out_size, passwd_data, passwd_size, salt_data, opslimit, memlimit, alg), "password hashing failed, check memory requirements.") | |
| 1003 | } | |
| 1004 | ||
| 1005 | napi_value sn_crypto_pwhash_str (napi_env env, napi_callback_info info) { | |
| 1006 | SN_ARGV(4, crypto_pwhash_str) | |
| 1007 | ||
| 1008 | SN_ARGV_TYPEDARRAY(out, 0) | |
| 1009 | SN_ARGV_TYPEDARRAY(passwd, 1) | |
| 1010 | SN_ARGV_UINT64(opslimit, 2) | |
| 1011 | SN_ARGV_UINT64(memlimit, 3) | |
| 1012 | ||
| 1013 | SN_ASSERT_LENGTH(out_size, crypto_pwhash_STRBYTES, "out") | |
| 1014 | SN_ASSERT_MIN_LENGTH(opslimit, crypto_pwhash_OPSLIMIT_MIN, "opslimit") | |
| 1015 | SN_ASSERT_MAX_LENGTH(opslimit, crypto_pwhash_OPSLIMIT_MAX, "opslimit") | |
| 1016 | SN_ASSERT_MIN_LENGTH(memlimit, crypto_pwhash_MEMLIMIT_MIN, "memlimit") | |
| 1017 | SN_ASSERT_MAX_LENGTH(memlimit, (int64_t) crypto_pwhash_MEMLIMIT_MAX, "memlimit") | |
| 1018 | ||
| 1019 | SN_RETURN(crypto_pwhash_str(out_data, passwd_data, passwd_size, opslimit, memlimit), "password hashing failed, check memory requirements.") | |
| 1020 | } | |
| 1021 | ||
| 1022 | napi_value sn_crypto_pwhash_str_verify (napi_env env, napi_callback_info info) { | |
| 1023 | SN_ARGV(2, crypto_pwhash_str_verify) | |
| 1024 | ||
| 1025 | SN_ARGV_TYPEDARRAY(str, 0) | |
| 1026 | SN_ARGV_TYPEDARRAY(passwd, 1) | |
| 1027 | ||
| 1028 | SN_ASSERT_LENGTH(str_size, crypto_pwhash_STRBYTES, "str") | |
| 1029 | ||
| 1030 | SN_RETURN_BOOLEAN(crypto_pwhash_str_verify(str_data, passwd_data, passwd_size)) | |
| 1031 | } | |
| 1032 | ||
| 1033 | // CHECK: returns 1, 0, -1 | |
| 1034 | napi_value sn_crypto_pwhash_str_needs_rehash (napi_env env, napi_callback_info info) { | |
| 1035 | SN_ARGV(3, crypto_pwhash_str_needs_rehash) | |
| 1036 | ||
| 1037 | SN_ARGV_TYPEDARRAY(str, 0) | |
| 1038 | SN_ARGV_UINT64(opslimit, 1) | |
| 1039 | SN_ARGV_UINT64(memlimit, 2) | |
| 1040 | ||
| 1041 | SN_ASSERT_LENGTH(str_size, crypto_pwhash_STRBYTES, "str") | |
| 1042 | SN_ASSERT_MIN_LENGTH(opslimit, crypto_pwhash_OPSLIMIT_MIN, "opslimit") | |
| 1043 | SN_ASSERT_MAX_LENGTH(opslimit, crypto_pwhash_OPSLIMIT_MAX, "opslimit") | |
| 1044 | SN_ASSERT_MIN_LENGTH(memlimit, crypto_pwhash_MEMLIMIT_MIN, "memlimit") | |
| 1045 | SN_ASSERT_MAX_LENGTH(memlimit, (int64_t) crypto_pwhash_MEMLIMIT_MAX, "memlimit") | |
| 1046 | ||
| 1047 | SN_RETURN_BOOLEAN_FROM_1(crypto_pwhash_str_needs_rehash(str_data, opslimit, memlimit)) | |
| 1048 | } | |
| 1049 | ||
| 1050 | // CHECK: memlimit can be >32bit | |
| 1051 | napi_value sn_crypto_pwhash_scryptsalsa208sha256 (napi_env env, napi_callback_info info) { | |
| 1052 | SN_ARGV(5, crypto_pwhash_scryptsalsa208sha256) | |
| 1053 | ||
| 1054 | SN_ARGV_TYPEDARRAY(out, 0) | |
| 1055 | SN_ARGV_TYPEDARRAY(passwd, 1) | |
| 1056 | SN_ARGV_TYPEDARRAY(salt, 2) | |
| 1057 | SN_ARGV_UINT64(opslimit, 3) | |
| 1058 | SN_ARGV_UINT64(memlimit, 4) | |
| 1059 | ||
| 1060 | SN_ASSERT_MIN_LENGTH(out_size, crypto_pwhash_scryptsalsa208sha256_BYTES_MIN, "out") | |
| 1061 | SN_ASSERT_MAX_LENGTH(out_size, crypto_pwhash_scryptsalsa208sha256_BYTES_MAX, "out") | |
| 1062 | SN_ASSERT_LENGTH(salt_size, crypto_pwhash_scryptsalsa208sha256_SALTBYTES, "salt") | |
| 1063 | SN_ASSERT_MIN_LENGTH(opslimit, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, "opslimit") | |
| 1064 | SN_ASSERT_MAX_LENGTH(opslimit, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, "opslimit") | |
| 1065 | SN_ASSERT_MIN_LENGTH(memlimit, crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, "memlimit") | |
| 1066 | SN_ASSERT_MAX_LENGTH(memlimit, (int64_t) crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, "memlimit") | |
| 1067 | ||
| 1068 | SN_RETURN(crypto_pwhash_scryptsalsa208sha256(out_data, out_size, passwd_data, passwd_size, salt_data, opslimit, memlimit), "password hashing failed, check memory requirements.") | |
| 1069 | } | |
| 1070 | ||
| 1071 | napi_value sn_crypto_pwhash_scryptsalsa208sha256_str (napi_env env, napi_callback_info info) { | |
| 1072 | SN_ARGV(4, crypto_pwhash_scryptsalsa208sha256_str) | |
| 1073 | ||
| 1074 | SN_ARGV_TYPEDARRAY(out, 0) | |
| 1075 | SN_ARGV_TYPEDARRAY(passwd, 1) | |
| 1076 | SN_ARGV_UINT64(opslimit, 2) | |
| 1077 | SN_ARGV_UINT64(memlimit, 3) | |
| 1078 | ||
| 1079 | SN_ASSERT_LENGTH(out_size, crypto_pwhash_scryptsalsa208sha256_STRBYTES, "out") | |
| 1080 | SN_ASSERT_MIN_LENGTH(opslimit, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, "opslimit") | |
| 1081 | SN_ASSERT_MAX_LENGTH(opslimit, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, "opslimit") | |
| 1082 | SN_ASSERT_MIN_LENGTH(memlimit, crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, "memlimit") | |
| 1083 | SN_ASSERT_MAX_LENGTH(memlimit, (int64_t) crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, "memlimit") | |
| 1084 | ||
| 1085 | SN_RETURN(crypto_pwhash_scryptsalsa208sha256_str(out_data, passwd_data, passwd_size, opslimit, memlimit), "password hashing failed, check memory requirements.") | |
| 1086 | } | |
| 1087 | ||
| 1088 | napi_value sn_crypto_pwhash_scryptsalsa208sha256_str_verify (napi_env env, napi_callback_info info) { | |
| 1089 | SN_ARGV(2, crypto_pwhash_scryptsalsa208sha256_str_verify) | |
| 1090 | ||
| 1091 | SN_ARGV_TYPEDARRAY(str, 0) | |
| 1092 | SN_ARGV_TYPEDARRAY(passwd, 1) | |
| 1093 | ||
| 1094 | SN_ASSERT_LENGTH(str_size, crypto_pwhash_scryptsalsa208sha256_STRBYTES, "str") | |
| 1095 | ||
| 1096 | SN_RETURN_BOOLEAN(crypto_pwhash_scryptsalsa208sha256_str_verify(str_data, passwd_data, passwd_size)) | |
| 1097 | } | |
| 1098 | ||
| 1099 | napi_value sn_crypto_pwhash_scryptsalsa208sha256_str_needs_rehash (napi_env env, napi_callback_info info) { | |
| 1100 | SN_ARGV(3, crypto_pwhash_scryptsalsa208sha256_str_needs_rehash) | |
| 1101 | ||
| 1102 | SN_ARGV_TYPEDARRAY(str, 0) | |
| 1103 | SN_ARGV_UINT64(opslimit, 1) | |
| 1104 | SN_ARGV_UINT64(memlimit, 2) | |
| 1105 | ||
| 1106 | SN_ASSERT_LENGTH(str_size, crypto_pwhash_scryptsalsa208sha256_STRBYTES, "str") | |
| 1107 | SN_ASSERT_MIN_LENGTH(opslimit, crypto_pwhash_OPSLIMIT_MIN, "opslimit") | |
| 1108 | SN_ASSERT_MAX_LENGTH(opslimit, crypto_pwhash_OPSLIMIT_MAX, "opslimit") | |
| 1109 | SN_ASSERT_MIN_LENGTH(memlimit, crypto_pwhash_MEMLIMIT_MIN, "memlimit") | |
| 1110 | SN_ASSERT_MAX_LENGTH(memlimit, (int64_t) crypto_pwhash_MEMLIMIT_MAX, "memlimit") | |
| 1111 | ||
| 1112 | SN_RETURN_BOOLEAN_FROM_1(crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(str_data, opslimit, memlimit)) | |
| 1113 | } | |
| 1114 | ||
| 1115 | napi_value sn_crypto_kx_keypair (napi_env env, napi_callback_info info) { | |
| 1116 | SN_ARGV(2, crypto_kx_keypair) | |
| 1117 | ||
| 1118 | SN_ARGV_TYPEDARRAY(pk, 0) | |
| 1119 | SN_ARGV_TYPEDARRAY(sk, 1) | |
| 1120 | ||
| 1121 | SN_ASSERT_LENGTH(pk_size, crypto_kx_PUBLICKEYBYTES, "pk") | |
| 1122 | SN_ASSERT_LENGTH(sk_size, crypto_kx_SECRETKEYBYTES, "sk") | |
| 1123 | ||
| 1124 | SN_RETURN(crypto_kx_keypair(pk_data, sk_data), "failed to generate keypair") | |
| 1125 | } | |
| 1126 | ||
| 1127 | napi_value sn_crypto_kx_seed_keypair (napi_env env, napi_callback_info info) { | |
| 1128 | SN_ARGV(3, crypto_kx_seed_keypair) | |
| 1129 | ||
| 1130 | SN_ARGV_TYPEDARRAY(pk, 0) | |
| 1131 | SN_ARGV_TYPEDARRAY(sk, 1) | |
| 1132 | SN_ARGV_TYPEDARRAY(seed, 2) | |
| 1133 | ||
| 1134 | SN_ASSERT_LENGTH(pk_size, crypto_kx_PUBLICKEYBYTES, "pk") | |
| 1135 | SN_ASSERT_LENGTH(sk_size, crypto_kx_SECRETKEYBYTES, "sk") | |
| 1136 | SN_ASSERT_LENGTH(seed_size, crypto_kx_SEEDBYTES, "seed") | |
| 1137 | ||
| 1138 | SN_RETURN(crypto_kx_seed_keypair(pk_data, sk_data, seed_data), "failed to derive keypair from seed") | |
| 1139 | } | |
| 1140 | ||
| 1141 | napi_value sn_crypto_kx_client_session_keys (napi_env env, napi_callback_info info) { | |
| 1142 | SN_ARGV(5, crypto_kx_client_session_keys) | |
| 1143 | ||
| 1144 | SN_ARGV_OPTS_TYPEDARRAY(rx, 0) | |
| 1145 | SN_ARGV_OPTS_TYPEDARRAY(tx, 1) | |
| 1146 | ||
| 1147 | SN_THROWS(rx_data == NULL && tx_data == NULL, "at least one session key must be specified") | |
| 1148 | ||
| 1149 | SN_ARGV_TYPEDARRAY(client_pk, 2) | |
| 1150 | SN_ARGV_TYPEDARRAY(client_sk, 3) | |
| 1151 | SN_ARGV_TYPEDARRAY(server_pk, 4) | |
| 1152 | ||
| 1153 | SN_ASSERT_LENGTH(client_pk_size, crypto_kx_PUBLICKEYBYTES, "client_pk") | |
| 1154 | SN_ASSERT_LENGTH(client_sk_size, crypto_kx_SECRETKEYBYTES, "client_sk") | |
| 1155 | SN_ASSERT_LENGTH(server_pk_size, crypto_kx_PUBLICKEYBYTES, "server_pk") | |
| 1156 | ||
| 1157 | SN_THROWS(tx_size != crypto_kx_SESSIONKEYBYTES && tx_data != NULL, "transmitting key buffer must be 'crypto_kx_SESSIONKEYBYTES' bytes or null") | |
| 1158 | SN_THROWS(rx_size != crypto_kx_SESSIONKEYBYTES && rx_data != NULL, "receiving key buffer must be 'crypto_kx_SESSIONKEYBYTES' bytes or null") | |
| 1159 | ||
| 1160 | SN_RETURN(crypto_kx_client_session_keys(rx_data, tx_data, client_pk_data, client_sk_data, server_pk_data), "failed to derive session keys") | |
| 1161 | } | |
| 1162 | ||
| 1163 | napi_value sn_crypto_kx_server_session_keys (napi_env env, napi_callback_info info) { | |
| 1164 | SN_ARGV(5, crypto_kx_server_session_keys) | |
| 1165 | ||
| 1166 | SN_ARGV_OPTS_TYPEDARRAY(rx, 0) | |
| 1167 | SN_ARGV_OPTS_TYPEDARRAY(tx, 1) | |
| 1168 | ||
| 1169 | SN_THROWS(rx_data == NULL && tx_data == NULL, "at least one session key must be specified") | |
| 1170 | ||
| 1171 | SN_ARGV_TYPEDARRAY(server_pk, 2) | |
| 1172 | SN_ARGV_TYPEDARRAY(server_sk, 3) | |
| 1173 | SN_ARGV_TYPEDARRAY(client_pk, 4) | |
| 1174 | ||
| 1175 | SN_ASSERT_LENGTH(server_pk_size, crypto_kx_PUBLICKEYBYTES, "server_pk") | |
| 1176 | SN_ASSERT_LENGTH(server_sk_size, crypto_kx_SECRETKEYBYTES, "server_sk") | |
| 1177 | SN_ASSERT_LENGTH(client_pk_size, crypto_kx_PUBLICKEYBYTES, "client_pk") | |
| 1178 | ||
| 1179 | SN_THROWS(tx_size != crypto_kx_SESSIONKEYBYTES && tx_data != NULL, "transmitting key buffer must be 'crypto_kx_SESSIONKEYBYTES' bytes or null") | |
| 1180 | SN_THROWS(rx_size != crypto_kx_SESSIONKEYBYTES && rx_data != NULL, "receiving key buffer must be 'crypto_kx_SESSIONKEYBYTES' bytes or null") | |
| 1181 | ||
| 1182 | SN_RETURN(crypto_kx_server_session_keys(rx_data, tx_data, server_pk_data, server_sk_data, client_pk_data), "failed to derive session keys") | |
| 1183 | } | |
| 1184 | ||
| 1185 | napi_value sn_crypto_scalarmult_base (napi_env env, napi_callback_info info) { | |
| 1186 | SN_ARGV(2, crypto_scalarmult_base) | |
| 1187 | ||
| 1188 | SN_ARGV_TYPEDARRAY(q, 0) | |
| 1189 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 1190 | ||
| 1191 | SN_ASSERT_LENGTH(q_size, crypto_scalarmult_BYTES, "q") | |
| 1192 | SN_ASSERT_LENGTH(n_size, crypto_scalarmult_SCALARBYTES, "n") | |
| 1193 | ||
| 1194 | SN_RETURN(crypto_scalarmult_base(q_data, n_data), "failed to derive public key") | |
| 1195 | } | |
| 1196 | ||
| 1197 | napi_value sn_crypto_scalarmult (napi_env env, napi_callback_info info) { | |
| 1198 | SN_ARGV(3, crypto_scalarmult) | |
| 1199 | ||
| 1200 | SN_ARGV_TYPEDARRAY(q, 0) | |
| 1201 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 1202 | SN_ARGV_TYPEDARRAY(p, 2) | |
| 1203 | ||
| 1204 | SN_ASSERT_LENGTH(q_size, crypto_scalarmult_BYTES, "q") | |
| 1205 | SN_ASSERT_LENGTH(n_size, crypto_scalarmult_SCALARBYTES, "n") | |
| 1206 | SN_ASSERT_LENGTH(p_size, crypto_scalarmult_BYTES, "p") | |
| 1207 | ||
| 1208 | SN_RETURN(crypto_scalarmult(q_data, n_data, p_data), "failed to derive shared secret") | |
| 1209 | } | |
| 1210 | ||
| 1211 | napi_value sn_crypto_scalarmult_ed25519_base (napi_env env, napi_callback_info info) { | |
| 1212 | SN_ARGV(2, crypto_scalarmult_ed25519_base) | |
| 1213 | ||
| 1214 | SN_ARGV_TYPEDARRAY(q, 0) | |
| 1215 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 1216 | ||
| 1217 | SN_ASSERT_LENGTH(q_size, crypto_scalarmult_ed25519_BYTES, "q") | |
| 1218 | SN_ASSERT_LENGTH(n_size, crypto_scalarmult_ed25519_SCALARBYTES, "n") | |
| 1219 | ||
| 1220 | SN_RETURN(crypto_scalarmult_ed25519_base(q_data, n_data), "failed to derive public key") | |
| 1221 | } | |
| 1222 | ||
| 1223 | napi_value sn_crypto_scalarmult_ed25519 (napi_env env, napi_callback_info info) { | |
| 1224 | SN_ARGV(3, crypto_scalarmult_ed25519) | |
| 1225 | ||
| 1226 | SN_ARGV_TYPEDARRAY(q, 0) | |
| 1227 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 1228 | SN_ARGV_TYPEDARRAY(p, 2) | |
| 1229 | ||
| 1230 | SN_ASSERT_LENGTH(q_size, crypto_scalarmult_ed25519_BYTES, "q") | |
| 1231 | SN_ASSERT_LENGTH(n_size, crypto_scalarmult_ed25519_SCALARBYTES, "n") | |
| 1232 | SN_ASSERT_LENGTH(p_size, crypto_scalarmult_ed25519_BYTES, "p") | |
| 1233 | ||
| 1234 | SN_RETURN(crypto_scalarmult_ed25519(q_data, n_data, p_data), "failed to derive shared secret") | |
| 1235 | } | |
| 1236 | ||
| 1237 | napi_value sn_crypto_core_ed25519_is_valid_point (napi_env env, napi_callback_info info) { | |
| 1238 | SN_ARGV(1, crypto_core_ed25519_is_valid_point) | |
| 1239 | ||
| 1240 | SN_ARGV_TYPEDARRAY(p, 0) | |
| 1241 | ||
| 1242 | SN_ASSERT_LENGTH(p_size, crypto_core_ed25519_BYTES, "p") | |
| 1243 | ||
| 1244 | SN_RETURN_BOOLEAN_FROM_1(crypto_core_ed25519_is_valid_point(p_data)) | |
| 1245 | } | |
| 1246 | ||
| 1247 | napi_value sn_crypto_core_ed25519_from_uniform (napi_env env, napi_callback_info info) { | |
| 1248 | SN_ARGV(2, crypto_core_ed25519_from_uniform) | |
| 1249 | ||
| 1250 | SN_ARGV_TYPEDARRAY(p, 0) | |
| 1251 | SN_ARGV_TYPEDARRAY(r, 1) | |
| 1252 | ||
| 1253 | SN_ASSERT_LENGTH(p_size, crypto_core_ed25519_BYTES, "p") | |
| 1254 | SN_ASSERT_LENGTH(r_size, crypto_core_ed25519_UNIFORMBYTES, "r") | |
| 1255 | ||
| 1256 | SN_RETURN(crypto_core_ed25519_from_uniform(p_data, r_data), "could not generate curve point from input") | |
| 1257 | } | |
| 1258 | ||
| 1259 | napi_value sn_crypto_scalarmult_ed25519_base_noclamp (napi_env env, napi_callback_info info) { | |
| 1260 | SN_ARGV(2, crypto_scalarmult_ed25519_base_noclamp) | |
| 1261 | ||
| 1262 | SN_ARGV_TYPEDARRAY(q, 0) | |
| 1263 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 1264 | ||
| 1265 | SN_ASSERT_LENGTH(q_size, crypto_scalarmult_ed25519_BYTES, "q") | |
| 1266 | SN_ASSERT_LENGTH(n_size, crypto_scalarmult_ed25519_SCALARBYTES, "n") | |
| 1267 | ||
| 1268 | SN_RETURN(crypto_scalarmult_ed25519_base_noclamp(q_data, n_data), "failed to derive public key") | |
| 1269 | } | |
| 1270 | ||
| 1271 | napi_value sn_crypto_scalarmult_ed25519_noclamp (napi_env env, napi_callback_info info) { | |
| 1272 | SN_ARGV(3, crypto_scalarmult_ed25519_noclamp) | |
| 1273 | ||
| 1274 | SN_ARGV_TYPEDARRAY(q, 0) | |
| 1275 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 1276 | SN_ARGV_TYPEDARRAY(p, 2) | |
| 1277 | ||
| 1278 | SN_ASSERT_LENGTH(q_size, crypto_scalarmult_ed25519_BYTES, "q") | |
| 1279 | SN_ASSERT_LENGTH(n_size, crypto_scalarmult_ed25519_SCALARBYTES, "n") | |
| 1280 | SN_ASSERT_LENGTH(p_size, crypto_scalarmult_ed25519_BYTES, "p") | |
| 1281 | ||
| 1282 | SN_RETURN(crypto_scalarmult_ed25519_noclamp(q_data, n_data, p_data), "failed to derive shared secret") | |
| 1283 | } | |
| 1284 | ||
| 1285 | napi_value sn_crypto_core_ed25519_add (napi_env env, napi_callback_info info) { | |
| 1286 | SN_ARGV(3, crypto_core_ed25519_add) | |
| 1287 | ||
| 1288 | SN_ARGV_TYPEDARRAY(r, 0) | |
| 1289 | SN_ARGV_TYPEDARRAY(p, 1) | |
| 1290 | SN_ARGV_TYPEDARRAY(q, 2) | |
| 1291 | ||
| 1292 | SN_ASSERT_LENGTH(r_size, crypto_core_ed25519_BYTES, "r") | |
| 1293 | SN_ASSERT_LENGTH(p_size, crypto_core_ed25519_BYTES, "p") | |
| 1294 | SN_ASSERT_LENGTH(q_size, crypto_core_ed25519_BYTES, "q") | |
| 1295 | ||
| 1296 | SN_RETURN(crypto_core_ed25519_add(r_data, p_data, q_data), "could not add curve points") | |
| 1297 | } | |
| 1298 | ||
| 1299 | napi_value sn_crypto_core_ed25519_sub (napi_env env, napi_callback_info info) { | |
| 1300 | SN_ARGV(3, crypto_core_ed25519_sub) | |
| 1301 | ||
| 1302 | SN_ARGV_TYPEDARRAY(r, 0) | |
| 1303 | SN_ARGV_TYPEDARRAY(p, 1) | |
| 1304 | SN_ARGV_TYPEDARRAY(q, 2) | |
| 1305 | ||
| 1306 | SN_ASSERT_LENGTH(r_size, crypto_core_ed25519_BYTES, "r") | |
| 1307 | SN_ASSERT_LENGTH(p_size, crypto_core_ed25519_BYTES, "p") | |
| 1308 | SN_ASSERT_LENGTH(q_size, crypto_core_ed25519_BYTES, "q") | |
| 1309 | ||
| 1310 | SN_RETURN(crypto_core_ed25519_sub(r_data, p_data, q_data), "could not add curve points") | |
| 1311 | } | |
| 1312 | ||
| 1313 | napi_value sn_crypto_core_ed25519_scalar_random (napi_env env, napi_callback_info info) { | |
| 1314 | SN_ARGV(1, crypto_core_ed25519_scalar_random) | |
| 1315 | ||
| 1316 | SN_ARGV_TYPEDARRAY(r, 0) | |
| 1317 | ||
| 1318 | SN_ASSERT_LENGTH(r_size, crypto_core_ed25519_SCALARBYTES, "r") | |
| 1319 | ||
| 1320 | crypto_core_ed25519_scalar_random(r_data); | |
| 1321 | ||
| 1322 | return NULL; | |
| 1323 | } | |
| 1324 | ||
| 1325 | napi_value sn_crypto_core_ed25519_scalar_reduce (napi_env env, napi_callback_info info) { | |
| 1326 | SN_ARGV(2, crypto_core_ed25519_scalar_reduce) | |
| 1327 | ||
| 1328 | SN_ARGV_TYPEDARRAY(r, 0) | |
| 1329 | SN_ARGV_TYPEDARRAY(s, 1) | |
| 1330 | ||
| 1331 | SN_ASSERT_LENGTH(r_size, crypto_core_ed25519_SCALARBYTES, "r") | |
| 1332 | SN_ASSERT_LENGTH(s_size, crypto_core_ed25519_NONREDUCEDSCALARBYTES, "s") | |
| 1333 | ||
| 1334 | crypto_core_ed25519_scalar_reduce(r_data, s_data); | |
| 1335 | ||
| 1336 | return NULL; | |
| 1337 | } | |
| 1338 | ||
| 1339 | napi_value sn_crypto_core_ed25519_scalar_invert (napi_env env, napi_callback_info info) { | |
| 1340 | SN_ARGV(2, crypto_core_ed25519_scalar_invert) | |
| 1341 | ||
| 1342 | SN_ARGV_TYPEDARRAY(recip, 0) | |
| 1343 | SN_ARGV_TYPEDARRAY(s, 1) | |
| 1344 | ||
| 1345 | SN_ASSERT_LENGTH(recip_size, crypto_core_ed25519_SCALARBYTES, "recip") | |
| 1346 | SN_ASSERT_LENGTH(s_size, crypto_core_ed25519_SCALARBYTES, "s") | |
| 1347 | ||
| 1348 | crypto_core_ed25519_scalar_invert(recip_data, s_data); | |
| 1349 | ||
| 1350 | return NULL; | |
| 1351 | } | |
| 1352 | ||
| 1353 | napi_value sn_crypto_core_ed25519_scalar_negate (napi_env env, napi_callback_info info) { | |
| 1354 | SN_ARGV(2, crypto_core_ed25519_scalar_negate) | |
| 1355 | ||
| 1356 | SN_ARGV_TYPEDARRAY(neg, 0) | |
| 1357 | SN_ARGV_TYPEDARRAY(s, 1) | |
| 1358 | ||
| 1359 | SN_ASSERT_LENGTH(neg_size, crypto_core_ed25519_SCALARBYTES, "neg") | |
| 1360 | SN_ASSERT_LENGTH(s_size, crypto_core_ed25519_SCALARBYTES, "s") | |
| 1361 | ||
| 1362 | crypto_core_ed25519_scalar_negate(neg_data, s_data); | |
| 1363 | ||
| 1364 | return NULL; | |
| 1365 | } | |
| 1366 | ||
| 1367 | napi_value sn_crypto_core_ed25519_scalar_complement (napi_env env, napi_callback_info info) { | |
| 1368 | SN_ARGV(2, crypto_core_ed25519_scalar_complement) | |
| 1369 | ||
| 1370 | SN_ARGV_TYPEDARRAY(comp, 0) | |
| 1371 | SN_ARGV_TYPEDARRAY(s, 1) | |
| 1372 | ||
| 1373 | SN_ASSERT_LENGTH(comp_size, crypto_core_ed25519_SCALARBYTES, "comp") | |
| 1374 | SN_ASSERT_LENGTH(s_size, crypto_core_ed25519_SCALARBYTES, "s") | |
| 1375 | ||
| 1376 | crypto_core_ed25519_scalar_complement(comp_data, s_data); | |
| 1377 | ||
| 1378 | return NULL; | |
| 1379 | } | |
| 1380 | ||
| 1381 | napi_value sn_crypto_core_ed25519_scalar_add (napi_env env, napi_callback_info info) { | |
| 1382 | SN_ARGV(3, crypto_core_ed25519_scalar_add) | |
| 1383 | ||
| 1384 | SN_ARGV_TYPEDARRAY(z, 0) | |
| 1385 | SN_ARGV_TYPEDARRAY(x, 1) | |
| 1386 | SN_ARGV_TYPEDARRAY(y, 2) | |
| 1387 | ||
| 1388 | SN_ASSERT_LENGTH(z_size, crypto_core_ed25519_SCALARBYTES, "z") | |
| 1389 | SN_ASSERT_LENGTH(x_size, crypto_core_ed25519_SCALARBYTES, "x") | |
| 1390 | SN_ASSERT_LENGTH(y_size, crypto_core_ed25519_SCALARBYTES, "y") | |
| 1391 | ||
| 1392 | crypto_core_ed25519_scalar_add(z_data, x_data, y_data); | |
| 1393 | ||
| 1394 | return NULL; | |
| 1395 | } | |
| 1396 | ||
| 1397 | napi_value sn_crypto_core_ed25519_scalar_sub (napi_env env, napi_callback_info info) { | |
| 1398 | SN_ARGV(3, crypto_core_ed25519_scalar_sub) | |
| 1399 | ||
| 1400 | SN_ARGV_TYPEDARRAY(z, 0) | |
| 1401 | SN_ARGV_TYPEDARRAY(x, 1) | |
| 1402 | SN_ARGV_TYPEDARRAY(y, 2) | |
| 1403 | ||
| 1404 | SN_ASSERT_LENGTH(z_size, crypto_core_ed25519_SCALARBYTES, "z") | |
| 1405 | SN_ASSERT_LENGTH(x_size, crypto_core_ed25519_SCALARBYTES, "x") | |
| 1406 | SN_ASSERT_LENGTH(y_size, crypto_core_ed25519_SCALARBYTES, "y") | |
| 1407 | ||
| 1408 | crypto_core_ed25519_scalar_sub(z_data, x_data, y_data); | |
| 1409 | ||
| 1410 | return NULL; | |
| 1411 | } | |
| 1412 | ||
| 1413 | napi_value sn_crypto_shorthash (napi_env env, napi_callback_info info) { | |
| 1414 | SN_ARGV(3, crypto_shorthash) | |
| 1415 | ||
| 1416 | SN_ARGV_TYPEDARRAY(out, 0) | |
| 1417 | SN_ARGV_TYPEDARRAY(in, 1) | |
| 1418 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 1419 | ||
| 1420 | SN_ASSERT_LENGTH(out_size, crypto_shorthash_BYTES, "out") | |
| 1421 | SN_ASSERT_LENGTH(k_size, crypto_shorthash_KEYBYTES, "k") | |
| 1422 | ||
| 1423 | SN_RETURN(crypto_shorthash(out_data, in_data, in_size, k_data), "could not compute hash") | |
| 1424 | } | |
| 1425 | ||
| 1426 | napi_value sn_crypto_kdf_keygen (napi_env env, napi_callback_info info) { | |
| 1427 | SN_ARGV(1, crypto_kdf_keygen) | |
| 1428 | ||
| 1429 | SN_ARGV_TYPEDARRAY(key, 0) | |
| 1430 | ||
| 1431 | SN_ASSERT_LENGTH(key_size, crypto_kdf_KEYBYTES, "key") | |
| 1432 | ||
| 1433 | crypto_kdf_keygen(key_data); | |
| 1434 | ||
| 1435 | return NULL; | |
| 1436 | } | |
| 1437 | ||
| 1438 | napi_value sn_crypto_kdf_derive_from_key (napi_env env, napi_callback_info info) { | |
| 1439 | SN_ARGV(4, crypto_kdf_derive_from_key) | |
| 1440 | ||
| 1441 | SN_ARGV_TYPEDARRAY(subkey, 0) | |
| 1442 | SN_ARGV_UINT64(subkey_id, 1) | |
| 1443 | SN_ARGV_TYPEDARRAY(ctx, 2) | |
| 1444 | SN_ARGV_TYPEDARRAY(key, 3) | |
| 1445 | ||
| 1446 | SN_ASSERT_MIN_LENGTH(subkey_size, crypto_kdf_BYTES_MIN, "subkey") | |
| 1447 | SN_ASSERT_MAX_LENGTH(subkey_size, crypto_kdf_BYTES_MAX, "subkey") | |
| 1448 | SN_ASSERT_LENGTH(ctx_size, crypto_kdf_CONTEXTBYTES, "ctx") | |
| 1449 | SN_ASSERT_LENGTH(key_size, crypto_kdf_KEYBYTES, "key") | |
| 1450 | ||
| 1451 | SN_RETURN(crypto_kdf_derive_from_key(subkey_data, subkey_size, subkey_id, ctx_data, key_data), "could not generate key") | |
| 1452 | } | |
| 1453 | ||
| 1454 | napi_value sn_crypto_hash (napi_env env, napi_callback_info info) { | |
| 1455 | SN_ARGV(2, crypto_hash_sha256) | |
| 1456 | ||
| 1457 | SN_ARGV_TYPEDARRAY(out, 0) | |
| 1458 | SN_ARGV_TYPEDARRAY(in, 1) | |
| 1459 | ||
| 1460 | SN_ASSERT_LENGTH(out_size, crypto_hash_BYTES, "out") | |
| 1461 | ||
| 1462 | SN_RETURN(crypto_hash(out_data, in_data, in_size), "could not compute hash") | |
| 1463 | } | |
| 1464 | ||
| 1465 | napi_value sn_crypto_hash_sha256 (napi_env env, napi_callback_info info) { | |
| 1466 | SN_ARGV(2, crypto_hash_sha256) | |
| 1467 | ||
| 1468 | SN_ARGV_TYPEDARRAY(out, 0) | |
| 1469 | SN_ARGV_TYPEDARRAY(in, 1) | |
| 1470 | ||
| 1471 | SN_ASSERT_LENGTH(out_size, crypto_hash_sha256_BYTES, "out") | |
| 1472 | ||
| 1473 | SN_RETURN(crypto_hash_sha256(out_data, in_data, in_size), "could not compute hash") | |
| 1474 | } | |
| 1475 | ||
| 1476 | napi_value sn_crypto_hash_sha256_init (napi_env env, napi_callback_info info) { | |
| 1477 | SN_ARGV(1, crypto_hash_sha256_init) | |
| 1478 | ||
| 1479 | SN_ARGV_BUFFER_CAST(crypto_hash_sha256_state *, state, 0) | |
| 1480 | ||
| 1481 | SN_THROWS(state_size != sizeof(crypto_hash_sha256_state), "state must be 'crypto_hash_sha256_STATEBYTES' bytes") | |
| 1482 | ||
| 1483 | SN_RETURN(crypto_hash_sha256_init(state), "failed to initialise sha256") | |
| 1484 | } | |
| 1485 | ||
| 1486 | napi_value sn_crypto_hash_sha256_update(napi_env env, napi_callback_info info) { | |
| 1487 | SN_ARGV(2, crypto_hash_sha256_update) | |
| 1488 | ||
| 1489 | SN_ARGV_BUFFER_CAST(crypto_hash_sha256_state *, state, 0) | |
| 1490 | SN_ARGV_TYPEDARRAY(in, 1) | |
| 1491 | ||
| 1492 | SN_THROWS(state_size != sizeof(crypto_hash_sha256_state), "state must be 'crypto_hash_sha256_STATEBYTES' bytes") | |
| 1493 | ||
| 1494 | SN_RETURN(crypto_hash_sha256_update(state, in_data, in_size), "update failed") | |
| 1495 | } | |
| 1496 | ||
| 1497 | napi_value sn_crypto_hash_sha256_final(napi_env env, napi_callback_info info) { | |
| 1498 | SN_ARGV(2, crypto_hash_sha256_final) | |
| 1499 | ||
| 1500 | SN_ARGV_BUFFER_CAST(crypto_hash_sha256_state *, state, 0) | |
| 1501 | SN_ARGV_TYPEDARRAY(out, 1) | |
| 1502 | ||
| 1503 | SN_THROWS(state_size != sizeof(crypto_hash_sha256_state), "state must be 'crypto_hash_sha256_STATEBYTES' bytes") | |
| 1504 | SN_ASSERT_LENGTH(out_size, crypto_hash_sha256_BYTES, "state") | |
| 1505 | ||
| 1506 | SN_RETURN(crypto_hash_sha256_final(state, out_data), "failed to finalise") | |
| 1507 | } | |
| 1508 | ||
| 1509 | napi_value sn_crypto_hash_sha512 (napi_env env, napi_callback_info info) { | |
| 1510 | SN_ARGV(2, crypto_hash_sha512) | |
| 1511 | ||
| 1512 | SN_ARGV_TYPEDARRAY(out, 0) | |
| 1513 | SN_ARGV_TYPEDARRAY(in, 1) | |
| 1514 | ||
| 1515 | SN_ASSERT_LENGTH(out_size, crypto_hash_sha512_BYTES, "out") | |
| 1516 | ||
| 1517 | SN_RETURN(crypto_hash_sha512(out_data, in_data, in_size), "could not compute hash") | |
| 1518 | } | |
| 1519 | ||
| 1520 | napi_value sn_crypto_hash_sha512_init (napi_env env, napi_callback_info info) { | |
| 1521 | SN_ARGV(1, crypto_hash_sha512_init) | |
| 1522 | ||
| 1523 | SN_ARGV_BUFFER_CAST(crypto_hash_sha512_state *, state, 0) | |
| 1524 | ||
| 1525 | SN_THROWS(state_size != sizeof(crypto_hash_sha512_state), "state must be 'crypto_hash_sha256_STATEBYTES' bytes") | |
| 1526 | ||
| 1527 | SN_RETURN(crypto_hash_sha512_init(state), "failed to initialise sha512") | |
| 1528 | } | |
| 1529 | ||
| 1530 | napi_value sn_crypto_hash_sha512_update(napi_env env, napi_callback_info info) { | |
| 1531 | SN_ARGV(2, crypto_hash_sha512_update) | |
| 1532 | ||
| 1533 | SN_ARGV_BUFFER_CAST(crypto_hash_sha512_state *, state, 0) | |
| 1534 | SN_ARGV_TYPEDARRAY(in, 1) | |
| 1535 | ||
| 1536 | SN_THROWS(state_size != sizeof(crypto_hash_sha512_state), "state must be 'crypto_hash_sha256_STATEBYTES' bytes") | |
| 1537 | ||
| 1538 | SN_RETURN(crypto_hash_sha512_update(state, in_data, in_size), "update failed") | |
| 1539 | } | |
| 1540 | ||
| 1541 | napi_value sn_crypto_hash_sha512_final(napi_env env, napi_callback_info info) { | |
| 1542 | SN_ARGV(2, crypto_hash_sha512_final) | |
| 1543 | ||
| 1544 | SN_ARGV_BUFFER_CAST(crypto_hash_sha512_state *, state, 0) | |
| 1545 | SN_ARGV_TYPEDARRAY(out, 1) | |
| 1546 | ||
| 1547 | SN_THROWS(state_size != sizeof(crypto_hash_sha512_state), "state must be 'crypto_hash_sha256_STATEBYTES' bytes") | |
| 1548 | SN_ASSERT_LENGTH(out_size, crypto_hash_sha512_BYTES, "out") | |
| 1549 | ||
| 1550 | SN_RETURN(crypto_hash_sha512_final(state, out_data), "failed to finalise hash") | |
| 1551 | } | |
| 1552 | ||
| 1553 | napi_value sn_crypto_aead_xchacha20poly1305_ietf_keygen (napi_env env, napi_callback_info info) { | |
| 1554 | SN_ARGV(1, crypto_aead_xchacha20poly1305_ietf_keygen) | |
| 1555 | ||
| 1556 | SN_ARGV_TYPEDARRAY(k, 0) | |
| 1557 | ||
| 1558 | SN_ASSERT_LENGTH(k_size, crypto_aead_xchacha20poly1305_ietf_KEYBYTES, "k") | |
| 1559 | ||
| 1560 | crypto_aead_xchacha20poly1305_ietf_keygen(k_data); | |
| 1561 | return NULL; | |
| 1562 | } | |
| 1563 | ||
| 1564 | napi_value sn_crypto_aead_xchacha20poly1305_ietf_encrypt (napi_env env, napi_callback_info info) { | |
| 1565 | SN_ARGV(6, crypto_aead_xchacha20poly1305_ietf_encrypt) | |
| 1566 | ||
| 1567 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 1568 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 1569 | SN_ARGV_OPTS_TYPEDARRAY(ad, 2) | |
| 1570 | SN_ARGV_CHECK_NULL(nsec, 3) | |
| 1571 | SN_ARGV_TYPEDARRAY(npub, 4) | |
| 1572 | SN_ARGV_TYPEDARRAY(k, 5) | |
| 1573 | ||
| 1574 | SN_THROWS(!nsec_is_null, "nsec must always be set to null") | |
| 1575 | ||
| 1576 | SN_THROWS(c_size != m_size + crypto_aead_xchacha20poly1305_ietf_ABYTES, "c must 'm.byteLength + crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes") | |
| 1577 | SN_THROWS(c_size > 0xffffffff, "c.byteLength must be a 32bit integer") | |
| 1578 | SN_ASSERT_LENGTH(npub_size, crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, "npub") | |
| 1579 | SN_ASSERT_LENGTH(k_size, crypto_aead_xchacha20poly1305_ietf_KEYBYTES, "k") | |
| 1580 | ||
| 1581 | unsigned long long clen; | |
| 1582 | SN_CALL(crypto_aead_xchacha20poly1305_ietf_encrypt(c_data, &clen, m_data, m_size, ad_data, ad_size, NULL, npub_data, k_data), "could not encrypt data") | |
| 1583 | ||
| 1584 | napi_value result; | |
| 1585 | SN_STATUS_THROWS(napi_create_uint32(env, (uint32_t) clen, &result), "") | |
| 1586 | return result; | |
| 1587 | } | |
| 1588 | ||
| 1589 | napi_value sn_crypto_aead_xchacha20poly1305_ietf_decrypt (napi_env env, napi_callback_info info) { | |
| 1590 | SN_ARGV(6, crypto_aead_xchacha20poly1305_ietf_decrypt) | |
| 1591 | ||
| 1592 | SN_ARGV_TYPEDARRAY(m, 0) | |
| 1593 | SN_ARGV_CHECK_NULL(nsec, 1) | |
| 1594 | SN_ARGV_TYPEDARRAY(c, 2) | |
| 1595 | SN_ARGV_OPTS_TYPEDARRAY(ad, 3) | |
| 1596 | SN_ARGV_TYPEDARRAY(npub, 4) | |
| 1597 | SN_ARGV_TYPEDARRAY(k, 5) | |
| 1598 | ||
| 1599 | SN_THROWS(!nsec_is_null, "nsec must always be set to null") | |
| 1600 | ||
| 1601 | SN_THROWS(m_size != c_size - crypto_aead_xchacha20poly1305_ietf_ABYTES, "m must 'c.byteLength - crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes") | |
| 1602 | SN_ASSERT_LENGTH(npub_size, crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, "npub") | |
| 1603 | SN_ASSERT_LENGTH(k_size, crypto_aead_xchacha20poly1305_ietf_KEYBYTES, "k") | |
| 1604 | SN_THROWS(m_size > 0xffffffff, "m.byteLength must be a 32bit integer") | |
| 1605 | ||
| 1606 | unsigned long long mlen; | |
| 1607 | SN_CALL(crypto_aead_xchacha20poly1305_ietf_decrypt(m_data, &mlen, NULL, c_data, c_size, ad_data, ad_size, npub_data, k_data), "could not verify data") | |
| 1608 | ||
| 1609 | napi_value result; | |
| 1610 | SN_STATUS_THROWS(napi_create_uint32(env, (uint32_t) mlen, &result), "") | |
| 1611 | return result; | |
| 1612 | } | |
| 1613 | ||
| 1614 | napi_value sn_crypto_aead_xchacha20poly1305_ietf_encrypt_detached (napi_env env, napi_callback_info info) { | |
| 1615 | SN_ARGV(7, crypto_aead_xchacha20poly1305_ietf_encrypt_detached) | |
| 1616 | ||
| 1617 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 1618 | SN_ARGV_TYPEDARRAY(mac, 1) | |
| 1619 | SN_ARGV_TYPEDARRAY(m, 2) | |
| 1620 | SN_ARGV_OPTS_TYPEDARRAY(ad, 3) | |
| 1621 | SN_ARGV_CHECK_NULL(nsec, 4) | |
| 1622 | SN_ARGV_TYPEDARRAY(npub, 5) | |
| 1623 | SN_ARGV_TYPEDARRAY(k, 6) | |
| 1624 | ||
| 1625 | SN_THROWS(!nsec_is_null, "nsec must always be set to null") | |
| 1626 | ||
| 1627 | SN_THROWS(c_size != m_size, "c must be 'm.byteLength' bytes") | |
| 1628 | SN_ASSERT_LENGTH(mac_size, crypto_aead_xchacha20poly1305_ietf_ABYTES, "mac") | |
| 1629 | SN_ASSERT_LENGTH(npub_size, crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, "npub") | |
| 1630 | SN_ASSERT_LENGTH(k_size, crypto_aead_xchacha20poly1305_ietf_KEYBYTES, "k") | |
| 1631 | ||
| 1632 | unsigned long long maclen; | |
| 1633 | SN_CALL(crypto_aead_xchacha20poly1305_ietf_encrypt_detached(c_data, mac_data, &maclen, m_data, m_size, ad_data, ad_size, NULL, npub_data, k_data), "could not encrypt data") | |
| 1634 | ||
| 1635 | napi_value result; | |
| 1636 | SN_STATUS_THROWS(napi_create_uint32(env, (uint32_t) maclen, &result), "") | |
| 1637 | return result; | |
| 1638 | } | |
| 1639 | ||
| 1640 | napi_value sn_crypto_aead_xchacha20poly1305_ietf_decrypt_detached (napi_env env, napi_callback_info info) { | |
| 1641 | SN_ARGV(7, crypto_aead_xchacha20poly1305_ietf_decrypt_detached) | |
| 1642 | ||
| 1643 | SN_ARGV_TYPEDARRAY(m, 0) | |
| 1644 | SN_ARGV_CHECK_NULL(nsec, 1) | |
| 1645 | SN_ARGV_TYPEDARRAY(c, 2) | |
| 1646 | SN_ARGV_TYPEDARRAY(mac, 3) | |
| 1647 | SN_ARGV_OPTS_TYPEDARRAY(ad, 4) | |
| 1648 | SN_ARGV_TYPEDARRAY(npub, 5) | |
| 1649 | SN_ARGV_TYPEDARRAY(k, 6) | |
| 1650 | ||
| 1651 | SN_THROWS(!nsec_is_null, "nsec must always be set to null") | |
| 1652 | ||
| 1653 | SN_THROWS(m_size != c_size, "m must be 'c.byteLength' bytes") | |
| 1654 | SN_ASSERT_LENGTH(mac_size, crypto_aead_xchacha20poly1305_ietf_ABYTES, "mac") | |
| 1655 | SN_ASSERT_LENGTH(npub_size, crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, "npub") | |
| 1656 | SN_ASSERT_LENGTH(k_size, crypto_aead_xchacha20poly1305_ietf_KEYBYTES, "k") | |
| 1657 | ||
| 1658 | SN_RETURN(crypto_aead_xchacha20poly1305_ietf_decrypt_detached(m_data, NULL, c_data, c_size, mac_data, ad_data, ad_size, npub_data, k_data), "could not verify data") | |
| 1659 | } | |
| 1660 | ||
| 1661 | napi_value sn_crypto_aead_chacha20poly1305_ietf_keygen (napi_env env, napi_callback_info info) { | |
| 1662 | SN_ARGV(1, crypto_aead_chacha20poly1305_ietf_keygen) | |
| 1663 | ||
| 1664 | SN_ARGV_TYPEDARRAY(k, 0) | |
| 1665 | ||
| 1666 | SN_ASSERT_LENGTH(k_size, crypto_aead_chacha20poly1305_ietf_KEYBYTES, "k") | |
| 1667 | ||
| 1668 | crypto_aead_chacha20poly1305_ietf_keygen(k_data); | |
| 1669 | return NULL; | |
| 1670 | } | |
| 1671 | ||
| 1672 | napi_value sn_crypto_aead_chacha20poly1305_ietf_encrypt (napi_env env, napi_callback_info info) { | |
| 1673 | SN_ARGV(6, crypto_aead_chacha20poly1305_ietf_encrypt) | |
| 1674 | ||
| 1675 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 1676 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 1677 | SN_ARGV_OPTS_TYPEDARRAY(ad, 2) | |
| 1678 | SN_ARGV_CHECK_NULL(nsec, 3) | |
| 1679 | SN_ARGV_TYPEDARRAY(npub, 4) | |
| 1680 | SN_ARGV_TYPEDARRAY(k, 5) | |
| 1681 | ||
| 1682 | SN_THROWS(!nsec_is_null, "nsec must always be set to null") | |
| 1683 | ||
| 1684 | SN_THROWS(c_size != m_size + crypto_aead_chacha20poly1305_ietf_ABYTES, "c must 'm.byteLength + crypto_aead_chacha20poly1305_ietf_ABYTES' bytes") | |
| 1685 | SN_THROWS(c_size > 0xffffffff, "c.byteLength must be a 32bit integer") | |
| 1686 | SN_ASSERT_LENGTH(npub_size, crypto_aead_chacha20poly1305_ietf_NPUBBYTES, "npub") | |
| 1687 | SN_ASSERT_LENGTH(k_size, crypto_aead_chacha20poly1305_ietf_KEYBYTES, "k") | |
| 1688 | ||
| 1689 | unsigned long long clen; | |
| 1690 | SN_CALL(crypto_aead_chacha20poly1305_ietf_encrypt(c_data, &clen, m_data, m_size, ad_data, ad_size, NULL, npub_data, k_data), "could not encrypt data") | |
| 1691 | ||
| 1692 | napi_value result; | |
| 1693 | SN_STATUS_THROWS(napi_create_uint32(env, (uint32_t) clen, &result), "") | |
| 1694 | return result; | |
| 1695 | } | |
| 1696 | ||
| 1697 | napi_value sn_crypto_aead_chacha20poly1305_ietf_decrypt (napi_env env, napi_callback_info info) { | |
| 1698 | SN_ARGV(6, crypto_aead_chacha20poly1305_ietf_decrypt) | |
| 1699 | ||
| 1700 | SN_ARGV_TYPEDARRAY(m, 0) | |
| 1701 | SN_ARGV_CHECK_NULL(nsec, 1) | |
| 1702 | SN_ARGV_TYPEDARRAY(c, 2) | |
| 1703 | SN_ARGV_OPTS_TYPEDARRAY(ad, 3) | |
| 1704 | SN_ARGV_TYPEDARRAY(npub, 4) | |
| 1705 | SN_ARGV_TYPEDARRAY(k, 5) | |
| 1706 | ||
| 1707 | SN_THROWS(!nsec_is_null, "nsec must always be set to null") | |
| 1708 | ||
| 1709 | SN_THROWS(m_size != c_size - crypto_aead_chacha20poly1305_ietf_ABYTES, "m must 'c.byteLength - crypto_aead_chacha20poly1305_ietf_ABYTES' bytes") | |
| 1710 | SN_ASSERT_LENGTH(npub_size, crypto_aead_chacha20poly1305_ietf_NPUBBYTES, "npub") | |
| 1711 | SN_ASSERT_LENGTH(k_size, crypto_aead_chacha20poly1305_ietf_KEYBYTES, "k") | |
| 1712 | SN_THROWS(m_size > 0xffffffff, "m.byteLength must be a 32bit integer") | |
| 1713 | ||
| 1714 | unsigned long long mlen; | |
| 1715 | SN_CALL(crypto_aead_chacha20poly1305_ietf_decrypt(m_data, &mlen, NULL, c_data, c_size, ad_data, ad_size, npub_data, k_data), "could not verify data") | |
| 1716 | ||
| 1717 | napi_value result; | |
| 1718 | SN_STATUS_THROWS(napi_create_uint32(env, (uint32_t) mlen, &result), "") | |
| 1719 | return result; | |
| 1720 | } | |
| 1721 | ||
| 1722 | napi_value sn_crypto_aead_chacha20poly1305_ietf_encrypt_detached (napi_env env, napi_callback_info info) { | |
| 1723 | SN_ARGV(7, crypto_aead_chacha20poly1305_ietf_encrypt_detached) | |
| 1724 | ||
| 1725 | SN_ARGV_TYPEDARRAY(c, 0) | |
| 1726 | SN_ARGV_TYPEDARRAY(mac, 1) | |
| 1727 | SN_ARGV_TYPEDARRAY(m, 2) | |
| 1728 | SN_ARGV_OPTS_TYPEDARRAY(ad, 3) | |
| 1729 | SN_ARGV_CHECK_NULL(nsec, 4) | |
| 1730 | SN_ARGV_TYPEDARRAY(npub, 5) | |
| 1731 | SN_ARGV_TYPEDARRAY(k, 6) | |
| 1732 | ||
| 1733 | SN_THROWS(!nsec_is_null, "nsec must always be set to null") | |
| 1734 | ||
| 1735 | SN_THROWS(c_size != m_size, "c must be 'm.byteLength' bytes") | |
| 1736 | SN_ASSERT_LENGTH(mac_size, crypto_aead_chacha20poly1305_ietf_ABYTES, "mac") | |
| 1737 | SN_ASSERT_LENGTH(npub_size, crypto_aead_chacha20poly1305_ietf_NPUBBYTES, "npub") | |
| 1738 | SN_ASSERT_LENGTH(k_size, crypto_aead_chacha20poly1305_ietf_KEYBYTES, "k") | |
| 1739 | ||
| 1740 | unsigned long long maclen; | |
| 1741 | SN_CALL(crypto_aead_chacha20poly1305_ietf_encrypt_detached(c_data, mac_data, &maclen, m_data, m_size, ad_data, ad_size, NULL, npub_data, k_data), "could not encrypt data") | |
| 1742 | ||
| 1743 | napi_value result; | |
| 1744 | SN_STATUS_THROWS(napi_create_uint32(env, (uint32_t) maclen, &result), "") | |
| 1745 | return result; | |
| 1746 | } | |
| 1747 | ||
| 1748 | napi_value sn_crypto_aead_chacha20poly1305_ietf_decrypt_detached (napi_env env, napi_callback_info info) { | |
| 1749 | SN_ARGV(7, crypto_aead_chacha20poly1305_ietf_decrypt_detached) | |
| 1750 | ||
| 1751 | SN_ARGV_TYPEDARRAY(m, 0) | |
| 1752 | SN_ARGV_CHECK_NULL(nsec, 1) | |
| 1753 | SN_ARGV_TYPEDARRAY(c, 2) | |
| 1754 | SN_ARGV_TYPEDARRAY(mac, 3) | |
| 1755 | SN_ARGV_OPTS_TYPEDARRAY(ad, 4) | |
| 1756 | SN_ARGV_TYPEDARRAY(npub, 5) | |
| 1757 | SN_ARGV_TYPEDARRAY(k, 6) | |
| 1758 | ||
| 1759 | SN_THROWS(!nsec_is_null, "nsec must always be set to null") | |
| 1760 | ||
| 1761 | SN_THROWS(m_size != c_size, "m must be 'c.byteLength' bytes") | |
| 1762 | SN_ASSERT_LENGTH(mac_size, crypto_aead_chacha20poly1305_ietf_ABYTES, "mac") | |
| 1763 | SN_ASSERT_LENGTH(npub_size, crypto_aead_chacha20poly1305_ietf_NPUBBYTES, "npub") | |
| 1764 | SN_ASSERT_LENGTH(k_size, crypto_aead_chacha20poly1305_ietf_KEYBYTES, "k") | |
| 1765 | ||
| 1766 | SN_RETURN(crypto_aead_chacha20poly1305_ietf_decrypt_detached(m_data, NULL, c_data, c_size, mac_data, ad_data, ad_size, npub_data, k_data), "could not verify data") | |
| 1767 | } | |
| 1768 | ||
| 1769 | napi_value sn_crypto_secretstream_xchacha20poly1305_keygen (napi_env env, napi_callback_info info) { | |
| 1770 | SN_ARGV(1, crypto_secretstream_xchacha20poly1305_keygen) | |
| 1771 | ||
| 1772 | SN_ARGV_TYPEDARRAY(k, 0) | |
| 1773 | ||
| 1774 | SN_ASSERT_LENGTH(k_size, crypto_secretstream_xchacha20poly1305_KEYBYTES, "k") | |
| 1775 | ||
| 1776 | crypto_secretstream_xchacha20poly1305_keygen(k_data); | |
| 1777 | ||
| 1778 | return NULL; | |
| 1779 | } | |
| 1780 | ||
| 1781 | napi_value sn_crypto_secretstream_xchacha20poly1305_init_push (napi_env env, napi_callback_info info) { | |
| 1782 | SN_ARGV(3, crypto_secretstream_xchacha20poly1305_init_push) | |
| 1783 | ||
| 1784 | SN_ARGV_BUFFER_CAST(crypto_secretstream_xchacha20poly1305_state *, state, 0) | |
| 1785 | SN_ARGV_TYPEDARRAY(header, 1) | |
| 1786 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 1787 | ||
| 1788 | SN_THROWS(state_size != sizeof(crypto_secretstream_xchacha20poly1305_state), "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes") | |
| 1789 | SN_ASSERT_LENGTH(header_size, crypto_secretstream_xchacha20poly1305_HEADERBYTES, "header") | |
| 1790 | SN_ASSERT_LENGTH(k_size, crypto_secretstream_xchacha20poly1305_KEYBYTES, "k") | |
| 1791 | ||
| 1792 | SN_RETURN(crypto_secretstream_xchacha20poly1305_init_push(state, header_data, k_data), "initial push failed") | |
| 1793 | } | |
| 1794 | ||
| 1795 | napi_value sn_crypto_secretstream_xchacha20poly1305_push (napi_env env, napi_callback_info info) { | |
| 1796 | SN_ARGV(5, crypto_secretstream_xchacha20poly1305_push) | |
| 1797 | ||
| 1798 | SN_ARGV_BUFFER_CAST(crypto_secretstream_xchacha20poly1305_state *, state, 0) | |
| 1799 | SN_ARGV_TYPEDARRAY(c, 1) | |
| 1800 | SN_ARGV_TYPEDARRAY(m, 2) | |
| 1801 | SN_ARGV_OPTS_TYPEDARRAY(ad, 3) | |
| 1802 | SN_ARGV_UINT8(tag, 4) | |
| 1803 | ||
| 1804 | SN_THROWS(state_size != sizeof(crypto_secretstream_xchacha20poly1305_state), "state must be state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes") | |
| 1805 | SN_ASSERT_MAX_LENGTH(m_size, crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX, "m") | |
| 1806 | SN_THROWS(c_size != m_size + crypto_secretstream_xchacha20poly1305_ABYTES, "c must be 'm.byteLength + crypto_secretstream_xchacha20poly1305_ABYTES' bytes") | |
| 1807 | SN_THROWS(c_size > 0xffffffff, "c.byteLength must be a 32bit integer") | |
| 1808 | ||
| 1809 | unsigned long long clen; | |
| 1810 | SN_CALL(crypto_secretstream_xchacha20poly1305_push(state, c_data, &clen, m_data, m_size, ad_data, ad_size, tag), "push failed") | |
| 1811 | ||
| 1812 | napi_value result; | |
| 1813 | SN_STATUS_THROWS(napi_create_uint32(env, (uint32_t) clen, &result), "") | |
| 1814 | return result; | |
| 1815 | } | |
| 1816 | ||
| 1817 | napi_value sn_crypto_secretstream_xchacha20poly1305_init_pull (napi_env env, napi_callback_info info) { | |
| 1818 | SN_ARGV(3, crypto_secretstream_xchacha20poly1305_init_pull) | |
| 1819 | ||
| 1820 | SN_ARGV_BUFFER_CAST(crypto_secretstream_xchacha20poly1305_state *, state, 0) | |
| 1821 | SN_ARGV_TYPEDARRAY(header, 1) | |
| 1822 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 1823 | ||
| 1824 | SN_THROWS(state_size != sizeof(crypto_secretstream_xchacha20poly1305_state), "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes") | |
| 1825 | SN_ASSERT_LENGTH(header_size, crypto_secretstream_xchacha20poly1305_HEADERBYTES, "header") | |
| 1826 | SN_ASSERT_LENGTH(k_size, crypto_secretstream_xchacha20poly1305_KEYBYTES, "k") | |
| 1827 | ||
| 1828 | SN_RETURN(crypto_secretstream_xchacha20poly1305_init_pull(state, header_data, k_data), "initial pull failed") | |
| 1829 | } | |
| 1830 | ||
| 1831 | napi_value sn_crypto_secretstream_xchacha20poly1305_pull (napi_env env, napi_callback_info info) { | |
| 1832 | SN_ARGV(5, crypto_secretstream_xchacha20poly1305_pull) | |
| 1833 | ||
| 1834 | SN_ARGV_BUFFER_CAST(crypto_secretstream_xchacha20poly1305_state *, state, 0) | |
| 1835 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 1836 | SN_ARGV_TYPEDARRAY(tag, 2) | |
| 1837 | SN_ARGV_TYPEDARRAY(c, 3) | |
| 1838 | SN_ARGV_OPTS_TYPEDARRAY(ad, 4) | |
| 1839 | ||
| 1840 | SN_THROWS(state_size != sizeof(crypto_secretstream_xchacha20poly1305_state), "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes") | |
| 1841 | SN_ASSERT_MIN_LENGTH(c_size, crypto_secretstream_xchacha20poly1305_ABYTES, "c") | |
| 1842 | SN_ASSERT_LENGTH(tag_size, 1, "tag") | |
| 1843 | SN_THROWS(m_size != c_size - crypto_secretstream_xchacha20poly1305_ABYTES, "m must be 'c.byteLength - crypto_secretstream_xchacha20poly1305_ABYTES") | |
| 1844 | SN_THROWS(m_size > 0xffffffff, "m.byteLength must be a 32bit integer") | |
| 1845 | ||
| 1846 | unsigned long long mlen; | |
| 1847 | SN_CALL(crypto_secretstream_xchacha20poly1305_pull(state, m_data, &mlen, tag_data, c_data, c_size, ad_data, ad_size), "pull failed") | |
| 1848 | ||
| 1849 | napi_value result; | |
| 1850 | SN_STATUS_THROWS(napi_create_uint32(env, (uint32_t) mlen, &result), "") | |
| 1851 | return result; | |
| 1852 | } | |
| 1853 | ||
| 1854 | napi_value sn_crypto_secretstream_xchacha20poly1305_rekey (napi_env env, napi_callback_info info) { | |
| 1855 | SN_ARGV(1, crypto_secretstream_xchacha20poly1305_rekey) | |
| 1856 | ||
| 1857 | SN_ARGV_BUFFER_CAST(crypto_secretstream_xchacha20poly1305_state *, state, 0) | |
| 1858 | ||
| 1859 | SN_THROWS(state_size != sizeof(crypto_secretstream_xchacha20poly1305_state), "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes") | |
| 1860 | ||
| 1861 | crypto_secretstream_xchacha20poly1305_rekey(state); | |
| 1862 | ||
| 1863 | return NULL; | |
| 1864 | } | |
| 1865 | ||
| 1866 | typedef struct sn_async_task_t { | |
| 1867 | uv_work_t task; | |
| 1868 | ||
| 1869 | enum { | |
| 1870 | sn_async_task_promise, | |
| 1871 | sn_async_task_callback | |
| 1872 | } type; | |
| 1873 | ||
| 1874 | void *req; | |
| 1875 | int code; | |
| 1876 | ||
| 1877 | napi_deferred deferred; | |
| 1878 | napi_ref cb; | |
| 1879 | } sn_async_task_t; | |
| 1880 | ||
| 1881 | typedef struct sn_async_pwhash_request { | |
| 1882 | napi_env env; | |
| 1883 | napi_ref out_ref; | |
| 1884 | unsigned char *out_data; | |
| 1885 | size_t out_size; | |
| 1886 | napi_ref pwd_ref; | |
| 1887 | const char *pwd_data; | |
| 1888 | size_t pwd_size; | |
| 1889 | napi_ref salt_ref; | |
| 1890 | unsigned char *salt; | |
| 1891 | uint32_t opslimit; | |
| 1892 | uint32_t memlimit; | |
| 1893 | uint32_t alg; | |
| 1894 | } sn_async_pwhash_request; | |
| 1895 | ||
| 1896 | static void async_pwhash_execute (uv_work_t *uv_req) { | |
| 1897 | sn_async_task_t *task = (sn_async_task_t *) uv_req; | |
| 1898 | sn_async_pwhash_request *req = (sn_async_pwhash_request *) task->req; | |
| 1899 | task->code = crypto_pwhash(req->out_data, | |
| 1900 | req->out_size, | |
| 1901 | req->pwd_data, | |
| 1902 | req->pwd_size, | |
| 1903 | req->salt, | |
| 1904 | req->opslimit, | |
| 1905 | req->memlimit, | |
| 1906 | req->alg); | |
| 1907 | } | |
| 1908 | ||
| 1909 | static void async_pwhash_complete (uv_work_t *uv_req, int status) { | |
| 1910 | sn_async_task_t *task = (sn_async_task_t *) uv_req; | |
| 1911 | sn_async_pwhash_request *req = (sn_async_pwhash_request *) task->req; | |
| 1912 | ||
| 1913 | napi_handle_scope scope; | |
| 1914 | napi_open_handle_scope(req->env, &scope); | |
| 1915 | ||
| 1916 | napi_value global; | |
| 1917 | napi_get_global(req->env, &global); | |
| 1918 | ||
| 1919 | SN_ASYNC_COMPLETE("failed to compute password hash") | |
| 1920 | ||
| 1921 | napi_close_handle_scope(req->env, scope); | |
| 1922 | ||
| 1923 | napi_delete_reference(req->env, req->out_ref); | |
| 1924 | napi_delete_reference(req->env, req->pwd_ref); | |
| 1925 | napi_delete_reference(req->env, req->salt_ref); | |
| 1926 | ||
| 1927 | free(req); | |
| 1928 | free(task); | |
| 1929 | } | |
| 1930 | ||
| 1931 | napi_value sn_crypto_pwhash_async (napi_env env, napi_callback_info info) { | |
| 1932 | SN_ARGV_OPTS(6, 7, crypto_pwhash_async) | |
| 1933 | ||
| 1934 | SN_ARGV_BUFFER_CAST(unsigned char *, out, 0) | |
| 1935 | SN_ARGV_BUFFER_CAST(char *, pwd, 1) | |
| 1936 | SN_ARGV_BUFFER_CAST(unsigned char *, salt, 2) | |
| 1937 | SN_ARGV_UINT64(opslimit, 3) | |
| 1938 | SN_ARGV_UINT64(memlimit, 4) | |
| 1939 | SN_ARGV_UINT8(alg, 5) | |
| 1940 | ||
| 1941 | SN_ASSERT_MIN_LENGTH(out_size, crypto_pwhash_BYTES_MIN, "out") | |
| 1942 | SN_ASSERT_MAX_LENGTH(out_size, crypto_pwhash_BYTES_MAX, "out") | |
| 1943 | SN_ASSERT_LENGTH(salt_size, crypto_pwhash_SALTBYTES, "salt") | |
| 1944 | SN_ASSERT_MIN_LENGTH(opslimit, crypto_pwhash_OPSLIMIT_MIN, "opslimit") | |
| 1945 | SN_ASSERT_MAX_LENGTH(opslimit, crypto_pwhash_OPSLIMIT_MAX, "opslimit") | |
| 1946 | SN_ASSERT_MIN_LENGTH(memlimit, crypto_pwhash_MEMLIMIT_MIN, "memlimit") | |
| 1947 | SN_ASSERT_MAX_LENGTH(memlimit, (int64_t) crypto_pwhash_MEMLIMIT_MAX, "memlimit") | |
| 1948 | SN_THROWS(alg < 1 || alg > 2, "alg must be either Argon2i 1.3 or Argon2id 1.3") | |
| 1949 | ||
| 1950 | sn_async_pwhash_request *req = (sn_async_pwhash_request *) malloc(sizeof(sn_async_pwhash_request)); | |
| 1951 | ||
| 1952 | req->env = env; | |
| 1953 | req->out_data = out; | |
| 1954 | req->out_size = out_size; | |
| 1955 | req->pwd_data = pwd; | |
| 1956 | req->pwd_size = pwd_size; | |
| 1957 | req->salt = salt; | |
| 1958 | req->opslimit = opslimit; | |
| 1959 | req->memlimit = memlimit; | |
| 1960 | req->alg = alg; | |
| 1961 | ||
| 1962 | sn_async_task_t *task = (sn_async_task_t *) malloc(sizeof(sn_async_task_t)); | |
| 1963 | SN_ASYNC_TASK(6) | |
| 1964 | ||
| 1965 | SN_STATUS_THROWS(napi_create_reference(env, out_argv, 1, &req->out_ref), "") | |
| 1966 | SN_STATUS_THROWS(napi_create_reference(env, pwd_argv, 1, &req->pwd_ref), "") | |
| 1967 | SN_STATUS_THROWS(napi_create_reference(env, salt_argv, 1, &req->salt_ref), "") | |
| 1968 | ||
| 1969 | SN_QUEUE_TASK(task, async_pwhash_execute, async_pwhash_complete) | |
| 1970 | ||
| 1971 | return promise; | |
| 1972 | } | |
| 1973 | ||
| 1974 | typedef struct sn_async_pwhash_str_request { | |
| 1975 | uv_work_t task; | |
| 1976 | napi_env env; | |
| 1977 | napi_ref out_ref; | |
| 1978 | char *out_data; | |
| 1979 | napi_ref pwd_ref; | |
| 1980 | const char *pwd_data; | |
| 1981 | size_t pwd_size; | |
| 1982 | uint32_t opslimit; | |
| 1983 | uint32_t memlimit; | |
| 1984 | } sn_async_pwhash_str_request; | |
| 1985 | ||
| 1986 | static void async_pwhash_str_execute (uv_work_t *uv_req) { | |
| 1987 | sn_async_task_t *task = (sn_async_task_t *) uv_req; | |
| 1988 | sn_async_pwhash_str_request *req = (sn_async_pwhash_str_request *) task->req; | |
| 1989 | task->code = crypto_pwhash_str(req->out_data, | |
| 1990 | req->pwd_data, | |
| 1991 | req->pwd_size, | |
| 1992 | req->opslimit, | |
| 1993 | req->memlimit); | |
| 1994 | } | |
| 1995 | ||
| 1996 | static void async_pwhash_str_complete (uv_work_t *uv_req, int status) { | |
| 1997 | sn_async_task_t *task = (sn_async_task_t *) uv_req; | |
| 1998 | sn_async_pwhash_str_request *req = (sn_async_pwhash_str_request *) task->req; | |
| 1999 | ||
| 2000 | napi_handle_scope scope; | |
| 2001 | napi_open_handle_scope(req->env, &scope); | |
| 2002 | ||
| 2003 | napi_value global; | |
| 2004 | napi_get_global(req->env, &global); | |
| 2005 | ||
| 2006 | SN_ASYNC_COMPLETE("failed to compute password hash") | |
| 2007 | ||
| 2008 | napi_close_handle_scope(req->env, scope); | |
| 2009 | ||
| 2010 | napi_delete_reference(req->env, req->out_ref); | |
| 2011 | napi_delete_reference(req->env, req->pwd_ref); | |
| 2012 | ||
| 2013 | free(req); | |
| 2014 | free(task); | |
| 2015 | } | |
| 2016 | ||
| 2017 | napi_value sn_crypto_pwhash_str_async (napi_env env, napi_callback_info info) { | |
| 2018 | SN_ARGV_OPTS(4, 5, crypto_pwhash_str_async) | |
| 2019 | ||
| 2020 | SN_ARGV_BUFFER_CAST(char *, out, 0) | |
| 2021 | SN_ARGV_BUFFER_CAST(char *, pwd, 1) | |
| 2022 | SN_ARGV_UINT64(opslimit, 2) | |
| 2023 | SN_ARGV_UINT64(memlimit, 3) | |
| 2024 | ||
| 2025 | SN_ASSERT_LENGTH(out_size, crypto_pwhash_STRBYTES, "out") | |
| 2026 | SN_ASSERT_MIN_LENGTH(opslimit, crypto_pwhash_OPSLIMIT_MIN, "opslimit") | |
| 2027 | SN_ASSERT_MAX_LENGTH(opslimit, crypto_pwhash_OPSLIMIT_MAX, "opslimit") | |
| 2028 | SN_ASSERT_MIN_LENGTH(memlimit, crypto_pwhash_MEMLIMIT_MIN, "memlimit") | |
| 2029 | SN_ASSERT_MAX_LENGTH(memlimit, (int64_t) crypto_pwhash_MEMLIMIT_MAX, "memlimit") | |
| 2030 | ||
| 2031 | sn_async_pwhash_str_request *req = (sn_async_pwhash_str_request *) malloc(sizeof(sn_async_pwhash_str_request)); | |
| 2032 | req->env = env; | |
| 2033 | req->out_data = out; | |
| 2034 | req->pwd_data = pwd; | |
| 2035 | req->pwd_size = pwd_size; | |
| 2036 | req->opslimit = opslimit; | |
| 2037 | req->memlimit = memlimit; | |
| 2038 | ||
| 2039 | sn_async_task_t *task = (sn_async_task_t *) malloc(sizeof(sn_async_task_t)); | |
| 2040 | SN_ASYNC_TASK(4) | |
| 2041 | ||
| 2042 | SN_STATUS_THROWS(napi_create_reference(env, out_argv, 1, &req->out_ref), "") | |
| 2043 | SN_STATUS_THROWS(napi_create_reference(env, pwd_argv, 1, &req->pwd_ref), "") | |
| 2044 | ||
| 2045 | SN_QUEUE_TASK(task, async_pwhash_str_execute, async_pwhash_str_complete) | |
| 2046 | ||
| 2047 | return promise; | |
| 2048 | } | |
| 2049 | ||
| 2050 | typedef struct sn_async_pwhash_str_verify_request { | |
| 2051 | uv_work_t task; | |
| 2052 | napi_env env; | |
| 2053 | napi_ref str_ref; | |
| 2054 | char *str_data; | |
| 2055 | napi_ref pwd_ref; | |
| 2056 | const char *pwd_data; | |
| 2057 | size_t pwd_size; | |
| 2058 | } sn_async_pwhash_str_verify_request; | |
| 2059 | ||
| 2060 | static void async_pwhash_str_verify_execute (uv_work_t *uv_req) { | |
| 2061 | sn_async_task_t *task = (sn_async_task_t *) uv_req; | |
| 2062 | sn_async_pwhash_str_verify_request *req = (sn_async_pwhash_str_verify_request *) task->req; | |
| 2063 | task->code = crypto_pwhash_str_verify(req->str_data, req->pwd_data, req->pwd_size); | |
| 2064 | } | |
| 2065 | ||
| 2066 | static void async_pwhash_str_verify_complete (uv_work_t *uv_req, int status) { | |
| 2067 | sn_async_task_t *task = (sn_async_task_t *) uv_req; | |
| 2068 | sn_async_pwhash_str_verify_request *req = (sn_async_pwhash_str_verify_request *) task->req; | |
| 2069 | ||
| 2070 | napi_handle_scope scope; | |
| 2071 | napi_open_handle_scope(req->env, &scope); | |
| 2072 | ||
| 2073 | napi_value global; | |
| 2074 | napi_get_global(req->env, &global); | |
| 2075 | ||
| 2076 | napi_value argv[2]; | |
| 2077 | ||
| 2078 | // Due to the way that crypto_pwhash_str_verify signals error different | |
| 2079 | // from a verification mismatch, we will count all errors as mismatch. | |
| 2080 | // The other possible error is wrong argument sizes, which is protected | |
| 2081 | // by macros above | |
| 2082 | napi_get_null(req->env, &argv[0]); | |
| 2083 | napi_get_boolean(req->env, task->code == 0, &argv[1]); | |
| 2084 | ||
| 2085 | switch (task->type) { | |
| 2086 | case sn_async_task_promise: { | |
| 2087 | napi_resolve_deferred(req->env, task->deferred, argv[1]); | |
| 2088 | task->deferred = NULL; | |
| 2089 | break; | |
| 2090 | } | |
| 2091 | ||
| 2092 | case sn_async_task_callback: { | |
| 2093 | napi_value callback; | |
| 2094 | napi_get_reference_value(req->env, task->cb, &callback); | |
| 2095 | ||
| 2096 | napi_value return_val; | |
| 2097 | SN_CALL_FUNCTION(req->env, global, callback, 2, argv, &return_val) | |
| 2098 | break; | |
| 2099 | } | |
| 2100 | } | |
| 2101 | ||
| 2102 | napi_close_handle_scope(req->env, scope); | |
| 2103 | ||
| 2104 | napi_delete_reference(req->env, req->str_ref); | |
| 2105 | napi_delete_reference(req->env, req->pwd_ref); | |
| 2106 | ||
| 2107 | free(req); | |
| 2108 | free(task); | |
| 2109 | } | |
| 2110 | ||
| 2111 | napi_value sn_crypto_pwhash_str_verify_async (napi_env env, napi_callback_info info) { | |
| 2112 | SN_ARGV_OPTS(2, 3, crypto_pwhash_str_async) | |
| 2113 | ||
| 2114 | SN_ARGV_BUFFER_CAST(char *, str, 0) | |
| 2115 | SN_ARGV_BUFFER_CAST(char *, pwd, 1) | |
| 2116 | ||
| 2117 | SN_ASSERT_LENGTH(str_size, crypto_pwhash_STRBYTES, "str") | |
| 2118 | ||
| 2119 | sn_async_pwhash_str_verify_request *req = (sn_async_pwhash_str_verify_request *) malloc(sizeof(sn_async_pwhash_str_verify_request)); | |
| 2120 | req->env = env; | |
| 2121 | req->str_data = str; | |
| 2122 | req->pwd_data = pwd; | |
| 2123 | req->pwd_size = pwd_size; | |
| 2124 | ||
| 2125 | sn_async_task_t *task = (sn_async_task_t *) malloc(sizeof(sn_async_task_t)); | |
| 2126 | SN_ASYNC_TASK(2) | |
| 2127 | ||
| 2128 | SN_STATUS_THROWS(napi_create_reference(env, str_argv, 1, &req->str_ref), "") | |
| 2129 | SN_STATUS_THROWS(napi_create_reference(env, pwd_argv, 1, &req->pwd_ref), "") | |
| 2130 | ||
| 2131 | SN_QUEUE_TASK(task, async_pwhash_str_verify_execute, async_pwhash_str_verify_complete) | |
| 2132 | ||
| 2133 | return promise; | |
| 2134 | } | |
| 2135 | ||
| 2136 | typedef struct sn_async_pwhash_scryptsalsa208sha256_request { | |
| 2137 | uv_work_t task; | |
| 2138 | napi_env env; | |
| 2139 | napi_ref out_ref; | |
| 2140 | unsigned char *out_data; | |
| 2141 | size_t out_size; | |
| 2142 | napi_ref pwd_ref; | |
| 2143 | const char *pwd_data; | |
| 2144 | size_t pwd_size; | |
| 2145 | napi_ref salt_ref; | |
| 2146 | unsigned char *salt; | |
| 2147 | uint32_t opslimit; | |
| 2148 | uint32_t memlimit; | |
| 2149 | } sn_async_pwhash_scryptsalsa208sha256_request; | |
| 2150 | ||
| 2151 | static void async_pwhash_scryptsalsa208sha256_execute (uv_work_t *uv_req) { | |
| 2152 | sn_async_task_t *task = (sn_async_task_t *) uv_req; | |
| 2153 | sn_async_pwhash_scryptsalsa208sha256_request *req = (sn_async_pwhash_scryptsalsa208sha256_request *) task->req; | |
| 2154 | task->code = crypto_pwhash_scryptsalsa208sha256(req->out_data, | |
| 2155 | req->out_size, | |
| 2156 | req-> pwd_data, | |
| 2157 | req->pwd_size, | |
| 2158 | req->salt, | |
| 2159 | req->opslimit, | |
| 2160 | req->memlimit); | |
| 2161 | } | |
| 2162 | ||
| 2163 | static void async_pwhash_scryptsalsa208sha256_complete (uv_work_t *uv_req, int status) { | |
| 2164 | sn_async_task_t *task = (sn_async_task_t *) uv_req; | |
| 2165 | sn_async_pwhash_scryptsalsa208sha256_request *req = (sn_async_pwhash_scryptsalsa208sha256_request *) task->req; | |
| 2166 | ||
| 2167 | napi_handle_scope scope; | |
| 2168 | napi_open_handle_scope(req->env, &scope); | |
| 2169 | ||
| 2170 | napi_value global; | |
| 2171 | napi_get_global(req->env, &global); | |
| 2172 | ||
| 2173 | SN_ASYNC_COMPLETE("failed to compute password hash") | |
| 2174 | ||
| 2175 | napi_close_handle_scope(req->env, scope); | |
| 2176 | ||
| 2177 | napi_delete_reference(req->env, req->out_ref); | |
| 2178 | napi_delete_reference(req->env, req->pwd_ref); | |
| 2179 | napi_delete_reference(req->env, req->salt_ref); | |
| 2180 | ||
| 2181 | free(req); | |
| 2182 | free(task); | |
| 2183 | } | |
| 2184 | ||
| 2185 | napi_value sn_crypto_pwhash_scryptsalsa208sha256_async (napi_env env, napi_callback_info info) { | |
| 2186 | SN_ARGV_OPTS(5, 6, crypto_pwhash_scryptsalsa208sha256_async) | |
| 2187 | ||
| 2188 | SN_ARGV_BUFFER_CAST(unsigned char *, out, 0) | |
| 2189 | SN_ARGV_BUFFER_CAST(char *, pwd, 1) | |
| 2190 | SN_ARGV_BUFFER_CAST(unsigned char *, salt, 2) | |
| 2191 | SN_ARGV_UINT64(opslimit, 3) | |
| 2192 | SN_ARGV_UINT64(memlimit, 4) | |
| 2193 | ||
| 2194 | SN_ASSERT_MIN_LENGTH(out_size, crypto_pwhash_scryptsalsa208sha256_BYTES_MIN, "out") | |
| 2195 | SN_ASSERT_MAX_LENGTH(out_size, crypto_pwhash_scryptsalsa208sha256_BYTES_MAX, "out") | |
| 2196 | SN_ASSERT_LENGTH(salt_size, crypto_pwhash_scryptsalsa208sha256_SALTBYTES, "salt") | |
| 2197 | SN_ASSERT_MIN_LENGTH(opslimit, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, "opslimit") | |
| 2198 | SN_ASSERT_MAX_LENGTH(opslimit, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, "opslimit") | |
| 2199 | SN_ASSERT_MIN_LENGTH(memlimit, crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, "memlimit") | |
| 2200 | SN_ASSERT_MAX_LENGTH(memlimit, (int64_t) crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, "memlimit") | |
| 2201 | ||
| 2202 | sn_async_pwhash_scryptsalsa208sha256_request *req = (sn_async_pwhash_scryptsalsa208sha256_request *) malloc(sizeof(sn_async_pwhash_scryptsalsa208sha256_request)); | |
| 2203 | req->env = env; | |
| 2204 | req->out_data = out; | |
| 2205 | req->out_size = out_size; | |
| 2206 | req->pwd_data = pwd; | |
| 2207 | req->pwd_size = pwd_size; | |
| 2208 | req-> salt = salt; | |
| 2209 | req->opslimit = opslimit; | |
| 2210 | req->memlimit = memlimit; | |
| 2211 | ||
| 2212 | sn_async_task_t *task = (sn_async_task_t *) malloc(sizeof(sn_async_task_t)); | |
| 2213 | SN_ASYNC_TASK(5) | |
| 2214 | ||
| 2215 | SN_STATUS_THROWS(napi_create_reference(env, out_argv, 1, &req->out_ref), "") | |
| 2216 | SN_STATUS_THROWS(napi_create_reference(env, pwd_argv, 1, &req->pwd_ref), "") | |
| 2217 | SN_STATUS_THROWS(napi_create_reference(env, salt_argv, 1, &req->salt_ref), "") | |
| 2218 | ||
| 2219 | SN_QUEUE_TASK(task, async_pwhash_scryptsalsa208sha256_execute, async_pwhash_scryptsalsa208sha256_complete) | |
| 2220 | ||
| 2221 | return promise; | |
| 2222 | } | |
| 2223 | ||
| 2224 | typedef struct sn_async_pwhash_scryptsalsa208sha256_str_request { | |
| 2225 | uv_work_t task; | |
| 2226 | napi_env env; | |
| 2227 | napi_ref out_ref; | |
| 2228 | char *out_data; | |
| 2229 | napi_ref pwd_ref; | |
| 2230 | const char *pwd_data; | |
| 2231 | size_t pwd_size; | |
| 2232 | uint32_t opslimit; | |
| 2233 | uint32_t memlimit; | |
| 2234 | } sn_async_pwhash_scryptsalsa208sha256_str_request; | |
| 2235 | ||
| 2236 | static void async_pwhash_scryptsalsa208sha256_str_execute (uv_work_t *uv_req) { | |
| 2237 | sn_async_task_t *task = (sn_async_task_t *) uv_req; | |
| 2238 | sn_async_pwhash_scryptsalsa208sha256_str_request *req = (sn_async_pwhash_scryptsalsa208sha256_str_request *) task->req; | |
| 2239 | task->code = crypto_pwhash_scryptsalsa208sha256_str(req->out_data, | |
| 2240 | req->pwd_data, | |
| 2241 | req->pwd_size, | |
| 2242 | req->opslimit, | |
| 2243 | req->memlimit); | |
| 2244 | } | |
| 2245 | ||
| 2246 | static void async_pwhash_scryptsalsa208sha256_str_complete (uv_work_t *uv_req, int status) { | |
| 2247 | sn_async_task_t *task = (sn_async_task_t *) uv_req; | |
| 2248 | sn_async_pwhash_scryptsalsa208sha256_str_request *req = (sn_async_pwhash_scryptsalsa208sha256_str_request *) task->req; | |
| 2249 | ||
| 2250 | napi_handle_scope scope; | |
| 2251 | napi_open_handle_scope(req->env, &scope); | |
| 2252 | ||
| 2253 | napi_value global; | |
| 2254 | napi_get_global(req->env, &global); | |
| 2255 | ||
| 2256 | SN_ASYNC_COMPLETE("failed to compute password hash") | |
| 2257 | ||
| 2258 | napi_close_handle_scope(req->env, scope); | |
| 2259 | ||
| 2260 | napi_delete_reference(req->env, req->out_ref); | |
| 2261 | napi_delete_reference(req->env, req->pwd_ref); | |
| 2262 | ||
| 2263 | free(req); | |
| 2264 | free(task); | |
| 2265 | } | |
| 2266 | ||
| 2267 | napi_value sn_crypto_pwhash_scryptsalsa208sha256_str_async (napi_env env, napi_callback_info info) { | |
| 2268 | SN_ARGV_OPTS(4, 5, crypto_pwhash_scryptsalsa208sha256_str_async) | |
| 2269 | ||
| 2270 | SN_ARGV_BUFFER_CAST(char *, out, 0) | |
| 2271 | SN_ARGV_BUFFER_CAST(char *, pwd, 1) | |
| 2272 | SN_ARGV_UINT64(opslimit, 2) | |
| 2273 | SN_ARGV_UINT64(memlimit, 3) | |
| 2274 | ||
| 2275 | SN_ASSERT_LENGTH(out_size, crypto_pwhash_scryptsalsa208sha256_STRBYTES, "out") | |
| 2276 | SN_ASSERT_MIN_LENGTH(opslimit, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, "opslimit") | |
| 2277 | SN_ASSERT_MAX_LENGTH(opslimit, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, "opslimit") | |
| 2278 | SN_ASSERT_MIN_LENGTH(memlimit, crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, "memlimit") | |
| 2279 | SN_ASSERT_MAX_LENGTH(memlimit, (int64_t) crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, "memlimit") | |
| 2280 | ||
| 2281 | sn_async_pwhash_scryptsalsa208sha256_str_request *req = (sn_async_pwhash_scryptsalsa208sha256_str_request *) malloc(sizeof(sn_async_pwhash_scryptsalsa208sha256_str_request)); | |
| 2282 | req->env = env; | |
| 2283 | req->out_data = out; | |
| 2284 | req->pwd_data = pwd; | |
| 2285 | req->pwd_size = pwd_size; | |
| 2286 | req->opslimit = opslimit; | |
| 2287 | req->memlimit = memlimit; | |
| 2288 | ||
| 2289 | sn_async_task_t *task = (sn_async_task_t *) malloc(sizeof(sn_async_task_t)); | |
| 2290 | SN_ASYNC_TASK(4) | |
| 2291 | ||
| 2292 | SN_STATUS_THROWS(napi_create_reference(env, out_argv, 1, &req->out_ref), "") | |
| 2293 | SN_STATUS_THROWS(napi_create_reference(env, pwd_argv, 1, &req->pwd_ref), "") | |
| 2294 | ||
| 2295 | SN_QUEUE_TASK(task, async_pwhash_scryptsalsa208sha256_str_execute, async_pwhash_scryptsalsa208sha256_str_complete) | |
| 2296 | ||
| 2297 | return promise; | |
| 2298 | } | |
| 2299 | ||
| 2300 | typedef struct sn_async_pwhash_scryptsalsa208sha256_str_verify_request { | |
| 2301 | uv_work_t task; | |
| 2302 | napi_env env; | |
| 2303 | napi_ref str_ref; | |
| 2304 | char *str_data; | |
| 2305 | napi_ref pwd_ref; | |
| 2306 | const char *pwd_data; | |
| 2307 | size_t pwd_size; | |
| 2308 | } sn_async_pwhash_scryptsalsa208sha256_str_verify_request; | |
| 2309 | ||
| 2310 | static void async_pwhash_scryptsalsa208sha256_str_verify_execute (uv_work_t *uv_req) { | |
| 2311 | sn_async_task_t *task = (sn_async_task_t *) uv_req; | |
| 2312 | sn_async_pwhash_scryptsalsa208sha256_str_verify_request *req = (sn_async_pwhash_scryptsalsa208sha256_str_verify_request *) task->req; | |
| 2313 | task->code = crypto_pwhash_scryptsalsa208sha256_str_verify(req->str_data, req->pwd_data, req->pwd_size); | |
| 2314 | } | |
| 2315 | ||
| 2316 | static void async_pwhash_scryptsalsa208sha256_str_verify_complete (uv_work_t *uv_req, int status) { | |
| 2317 | sn_async_task_t *task = (sn_async_task_t *) uv_req; | |
| 2318 | sn_async_pwhash_scryptsalsa208sha256_str_verify_request *req = (sn_async_pwhash_scryptsalsa208sha256_str_verify_request *) task->req; | |
| 2319 | ||
| 2320 | napi_handle_scope scope; | |
| 2321 | napi_open_handle_scope(req->env, &scope); | |
| 2322 | ||
| 2323 | napi_value global; | |
| 2324 | napi_get_global(req->env, &global); | |
| 2325 | ||
| 2326 | napi_value argv[2]; | |
| 2327 | ||
| 2328 | // Due to the way that crypto_pwhash_scryptsalsa208sha256_str_verify | |
| 2329 | // signal serror different from a verification mismatch, we will count | |
| 2330 | // all errors as mismatch. The other possible error is wrong argument | |
| 2331 | // sizes, which is protected by macros above | |
| 2332 | napi_get_null(req->env, &argv[0]); | |
| 2333 | napi_get_boolean(req->env, task->code == 0, &argv[1]); | |
| 2334 | ||
| 2335 | switch (task->type) { | |
| 2336 | case sn_async_task_promise: { | |
| 2337 | napi_resolve_deferred(req->env, task->deferred, argv[1]); | |
| 2338 | task->deferred = NULL; | |
| 2339 | break; | |
| 2340 | } | |
| 2341 | ||
| 2342 | case sn_async_task_callback: { | |
| 2343 | napi_value callback; | |
| 2344 | napi_get_reference_value(req->env, task->cb, &callback); | |
| 2345 | ||
| 2346 | napi_value return_val; | |
| 2347 | SN_CALL_FUNCTION(req->env, global, callback, 2, argv, &return_val) | |
| 2348 | break; | |
| 2349 | } | |
| 2350 | } | |
| 2351 | ||
| 2352 | napi_close_handle_scope(req->env, scope); | |
| 2353 | ||
| 2354 | napi_delete_reference(req->env, req->str_ref); | |
| 2355 | napi_delete_reference(req->env, req->pwd_ref); | |
| 2356 | ||
| 2357 | free(req); | |
| 2358 | free(task); | |
| 2359 | } | |
| 2360 | ||
| 2361 | napi_value sn_crypto_pwhash_scryptsalsa208sha256_str_verify_async (napi_env env, napi_callback_info info) { | |
| 2362 | SN_ARGV_OPTS(2, 3, crypto_pwhash_scryptsalsa208sha256_str_async) | |
| 2363 | ||
| 2364 | SN_ARGV_BUFFER_CAST(char *, str, 0) | |
| 2365 | SN_ARGV_BUFFER_CAST(char *, pwd, 1) | |
| 2366 | ||
| 2367 | SN_ASSERT_LENGTH(str_size, crypto_pwhash_scryptsalsa208sha256_STRBYTES, "str") | |
| 2368 | ||
| 2369 | sn_async_pwhash_scryptsalsa208sha256_str_verify_request *req = (sn_async_pwhash_scryptsalsa208sha256_str_verify_request *) malloc(sizeof(sn_async_pwhash_scryptsalsa208sha256_str_verify_request)); | |
| 2370 | req->env = env; | |
| 2371 | req->str_data = str; | |
| 2372 | req->pwd_data = pwd; | |
| 2373 | req->pwd_size = pwd_size; | |
| 2374 | ||
| 2375 | sn_async_task_t *task = (sn_async_task_t *) malloc(sizeof(sn_async_task_t)); | |
| 2376 | SN_ASYNC_TASK(2) | |
| 2377 | ||
| 2378 | SN_STATUS_THROWS(napi_create_reference(env, str_argv, 1, &req->str_ref), "") | |
| 2379 | SN_STATUS_THROWS(napi_create_reference(env, pwd_argv, 1, &req->pwd_ref), "") | |
| 2380 | ||
| 2381 | SN_QUEUE_TASK(task, async_pwhash_scryptsalsa208sha256_str_verify_execute, async_pwhash_scryptsalsa208sha256_str_verify_complete) | |
| 2382 | ||
| 2383 | return promise; | |
| 2384 | } | |
| 2385 | ||
| 2386 | typedef struct sn_crypto_stream_xor_state { | |
| 2387 | unsigned char n[crypto_stream_NONCEBYTES]; | |
| 2388 | unsigned char k[crypto_stream_KEYBYTES]; | |
| 2389 | unsigned char next_block[64]; | |
| 2390 | int remainder; | |
| 2391 | uint64_t block_counter; | |
| 2392 | } sn_crypto_stream_xor_state; | |
| 2393 | ||
| 2394 | napi_value sn_crypto_stream_xor_wrap_init (napi_env env, napi_callback_info info) { | |
| 2395 | SN_ARGV(3, crypto_stream_xor_instance_init) | |
| 2396 | ||
| 2397 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_xor_state *, state, 0) | |
| 2398 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 2399 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 2400 | ||
| 2401 | SN_THROWS(state_size != sizeof(sn_crypto_stream_xor_state), "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes") | |
| 2402 | SN_ASSERT_LENGTH(n_size, crypto_stream_NONCEBYTES, "n") | |
| 2403 | SN_ASSERT_LENGTH(k_size, crypto_stream_KEYBYTES, "k") | |
| 2404 | ||
| 2405 | state->remainder = 0; | |
| 2406 | state->block_counter = 0; | |
| 2407 | memcpy(state->n, n_data, crypto_stream_NONCEBYTES); | |
| 2408 | memcpy(state->k, k_data, crypto_stream_KEYBYTES); | |
| 2409 | ||
| 2410 | return NULL; | |
| 2411 | } | |
| 2412 | ||
| 2413 | napi_value sn_crypto_stream_xor_wrap_update (napi_env env, napi_callback_info info) { | |
| 2414 | SN_ARGV(3, crypto_stream_xor_instance_init) | |
| 2415 | ||
| 2416 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_xor_state *, state, 0) | |
| 2417 | SN_ARGV_BUFFER_CAST(unsigned char *, c, 1) | |
| 2418 | SN_ARGV_BUFFER_CAST(unsigned char *, m, 2) | |
| 2419 | ||
| 2420 | SN_THROWS(state_size != sizeof(sn_crypto_stream_xor_state), "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes") | |
| 2421 | SN_THROWS(c_size != m_size, "c must be 'm.byteLength' bytes") | |
| 2422 | ||
| 2423 | unsigned char *next_block = state->next_block; | |
| 2424 | ||
| 2425 | if (state->remainder) { | |
| 2426 | uint64_t offset = 0; | |
| 2427 | int rem = state->remainder; | |
| 2428 | ||
| 2429 | while (rem < 64 && offset < m_size) { | |
| 2430 | c[offset] = next_block[rem] ^ m[offset]; | |
| 2431 | offset++; | |
| 2432 | rem++; | |
| 2433 | } | |
| 2434 | ||
| 2435 | c += offset; | |
| 2436 | m += offset; | |
| 2437 | m_size -= offset; | |
| 2438 | state->remainder = rem == 64 ? 0 : rem; | |
| 2439 | ||
| 2440 | if (!m_size) return NULL; | |
| 2441 | } | |
| 2442 | ||
| 2443 | state->remainder = m_size & 63; | |
| 2444 | m_size -= state->remainder; | |
| 2445 | crypto_stream_xsalsa20_xor_ic(c, m, m_size, state->n, state->block_counter, state->k); | |
| 2446 | state->block_counter += m_size / 64; | |
| 2447 | ||
| 2448 | if (state->remainder) { | |
| 2449 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 2450 | memcpy(next_block, m + m_size, state->remainder); | |
| 2451 | ||
| 2452 | crypto_stream_xsalsa20_xor_ic(next_block, next_block, 64, state->n, state->block_counter, state->k); | |
| 2453 | memcpy(c + m_size, next_block, state->remainder); | |
| 2454 | ||
| 2455 | state->block_counter++; | |
| 2456 | } | |
| 2457 | ||
| 2458 | return NULL; | |
| 2459 | } | |
| 2460 | ||
| 2461 | napi_value sn_crypto_stream_xor_wrap_final (napi_env env, napi_callback_info info) { | |
| 2462 | SN_ARGV(1, crypto_stream_xor_instance_init) | |
| 2463 | ||
| 2464 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_xor_state *, state, 0) | |
| 2465 | ||
| 2466 | SN_THROWS(state_size != sizeof(sn_crypto_stream_xor_state), "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes") | |
| 2467 | ||
| 2468 | sodium_memzero(state->n, sizeof(state->n)); | |
| 2469 | sodium_memzero(state->k, sizeof(state->k)); | |
| 2470 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 2471 | state->remainder = 0; | |
| 2472 | ||
| 2473 | return NULL; | |
| 2474 | } | |
| 2475 | ||
| 2476 | typedef struct sn_crypto_stream_chacha20_xor_state { | |
| 2477 | unsigned char n[crypto_stream_chacha20_NONCEBYTES]; | |
| 2478 | unsigned char k[crypto_stream_chacha20_KEYBYTES]; | |
| 2479 | unsigned char next_block[64]; | |
| 2480 | int remainder; | |
| 2481 | uint64_t block_counter; | |
| 2482 | } sn_crypto_stream_chacha20_xor_state; | |
| 2483 | ||
| 2484 | napi_value sn_crypto_stream_chacha20_xor_wrap_init (napi_env env, napi_callback_info info) { | |
| 2485 | SN_ARGV(3, crypto_stream_chacha20_xor_instance_init) | |
| 2486 | ||
| 2487 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_chacha20_xor_state *, state, 0) | |
| 2488 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 2489 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 2490 | ||
| 2491 | SN_THROWS(state_size != sizeof(sn_crypto_stream_chacha20_xor_state), "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes") | |
| 2492 | SN_ASSERT_LENGTH(n_size, crypto_stream_chacha20_NONCEBYTES, "n") | |
| 2493 | SN_ASSERT_LENGTH(k_size, crypto_stream_chacha20_KEYBYTES, "k") | |
| 2494 | ||
| 2495 | state->remainder = 0; | |
| 2496 | state->block_counter = 0; | |
| 2497 | memcpy(state->n, n_data, crypto_stream_chacha20_NONCEBYTES); | |
| 2498 | memcpy(state->k, k_data, crypto_stream_chacha20_KEYBYTES); | |
| 2499 | ||
| 2500 | return NULL; | |
| 2501 | } | |
| 2502 | ||
| 2503 | napi_value sn_crypto_stream_chacha20_xor_wrap_update (napi_env env, napi_callback_info info) { | |
| 2504 | SN_ARGV(3, crypto_stream_chacha20_xor_instance_init) | |
| 2505 | ||
| 2506 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_chacha20_xor_state *, state, 0) | |
| 2507 | SN_ARGV_BUFFER_CAST(unsigned char *, c, 1) | |
| 2508 | SN_ARGV_BUFFER_CAST(unsigned char *, m, 2) | |
| 2509 | ||
| 2510 | SN_THROWS(state_size != sizeof(sn_crypto_stream_chacha20_xor_state), "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes") | |
| 2511 | SN_THROWS(c_size != m_size, "c must be 'm.byteLength' bytes") | |
| 2512 | ||
| 2513 | unsigned char *next_block = state->next_block; | |
| 2514 | ||
| 2515 | if (state->remainder) { | |
| 2516 | uint64_t offset = 0; | |
| 2517 | int rem = state->remainder; | |
| 2518 | ||
| 2519 | while (rem < 64 && offset < m_size) { | |
| 2520 | c[offset] = next_block[rem] ^ m[offset]; | |
| 2521 | offset++; | |
| 2522 | rem++; | |
| 2523 | } | |
| 2524 | ||
| 2525 | c += offset; | |
| 2526 | m += offset; | |
| 2527 | m_size -= offset; | |
| 2528 | state->remainder = rem == 64 ? 0 : rem; | |
| 2529 | ||
| 2530 | if (!m_size) return NULL; | |
| 2531 | } | |
| 2532 | ||
| 2533 | state->remainder = m_size & 63; | |
| 2534 | m_size -= state->remainder; | |
| 2535 | crypto_stream_chacha20_xor_ic(c, m, m_size, state->n, state->block_counter, state->k); | |
| 2536 | state->block_counter += m_size / 64; | |
| 2537 | ||
| 2538 | if (state->remainder) { | |
| 2539 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 2540 | memcpy(next_block, m + m_size, state->remainder); | |
| 2541 | ||
| 2542 | crypto_stream_chacha20_xor_ic(next_block, next_block, 64, state->n, state->block_counter, state->k); | |
| 2543 | memcpy(c + m_size, next_block, state->remainder); | |
| 2544 | ||
| 2545 | state->block_counter++; | |
| 2546 | } | |
| 2547 | ||
| 2548 | return NULL; | |
| 2549 | } | |
| 2550 | ||
| 2551 | napi_value sn_crypto_stream_chacha20_xor_wrap_final (napi_env env, napi_callback_info info) { | |
| 2552 | SN_ARGV(1, crypto_stream_chacha20_xor_instance_init) | |
| 2553 | ||
| 2554 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_chacha20_xor_state *, state, 0) | |
| 2555 | ||
| 2556 | SN_THROWS(state_size != sizeof(sn_crypto_stream_chacha20_xor_state), "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes") | |
| 2557 | ||
| 2558 | sodium_memzero(state->n, sizeof(state->n)); | |
| 2559 | sodium_memzero(state->k, sizeof(state->k)); | |
| 2560 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 2561 | state->remainder = 0; | |
| 2562 | ||
| 2563 | return NULL; | |
| 2564 | } | |
| 2565 | ||
| 2566 | typedef struct sn_crypto_stream_chacha20_ietf_xor_state { | |
| 2567 | unsigned char n[crypto_stream_chacha20_ietf_NONCEBYTES]; | |
| 2568 | unsigned char k[crypto_stream_chacha20_ietf_KEYBYTES]; | |
| 2569 | unsigned char next_block[64]; | |
| 2570 | int remainder; | |
| 2571 | uint64_t block_counter; | |
| 2572 | } sn_crypto_stream_chacha20_ietf_xor_state; | |
| 2573 | ||
| 2574 | napi_value sn_crypto_stream_chacha20_ietf_xor_wrap_init (napi_env env, napi_callback_info info) { | |
| 2575 | SN_ARGV(3, crypto_stream_chacha20_ietf_xor_wrap_init) | |
| 2576 | ||
| 2577 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_chacha20_ietf_xor_state *, state, 0) | |
| 2578 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 2579 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 2580 | ||
| 2581 | SN_THROWS(state_size != sizeof(sn_crypto_stream_chacha20_ietf_xor_state), "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes") | |
| 2582 | SN_ASSERT_LENGTH(n_size, crypto_stream_chacha20_ietf_NONCEBYTES, "n") | |
| 2583 | SN_ASSERT_LENGTH(k_size, crypto_stream_chacha20_ietf_KEYBYTES, "k") | |
| 2584 | ||
| 2585 | state->remainder = 0; | |
| 2586 | state->block_counter = 0; | |
| 2587 | memcpy(state->n, n_data, crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 2588 | memcpy(state->k, k_data, crypto_stream_chacha20_ietf_KEYBYTES); | |
| 2589 | ||
| 2590 | return NULL; | |
| 2591 | } | |
| 2592 | ||
| 2593 | napi_value sn_crypto_stream_chacha20_ietf_xor_wrap_update (napi_env env, napi_callback_info info) { | |
| 2594 | SN_ARGV(3, crypto_stream_chacha20_ietf_xor_wrap_update) | |
| 2595 | ||
| 2596 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_chacha20_ietf_xor_state *, state, 0) | |
| 2597 | SN_ARGV_BUFFER_CAST(unsigned char *, c, 1) | |
| 2598 | SN_ARGV_BUFFER_CAST(unsigned char *, m, 2) | |
| 2599 | ||
| 2600 | SN_THROWS(state_size != sizeof(sn_crypto_stream_chacha20_ietf_xor_state), "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes") | |
| 2601 | SN_THROWS(c_size != m_size, "c must be 'm.byteLength' bytes") | |
| 2602 | ||
| 2603 | unsigned char *next_block = state->next_block; | |
| 2604 | ||
| 2605 | if (state->remainder) { | |
| 2606 | uint64_t offset = 0; | |
| 2607 | int rem = state->remainder; | |
| 2608 | ||
| 2609 | while (rem < 64 && offset < m_size) { | |
| 2610 | c[offset] = next_block[rem] ^ m[offset]; | |
| 2611 | offset++; | |
| 2612 | rem++; | |
| 2613 | } | |
| 2614 | ||
| 2615 | c += offset; | |
| 2616 | m += offset; | |
| 2617 | m_size -= offset; | |
| 2618 | state->remainder = rem == 64 ? 0 : rem; | |
| 2619 | ||
| 2620 | if (!m_size) return NULL; | |
| 2621 | } | |
| 2622 | ||
| 2623 | state->remainder = m_size & 63; | |
| 2624 | m_size -= state->remainder; | |
| 2625 | crypto_stream_chacha20_ietf_xor_ic(c, m, m_size, state->n, state->block_counter, state->k); | |
| 2626 | state->block_counter += m_size / 64; | |
| 2627 | ||
| 2628 | if (state->remainder) { | |
| 2629 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 2630 | memcpy(next_block, m + m_size, state->remainder); | |
| 2631 | ||
| 2632 | crypto_stream_chacha20_ietf_xor_ic(next_block, next_block, 64, state->n, state->block_counter, state->k); | |
| 2633 | memcpy(c + m_size, next_block, state->remainder); | |
| 2634 | ||
| 2635 | state->block_counter++; | |
| 2636 | } | |
| 2637 | ||
| 2638 | return NULL; | |
| 2639 | } | |
| 2640 | ||
| 2641 | napi_value sn_crypto_stream_chacha20_ietf_xor_wrap_final (napi_env env, napi_callback_info info) { | |
| 2642 | SN_ARGV(1, crypto_stream_chacha20_ietf_xor_wrap_final) | |
| 2643 | ||
| 2644 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_chacha20_ietf_xor_state *, state, 0) | |
| 2645 | ||
| 2646 | SN_THROWS(state_size != sizeof(sn_crypto_stream_chacha20_ietf_xor_state), "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes") | |
| 2647 | ||
| 2648 | sodium_memzero(state->n, sizeof(state->n)); | |
| 2649 | sodium_memzero(state->k, sizeof(state->k)); | |
| 2650 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 2651 | state->remainder = 0; | |
| 2652 | ||
| 2653 | return NULL; | |
| 2654 | } | |
| 2655 | ||
| 2656 | typedef struct sn_crypto_stream_xchacha20_xor_state { | |
| 2657 | unsigned char n[crypto_stream_xchacha20_NONCEBYTES]; | |
| 2658 | unsigned char k[crypto_stream_xchacha20_KEYBYTES]; | |
| 2659 | unsigned char next_block[64]; | |
| 2660 | int remainder; | |
| 2661 | uint64_t block_counter; | |
| 2662 | } sn_crypto_stream_xchacha20_xor_state; | |
| 2663 | ||
| 2664 | napi_value sn_crypto_stream_xchacha20_xor_wrap_init (napi_env env, napi_callback_info info) { | |
| 2665 | SN_ARGV(3, crypto_stream_xchacha20_xor_wrap_init) | |
| 2666 | ||
| 2667 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_xchacha20_xor_state *, state, 0) | |
| 2668 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 2669 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 2670 | ||
| 2671 | SN_THROWS(state_size != sizeof(sn_crypto_stream_xchacha20_xor_state), "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes") | |
| 2672 | SN_ASSERT_LENGTH(n_size, crypto_stream_xchacha20_NONCEBYTES, "n") | |
| 2673 | SN_ASSERT_LENGTH(k_size, crypto_stream_xchacha20_KEYBYTES, "k") | |
| 2674 | ||
| 2675 | state->remainder = 0; | |
| 2676 | state->block_counter = 0; | |
| 2677 | memcpy(state->n, n_data, crypto_stream_xchacha20_NONCEBYTES); | |
| 2678 | memcpy(state->k, k_data, crypto_stream_xchacha20_KEYBYTES); | |
| 2679 | ||
| 2680 | return NULL; | |
| 2681 | } | |
| 2682 | ||
| 2683 | napi_value sn_crypto_stream_xchacha20_xor_wrap_update (napi_env env, napi_callback_info info) { | |
| 2684 | SN_ARGV(3, crypto_stream_xchacha20_xor_wrap_update) | |
| 2685 | ||
| 2686 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_xchacha20_xor_state *, state, 0) | |
| 2687 | SN_ARGV_BUFFER_CAST(unsigned char *, c, 1) | |
| 2688 | SN_ARGV_BUFFER_CAST(unsigned char *, m, 2) | |
| 2689 | ||
| 2690 | SN_THROWS(state_size != sizeof(sn_crypto_stream_xchacha20_xor_state), "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes") | |
| 2691 | SN_THROWS(c_size != m_size, "c must be 'm.byteLength' bytes") | |
| 2692 | ||
| 2693 | unsigned char *next_block = state->next_block; | |
| 2694 | ||
| 2695 | if (state->remainder) { | |
| 2696 | uint64_t offset = 0; | |
| 2697 | int rem = state->remainder; | |
| 2698 | ||
| 2699 | while (rem < 64 && offset < m_size) { | |
| 2700 | c[offset] = next_block[rem] ^ m[offset]; | |
| 2701 | offset++; | |
| 2702 | rem++; | |
| 2703 | } | |
| 2704 | ||
| 2705 | c += offset; | |
| 2706 | m += offset; | |
| 2707 | m_size -= offset; | |
| 2708 | state->remainder = rem == 64 ? 0 : rem; | |
| 2709 | ||
| 2710 | if (!m_size) return NULL; | |
| 2711 | } | |
| 2712 | ||
| 2713 | state->remainder = m_size & 63; | |
| 2714 | m_size -= state->remainder; | |
| 2715 | crypto_stream_xchacha20_xor_ic(c, m, m_size, state->n, state->block_counter, state->k); | |
| 2716 | state->block_counter += m_size / 64; | |
| 2717 | ||
| 2718 | if (state->remainder) { | |
| 2719 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 2720 | memcpy(next_block, m + m_size, state->remainder); | |
| 2721 | ||
| 2722 | crypto_stream_xchacha20_xor_ic(next_block, next_block, 64, state->n, state->block_counter, state->k); | |
| 2723 | memcpy(c + m_size, next_block, state->remainder); | |
| 2724 | ||
| 2725 | state->block_counter++; | |
| 2726 | } | |
| 2727 | ||
| 2728 | return NULL; | |
| 2729 | } | |
| 2730 | ||
| 2731 | napi_value sn_crypto_stream_xchacha20_xor_wrap_final (napi_env env, napi_callback_info info) { | |
| 2732 | SN_ARGV(1, crypto_stream_xchacha20_xor_wrap_final) | |
| 2733 | ||
| 2734 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_xchacha20_xor_state *, state, 0) | |
| 2735 | ||
| 2736 | SN_THROWS(state_size != sizeof(sn_crypto_stream_xchacha20_xor_state), "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes") | |
| 2737 | ||
| 2738 | sodium_memzero(state->n, sizeof(state->n)); | |
| 2739 | sodium_memzero(state->k, sizeof(state->k)); | |
| 2740 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 2741 | state->remainder = 0; | |
| 2742 | ||
| 2743 | return NULL; | |
| 2744 | } | |
| 2745 | ||
| 2746 | typedef struct sn_crypto_stream_salsa20_xor_state { | |
| 2747 | unsigned char n[crypto_stream_salsa20_NONCEBYTES]; | |
| 2748 | unsigned char k[crypto_stream_salsa20_KEYBYTES]; | |
| 2749 | unsigned char next_block[64]; | |
| 2750 | int remainder; | |
| 2751 | uint64_t block_counter; | |
| 2752 | } sn_crypto_stream_salsa20_xor_state; | |
| 2753 | ||
| 2754 | napi_value sn_crypto_stream_salsa20_xor_wrap_init (napi_env env, napi_callback_info info) { | |
| 2755 | SN_ARGV(3, crypto_stream_salsa20_xor_wrap_init) | |
| 2756 | ||
| 2757 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_salsa20_xor_state *, state, 0) | |
| 2758 | SN_ARGV_TYPEDARRAY(n, 1) | |
| 2759 | SN_ARGV_TYPEDARRAY(k, 2) | |
| 2760 | ||
| 2761 | SN_THROWS(state_size != sizeof(sn_crypto_stream_salsa20_xor_state), "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes") | |
| 2762 | SN_ASSERT_LENGTH(n_size, crypto_stream_salsa20_NONCEBYTES, "n") | |
| 2763 | SN_ASSERT_LENGTH(k_size, crypto_stream_salsa20_KEYBYTES, "k") | |
| 2764 | ||
| 2765 | state->remainder = 0; | |
| 2766 | state->block_counter = 0; | |
| 2767 | memcpy(state->n, n_data, crypto_stream_salsa20_NONCEBYTES); | |
| 2768 | memcpy(state->k, k_data, crypto_stream_salsa20_KEYBYTES); | |
| 2769 | ||
| 2770 | return NULL; | |
| 2771 | } | |
| 2772 | ||
| 2773 | napi_value sn_crypto_stream_salsa20_xor_wrap_update (napi_env env, napi_callback_info info) { | |
| 2774 | SN_ARGV(3, crypto_stream_salsa20_xor_wrap_update) | |
| 2775 | ||
| 2776 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_salsa20_xor_state *, state, 0) | |
| 2777 | SN_ARGV_BUFFER_CAST(unsigned char *, c, 1) | |
| 2778 | SN_ARGV_BUFFER_CAST(unsigned char *, m, 2) | |
| 2779 | ||
| 2780 | SN_THROWS(state_size != sizeof(sn_crypto_stream_salsa20_xor_state), "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes") | |
| 2781 | SN_THROWS(c_size != m_size, "c must be 'm.byteLength' bytes") | |
| 2782 | ||
| 2783 | unsigned char *next_block = state->next_block; | |
| 2784 | ||
| 2785 | if (state->remainder) { | |
| 2786 | uint64_t offset = 0; | |
| 2787 | int rem = state->remainder; | |
| 2788 | ||
| 2789 | while (rem < 64 && offset < m_size) { | |
| 2790 | c[offset] = next_block[rem] ^ m[offset]; | |
| 2791 | offset++; | |
| 2792 | rem++; | |
| 2793 | } | |
| 2794 | ||
| 2795 | c += offset; | |
| 2796 | m += offset; | |
| 2797 | m_size -= offset; | |
| 2798 | state->remainder = rem == 64 ? 0 : rem; | |
| 2799 | ||
| 2800 | if (!m_size) return NULL; | |
| 2801 | } | |
| 2802 | ||
| 2803 | state->remainder = m_size & 63; | |
| 2804 | m_size -= state->remainder; | |
| 2805 | crypto_stream_salsa20_xor_ic(c, m, m_size, state->n, state->block_counter, state->k); | |
| 2806 | state->block_counter += m_size / 64; | |
| 2807 | ||
| 2808 | if (state->remainder) { | |
| 2809 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 2810 | memcpy(next_block, m + m_size, state->remainder); | |
| 2811 | ||
| 2812 | crypto_stream_salsa20_xor_ic(next_block, next_block, 64, state->n, state->block_counter, state->k); | |
| 2813 | memcpy(c + m_size, next_block, state->remainder); | |
| 2814 | ||
| 2815 | state->block_counter++; | |
| 2816 | } | |
| 2817 | ||
| 2818 | return NULL; | |
| 2819 | } | |
| 2820 | ||
| 2821 | napi_value sn_crypto_stream_salsa20_xor_wrap_final (napi_env env, napi_callback_info info) { | |
| 2822 | SN_ARGV(1, crypto_stream_salsa20_xor_wrap_final) | |
| 2823 | ||
| 2824 | SN_ARGV_BUFFER_CAST(sn_crypto_stream_salsa20_xor_state *, state, 0) | |
| 2825 | ||
| 2826 | SN_THROWS(state_size != sizeof(sn_crypto_stream_salsa20_xor_state), "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes") | |
| 2827 | ||
| 2828 | sodium_memzero(state->n, sizeof(state->n)); | |
| 2829 | sodium_memzero(state->k, sizeof(state->k)); | |
| 2830 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 2831 | state->remainder = 0; | |
| 2832 | ||
| 2833 | return NULL; | |
| 2834 | } | |
| 2835 | ||
| 2836 | // Experimental API | |
| 2837 | ||
| 2838 | napi_value sn_extension_tweak_ed25519_base (napi_env env, napi_callback_info info) { | |
| 2839 | SN_ARGV(3, extension_tweak_ed25519_base) | |
| 2840 | ||
| 2841 | SN_ARGV_TYPEDARRAY(n, 0) | |
| 2842 | SN_ARGV_TYPEDARRAY(p, 1) | |
| 2843 | SN_ARGV_TYPEDARRAY(ns, 2) | |
| 2844 | ||
| 2845 | SN_ASSERT_LENGTH(n_size, sn__extension_tweak_ed25519_SCALARBYTES, "n") | |
| 2846 | SN_ASSERT_LENGTH(p_size, sn__extension_tweak_ed25519_BYTES, "p") | |
| 2847 | ||
| 2848 | sn__extension_tweak_ed25519_base(p_data, n_data, ns_data, ns_size); | |
| 2849 | ||
| 2850 | return NULL; | |
| 2851 | } | |
| 2852 | ||
| 2853 | napi_value sn_extension_tweak_ed25519_sign_detached (napi_env env, napi_callback_info info) { | |
| 2854 | SN_ARGV_OPTS(3, 4, extension_tweak_ed25519_sign_detached) | |
| 2855 | ||
| 2856 | SN_ARGV_TYPEDARRAY(sig, 0) | |
| 2857 | SN_ARGV_TYPEDARRAY(m, 1) | |
| 2858 | SN_ARGV_TYPEDARRAY(scalar, 2) | |
| 2859 | SN_ARGV_OPTS_TYPEDARRAY(pk, 3) | |
| 2860 | ||
| 2861 | SN_ASSERT_LENGTH(sig_size, crypto_sign_BYTES, "sig") | |
| 2862 | SN_ASSERT_LENGTH(scalar_size, sn__extension_tweak_ed25519_SCALARBYTES, "scalar") | |
| 2863 | ||
| 2864 | if (pk_data != NULL) { | |
| 2865 | SN_ASSERT_LENGTH(pk_size, crypto_sign_PUBLICKEYBYTES, "pk") | |
| 2866 | } | |
| 2867 | ||
| 2868 | SN_RETURN(sn__extension_tweak_ed25519_sign_detached(sig_data, NULL, m_data, m_size, scalar_data, pk_data), "failed to compute signature") | |
| 2869 | } | |
| 2870 | ||
| 2871 | napi_value sn_extension_tweak_ed25519_sk_to_scalar (napi_env env, napi_callback_info info) { | |
| 2872 | SN_ARGV(2, extension_tweak_ed25519_sk_to_scalar) | |
| 2873 | ||
| 2874 | SN_ARGV_TYPEDARRAY(n, 0) | |
| 2875 | SN_ARGV_TYPEDARRAY(sk, 1) | |
| 2876 | ||
| 2877 | SN_ASSERT_LENGTH(n_size, sn__extension_tweak_ed25519_SCALARBYTES, "n") | |
| 2878 | SN_ASSERT_LENGTH(sk_size, crypto_sign_SECRETKEYBYTES, "sk") | |
| 2879 | ||
| 2880 | sn__extension_tweak_ed25519_sk_to_scalar(n_data, sk_data); | |
| 2881 | ||
| 2882 | return NULL; | |
| 2883 | } | |
| 2884 | ||
| 2885 | napi_value sn_extension_tweak_ed25519_scalar (napi_env env, napi_callback_info info) { | |
| 2886 | SN_ARGV(3, extension_tweak_ed25519_scalar) | |
| 2887 | ||
| 2888 | SN_ARGV_TYPEDARRAY(scalar_out, 0) | |
| 2889 | SN_ARGV_TYPEDARRAY(scalar, 1) | |
| 2890 | SN_ARGV_TYPEDARRAY(ns, 2) | |
| 2891 | ||
| 2892 | SN_ASSERT_LENGTH(scalar_out_size, sn__extension_tweak_ed25519_SCALARBYTES, "scalar_out") | |
| 2893 | SN_ASSERT_LENGTH(scalar_size, sn__extension_tweak_ed25519_SCALARBYTES, "scalar") | |
| 2894 | ||
| 2895 | sn__extension_tweak_ed25519_scalar(scalar_out_data, scalar_data, ns_data, ns_size); | |
| 2896 | ||
| 2897 | return NULL; | |
| 2898 | } | |
| 2899 | ||
| 2900 | napi_value sn_extension_tweak_ed25519_pk (napi_env env, napi_callback_info info) { | |
| 2901 | SN_ARGV(3, extension_tweak_ed25519_pk) | |
| 2902 | ||
| 2903 | SN_ARGV_TYPEDARRAY(tpk, 0) | |
| 2904 | SN_ARGV_TYPEDARRAY(pk, 1) | |
| 2905 | SN_ARGV_TYPEDARRAY(ns, 2) | |
| 2906 | ||
| 2907 | SN_ASSERT_LENGTH(tpk_size, crypto_sign_PUBLICKEYBYTES, "tpk") | |
| 2908 | SN_ASSERT_LENGTH(pk_size, crypto_sign_PUBLICKEYBYTES, "pk") | |
| 2909 | ||
| 2910 | SN_RETURN(sn__extension_tweak_ed25519_pk(tpk_data, pk_data, ns_data, ns_size), "failed to tweak public key") | |
| 2911 | } | |
| 2912 | ||
| 2913 | napi_value sn_extension_tweak_ed25519_keypair (napi_env env, napi_callback_info info) { | |
| 2914 | SN_ARGV(4, extension_tweak_ed25519_keypair) | |
| 2915 | ||
| 2916 | SN_ARGV_TYPEDARRAY(pk, 0) | |
| 2917 | SN_ARGV_TYPEDARRAY(scalar_out, 1) | |
| 2918 | SN_ARGV_TYPEDARRAY(scalar_in, 2) | |
| 2919 | SN_ARGV_TYPEDARRAY(ns, 3) | |
| 2920 | ||
| 2921 | SN_ASSERT_LENGTH(pk_size, sn__extension_tweak_ed25519_BYTES, "pk") | |
| 2922 | SN_ASSERT_LENGTH(scalar_out_size, sn__extension_tweak_ed25519_SCALARBYTES, "scalar_out") | |
| 2923 | SN_ASSERT_LENGTH(scalar_in_size, sn__extension_tweak_ed25519_SCALARBYTES, "scalar_in") | |
| 2924 | ||
| 2925 | sn__extension_tweak_ed25519_keypair(pk_data, scalar_out_data, scalar_in_data, ns_data, ns_size); | |
| 2926 | ||
| 2927 | return NULL; | |
| 2928 | } | |
| 2929 | ||
| 2930 | napi_value sn_extension_tweak_ed25519_scalar_add (napi_env env, napi_callback_info info) { | |
| 2931 | SN_ARGV(3, extension_tweak_ed25519_scalar_add) | |
| 2932 | ||
| 2933 | SN_ARGV_TYPEDARRAY(scalar_out, 0) | |
| 2934 | SN_ARGV_TYPEDARRAY(scalar, 1) | |
| 2935 | SN_ARGV_TYPEDARRAY(n, 2) | |
| 2936 | ||
| 2937 | SN_ASSERT_LENGTH(scalar_out_size, sn__extension_tweak_ed25519_SCALARBYTES, "scalar_out") | |
| 2938 | SN_ASSERT_LENGTH(scalar_size, sn__extension_tweak_ed25519_SCALARBYTES, "scalar") | |
| 2939 | SN_ASSERT_LENGTH(n_size, sn__extension_tweak_ed25519_SCALARBYTES, "n") | |
| 2940 | ||
| 2941 | sn__extension_tweak_ed25519_scalar_add(scalar_out_data, scalar_data, n_data); | |
| 2942 | ||
| 2943 | return NULL; | |
| 2944 | } | |
| 2945 | ||
| 2946 | napi_value sn_extension_tweak_ed25519_pk_add (napi_env env, napi_callback_info info) { | |
| 2947 | SN_ARGV(3, extension_tweak_ed25519_pk) | |
| 2948 | ||
| 2949 | SN_ARGV_TYPEDARRAY(tpk, 0) | |
| 2950 | SN_ARGV_TYPEDARRAY(pk, 1) | |
| 2951 | SN_ARGV_TYPEDARRAY(p, 2) | |
| 2952 | ||
| 2953 | SN_ASSERT_LENGTH(tpk_size, crypto_sign_PUBLICKEYBYTES, "tpk") | |
| 2954 | SN_ASSERT_LENGTH(pk_size, crypto_sign_PUBLICKEYBYTES, "pk") | |
| 2955 | SN_ASSERT_LENGTH(p_size, crypto_sign_PUBLICKEYBYTES, "p") | |
| 2956 | ||
| 2957 | SN_RETURN(sn__extension_tweak_ed25519_pk_add(tpk_data, pk_data, p_data), "failed to add tweak to public key") | |
| 2958 | } | |
| 2959 | ||
| 2960 | napi_value sn_extension_tweak_ed25519_keypair_add (napi_env env, napi_callback_info info) { | |
| 2961 | SN_ARGV(4, extension_tweak_ed25519_keypair_add) | |
| 2962 | ||
| 2963 | SN_ARGV_TYPEDARRAY(pk, 0) | |
| 2964 | SN_ARGV_TYPEDARRAY(scalar_out, 1) | |
| 2965 | SN_ARGV_TYPEDARRAY(scalar_in, 2) | |
| 2966 | SN_ARGV_TYPEDARRAY(tweak, 3) | |
| 2967 | ||
| 2968 | SN_ASSERT_LENGTH(pk_size, sn__extension_tweak_ed25519_BYTES, "pk") | |
| 2969 | SN_ASSERT_LENGTH(scalar_out_size, sn__extension_tweak_ed25519_SCALARBYTES, "scalar_out") | |
| 2970 | SN_ASSERT_LENGTH(scalar_in_size, sn__extension_tweak_ed25519_SCALARBYTES, "scalar_in") | |
| 2971 | SN_ASSERT_LENGTH(tweak_size, sn__extension_tweak_ed25519_SCALARBYTES, "tweak") | |
| 2972 | ||
| 2973 | SN_RETURN(sn__extension_tweak_ed25519_keypair_add(pk_data, scalar_out_data, scalar_in_data, tweak_data), "failed to add tweak to keypair") | |
| 2974 | } | |
| 2975 | ||
| 2976 | napi_value sn_extension_pbkdf2_sha512 (napi_env env, napi_callback_info info) { | |
| 2977 | SN_ARGV(5, extension_pbkdf2_sha512) | |
| 2978 | ||
| 2979 | SN_ARGV_BUFFER_CAST(unsigned char *, out, 0) | |
| 2980 | SN_ARGV_BUFFER_CAST(unsigned char *, passwd, 1) | |
| 2981 | SN_ARGV_BUFFER_CAST(unsigned char *, salt, 2) | |
| 2982 | SN_ARGV_UINT64(iter, 3) | |
| 2983 | SN_ARGV_UINT64(outlen, 4) | |
| 2984 | ||
| 2985 | SN_ASSERT_MIN_LENGTH(iter, sn__extension_pbkdf2_sha512_ITERATIONS_MIN, "iterations") | |
| 2986 | SN_ASSERT_MAX_LENGTH(outlen, sn__extension_pbkdf2_sha512_BYTES_MAX, "outlen") | |
| 2987 | ||
| 2988 | SN_ASSERT_MIN_LENGTH(out_size, outlen, "out") | |
| 2989 | ||
| 2990 | SN_RETURN(sn__extension_pbkdf2_sha512(passwd, passwd_size, salt, salt_size, iter, out, outlen), "failed to add tweak to public key") | |
| 2991 | } | |
| 2992 | ||
| 2993 | typedef struct sn_async_pbkdf2_sha512_request { | |
| 2994 | napi_env env; | |
| 2995 | unsigned char *out_data; | |
| 2996 | size_t out_size; | |
| 2997 | napi_ref out_ref; | |
| 2998 | size_t outlen; | |
| 2999 | napi_ref pwd_ref; | |
| 3000 | const unsigned char *pwd_data; | |
| 3001 | size_t pwd_size; | |
| 3002 | napi_ref salt_ref; | |
| 3003 | unsigned char *salt_data; | |
| 3004 | size_t salt_size; | |
| 3005 | uint64_t iter; | |
| 3006 | } sn_async_pbkdf2_sha512_request; | |
| 3007 | ||
| 3008 | static void async_pbkdf2_sha512_execute (uv_work_t *uv_req) { | |
| 3009 | sn_async_task_t *task = (sn_async_task_t *) uv_req; | |
| 3010 | sn_async_pbkdf2_sha512_request *req = (sn_async_pbkdf2_sha512_request *) task->req; | |
| 3011 | task->code = sn__extension_pbkdf2_sha512(req->pwd_data, | |
| 3012 | req->pwd_size, | |
| 3013 | req->salt_data, | |
| 3014 | req->salt_size, | |
| 3015 | req->iter, | |
| 3016 | req->out_data, | |
| 3017 | req->outlen); | |
| 3018 | } | |
| 3019 | ||
| 3020 | static void async_pbkdf2_sha512_complete (uv_work_t *uv_req, int status) { | |
| 3021 | sn_async_task_t *task = (sn_async_task_t *) uv_req; | |
| 3022 | sn_async_pbkdf2_sha512_request *req = (sn_async_pbkdf2_sha512_request *) task->req; | |
| 3023 | ||
| 3024 | napi_handle_scope scope; | |
| 3025 | napi_open_handle_scope(req->env, &scope); | |
| 3026 | ||
| 3027 | napi_value global; | |
| 3028 | napi_get_global(req->env, &global); | |
| 3029 | ||
| 3030 | SN_ASYNC_COMPLETE("failed to compute kdf") | |
| 3031 | ||
| 3032 | napi_close_handle_scope(req->env, scope); | |
| 3033 | ||
| 3034 | napi_delete_reference(req->env, req->out_ref); | |
| 3035 | napi_delete_reference(req->env, req->pwd_ref); | |
| 3036 | napi_delete_reference(req->env, req->salt_ref); | |
| 3037 | ||
| 3038 | free(req); | |
| 3039 | free(task); | |
| 3040 | } | |
| 3041 | ||
| 3042 | napi_value sn_extension_pbkdf2_sha512_async (napi_env env, napi_callback_info info) { | |
| 3043 | SN_ARGV_OPTS(5, 6, extension_pbkdf2_sha512_async) | |
| 3044 | ||
| 3045 | SN_ARGV_BUFFER_CAST(unsigned char *, out, 0) | |
| 3046 | SN_ARGV_BUFFER_CAST(unsigned char *, pwd, 1) | |
| 3047 | SN_ARGV_BUFFER_CAST(unsigned char *, salt, 2) | |
| 3048 | SN_ARGV_UINT64(iter, 3) | |
| 3049 | SN_ARGV_UINT64(outlen, 4) | |
| 3050 | ||
| 3051 | SN_ASSERT_MIN_LENGTH(iter, sn__extension_pbkdf2_sha512_ITERATIONS_MIN, "iterations") | |
| 3052 | SN_ASSERT_MAX_LENGTH(outlen, sn__extension_pbkdf2_sha512_BYTES_MAX, "outlen") | |
| 3053 | SN_ASSERT_MIN_LENGTH(out_size, outlen, "output") | |
| 3054 | ||
| 3055 | sn_async_pbkdf2_sha512_request *req = (sn_async_pbkdf2_sha512_request *) malloc(sizeof(sn_async_pbkdf2_sha512_request)); | |
| 3056 | ||
| 3057 | req->env = env; | |
| 3058 | req->out_data = out; | |
| 3059 | req->out_size = out_size; | |
| 3060 | req->pwd_data = pwd; | |
| 3061 | req->pwd_size = pwd_size; | |
| 3062 | req->salt_data = salt; | |
| 3063 | req->salt_size = salt_size; | |
| 3064 | req->iter = iter; | |
| 3065 | req->outlen = outlen; | |
| 3066 | ||
| 3067 | sn_async_task_t *task = (sn_async_task_t *) malloc(sizeof(sn_async_task_t)); | |
| 3068 | SN_ASYNC_TASK(5); | |
| 3069 | ||
| 3070 | SN_STATUS_THROWS(napi_create_reference(env, out_argv, 1, &req->out_ref), "") | |
| 3071 | SN_STATUS_THROWS(napi_create_reference(env, pwd_argv, 1, &req->pwd_ref), "") | |
| 3072 | SN_STATUS_THROWS(napi_create_reference(env, salt_argv, 1, &req->salt_ref), "") | |
| 3073 | ||
| 3074 | SN_QUEUE_TASK(task, async_pbkdf2_sha512_execute, async_pbkdf2_sha512_complete) | |
| 3075 | ||
| 3076 | return promise; | |
| 3077 | } | |
| 3078 | ||
| 3079 | static napi_value create_sodium_native (napi_env env) { | |
| 3080 | SN_THROWS(sodium_init() == -1, "sodium_init() failed") | |
| 3081 | ||
| 3082 | napi_value exports; | |
| 3083 | napi_create_object(env, &exports); | |
| 3084 | ||
| 3085 | // memory | |
| 3086 | ||
| 3087 | SN_EXPORT_FUNCTION(sodium_memzero, sn_sodium_memzero) | |
| 3088 | SN_EXPORT_FUNCTION(sodium_mlock, sn_sodium_mlock) | |
| 3089 | SN_EXPORT_FUNCTION(sodium_munlock, sn_sodium_munlock) | |
| 3090 | SN_EXPORT_FUNCTION(sodium_malloc, sn_sodium_malloc) | |
| 3091 | SN_EXPORT_FUNCTION(sodium_free, sn_sodium_free) | |
| 3092 | SN_EXPORT_FUNCTION(sodium_mprotect_noaccess, sn_sodium_mprotect_noaccess) | |
| 3093 | SN_EXPORT_FUNCTION(sodium_mprotect_readonly, sn_sodium_mprotect_readonly) | |
| 3094 | SN_EXPORT_FUNCTION(sodium_mprotect_readwrite, sn_sodium_mprotect_readwrite) | |
| 3095 | ||
| 3096 | // randombytes | |
| 3097 | ||
| 3098 | SN_EXPORT_FUNCTION(randombytes_buf, sn_randombytes_buf) | |
| 3099 | SN_EXPORT_FUNCTION(randombytes_buf_deterministic, sn_randombytes_buf_deterministic) | |
| 3100 | SN_EXPORT_FUNCTION(randombytes_uniform, sn_randombytes_uniform) | |
| 3101 | SN_EXPORT_FUNCTION(randombytes_random, sn_randombytes_random) | |
| 3102 | SN_EXPORT_UINT32(randombytes_SEEDBYTES, randombytes_SEEDBYTES) | |
| 3103 | ||
| 3104 | // sodium helpers | |
| 3105 | ||
| 3106 | SN_EXPORT_FUNCTION(sodium_memcmp, sn_sodium_memcmp) | |
| 3107 | SN_EXPORT_FUNCTION(sodium_increment, sn_sodium_increment) | |
| 3108 | SN_EXPORT_FUNCTION(sodium_add, sn_sodium_add) | |
| 3109 | SN_EXPORT_FUNCTION(sodium_sub, sn_sodium_sub) | |
| 3110 | SN_EXPORT_FUNCTION(sodium_compare, sn_sodium_compare) | |
| 3111 | SN_EXPORT_FUNCTION(sodium_is_zero, sn_sodium_is_zero) | |
| 3112 | SN_EXPORT_FUNCTION(sodium_pad, sn_sodium_pad) | |
| 3113 | SN_EXPORT_FUNCTION(sodium_unpad, sn_sodium_unpad) | |
| 3114 | ||
| 3115 | // crypto_aead | |
| 3116 | ||
| 3117 | SN_EXPORT_FUNCTION(crypto_aead_xchacha20poly1305_ietf_keygen, sn_crypto_aead_xchacha20poly1305_ietf_keygen) | |
| 3118 | SN_EXPORT_FUNCTION(crypto_aead_xchacha20poly1305_ietf_encrypt, sn_crypto_aead_xchacha20poly1305_ietf_encrypt) | |
| 3119 | SN_EXPORT_FUNCTION(crypto_aead_xchacha20poly1305_ietf_decrypt, sn_crypto_aead_xchacha20poly1305_ietf_decrypt) | |
| 3120 | SN_EXPORT_FUNCTION(crypto_aead_xchacha20poly1305_ietf_encrypt_detached, sn_crypto_aead_xchacha20poly1305_ietf_encrypt_detached) | |
| 3121 | SN_EXPORT_FUNCTION(crypto_aead_xchacha20poly1305_ietf_decrypt_detached, sn_crypto_aead_xchacha20poly1305_ietf_decrypt_detached) | |
| 3122 | SN_EXPORT_UINT32(crypto_aead_xchacha20poly1305_ietf_ABYTES, crypto_aead_xchacha20poly1305_ietf_ABYTES) | |
| 3123 | SN_EXPORT_UINT32(crypto_aead_xchacha20poly1305_ietf_KEYBYTES, crypto_aead_xchacha20poly1305_ietf_KEYBYTES) | |
| 3124 | SN_EXPORT_UINT32(crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, crypto_aead_xchacha20poly1305_ietf_NPUBBYTES) | |
| 3125 | SN_EXPORT_UINT32(crypto_aead_xchacha20poly1305_ietf_NSECBYTES, crypto_aead_xchacha20poly1305_ietf_NSECBYTES) | |
| 3126 | SN_EXPORT_UINT64(crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX, crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX) | |
| 3127 | ||
| 3128 | SN_EXPORT_FUNCTION(crypto_aead_chacha20poly1305_ietf_keygen, sn_crypto_aead_chacha20poly1305_ietf_keygen) | |
| 3129 | SN_EXPORT_FUNCTION(crypto_aead_chacha20poly1305_ietf_encrypt, sn_crypto_aead_chacha20poly1305_ietf_encrypt) | |
| 3130 | SN_EXPORT_FUNCTION(crypto_aead_chacha20poly1305_ietf_decrypt, sn_crypto_aead_chacha20poly1305_ietf_decrypt) | |
| 3131 | SN_EXPORT_FUNCTION(crypto_aead_chacha20poly1305_ietf_encrypt_detached, sn_crypto_aead_chacha20poly1305_ietf_encrypt_detached) | |
| 3132 | SN_EXPORT_FUNCTION(crypto_aead_chacha20poly1305_ietf_decrypt_detached, sn_crypto_aead_chacha20poly1305_ietf_decrypt_detached) | |
| 3133 | SN_EXPORT_UINT32(crypto_aead_chacha20poly1305_ietf_ABYTES, crypto_aead_chacha20poly1305_ietf_ABYTES) | |
| 3134 | SN_EXPORT_UINT32(crypto_aead_chacha20poly1305_ietf_KEYBYTES, crypto_aead_chacha20poly1305_ietf_KEYBYTES) | |
| 3135 | SN_EXPORT_UINT32(crypto_aead_chacha20poly1305_ietf_NPUBBYTES, crypto_aead_chacha20poly1305_ietf_NPUBBYTES) | |
| 3136 | SN_EXPORT_UINT32(crypto_aead_chacha20poly1305_ietf_NSECBYTES, crypto_aead_chacha20poly1305_ietf_NSECBYTES) | |
| 3137 | SN_EXPORT_UINT64(crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX, crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX) | |
| 3138 | ||
| 3139 | // crypto_auth | |
| 3140 | ||
| 3141 | SN_EXPORT_FUNCTION(crypto_auth, sn_crypto_auth) | |
| 3142 | SN_EXPORT_FUNCTION(crypto_auth_verify, sn_crypto_auth_verify) | |
| 3143 | SN_EXPORT_UINT32(crypto_auth_BYTES, crypto_auth_BYTES) | |
| 3144 | SN_EXPORT_UINT32(crypto_auth_KEYBYTES, crypto_auth_KEYBYTES) | |
| 3145 | SN_EXPORT_STRING(crypto_auth_PRIMITIVE, crypto_auth_PRIMITIVE) | |
| 3146 | ||
| 3147 | // crypto_box | |
| 3148 | ||
| 3149 | SN_EXPORT_FUNCTION(crypto_box_keypair, sn_crypto_box_keypair) | |
| 3150 | SN_EXPORT_FUNCTION(crypto_box_seed_keypair, sn_crypto_box_seed_keypair) | |
| 3151 | SN_EXPORT_FUNCTION(crypto_box_easy, sn_crypto_box_easy) | |
| 3152 | SN_EXPORT_FUNCTION(crypto_box_open_easy, sn_crypto_box_open_easy) | |
| 3153 | SN_EXPORT_FUNCTION(crypto_box_detached, sn_crypto_box_detached) | |
| 3154 | SN_EXPORT_FUNCTION(crypto_box_open_detached, sn_crypto_box_open_detached) | |
| 3155 | SN_EXPORT_FUNCTION(crypto_box_seal, sn_crypto_box_seal) | |
| 3156 | SN_EXPORT_FUNCTION(crypto_box_seal_open, sn_crypto_box_seal_open) | |
| 3157 | SN_EXPORT_UINT32(crypto_box_SEEDBYTES, crypto_box_SEEDBYTES) | |
| 3158 | SN_EXPORT_UINT32(crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES) | |
| 3159 | SN_EXPORT_UINT32(crypto_box_SECRETKEYBYTES, crypto_box_SECRETKEYBYTES) | |
| 3160 | SN_EXPORT_UINT32(crypto_box_NONCEBYTES, crypto_box_NONCEBYTES) | |
| 3161 | SN_EXPORT_UINT32(crypto_box_MACBYTES, crypto_box_MACBYTES) | |
| 3162 | SN_EXPORT_UINT32(crypto_box_SEALBYTES, crypto_box_SEALBYTES) | |
| 3163 | SN_EXPORT_STRING(crypto_box_PRIMITIVE, crypto_box_PRIMITIVE) | |
| 3164 | ||
| 3165 | // crypto_core | |
| 3166 | ||
| 3167 | SN_EXPORT_FUNCTION(crypto_core_ed25519_is_valid_point, sn_crypto_core_ed25519_is_valid_point) | |
| 3168 | SN_EXPORT_FUNCTION(crypto_core_ed25519_from_uniform, sn_crypto_core_ed25519_from_uniform) | |
| 3169 | SN_EXPORT_FUNCTION(crypto_core_ed25519_add, sn_crypto_core_ed25519_add) | |
| 3170 | SN_EXPORT_FUNCTION(crypto_core_ed25519_sub, sn_crypto_core_ed25519_sub) | |
| 3171 | SN_EXPORT_FUNCTION(crypto_core_ed25519_scalar_random, sn_crypto_core_ed25519_scalar_random) | |
| 3172 | SN_EXPORT_FUNCTION(crypto_core_ed25519_scalar_reduce, sn_crypto_core_ed25519_scalar_reduce) | |
| 3173 | SN_EXPORT_FUNCTION(crypto_core_ed25519_scalar_invert, sn_crypto_core_ed25519_scalar_invert) | |
| 3174 | SN_EXPORT_FUNCTION(crypto_core_ed25519_scalar_negate, sn_crypto_core_ed25519_scalar_negate) | |
| 3175 | SN_EXPORT_FUNCTION(crypto_core_ed25519_scalar_complement, sn_crypto_core_ed25519_scalar_complement) | |
| 3176 | SN_EXPORT_FUNCTION(crypto_core_ed25519_scalar_add, sn_crypto_core_ed25519_scalar_add) | |
| 3177 | SN_EXPORT_FUNCTION(crypto_core_ed25519_scalar_sub, sn_crypto_core_ed25519_scalar_sub) | |
| 3178 | SN_EXPORT_UINT32(crypto_core_ed25519_BYTES, crypto_core_ed25519_BYTES) | |
| 3179 | SN_EXPORT_UINT32(crypto_core_ed25519_UNIFORMBYTES, crypto_core_ed25519_UNIFORMBYTES) | |
| 3180 | SN_EXPORT_UINT32(crypto_core_ed25519_SCALARBYTES, crypto_core_ed25519_SCALARBYTES) | |
| 3181 | SN_EXPORT_UINT32(crypto_core_ed25519_NONREDUCEDSCALARBYTES, crypto_core_ed25519_NONREDUCEDSCALARBYTES) | |
| 3182 | ||
| 3183 | // crypto_kdf | |
| 3184 | ||
| 3185 | SN_EXPORT_FUNCTION(crypto_kdf_keygen, sn_crypto_kdf_keygen) | |
| 3186 | SN_EXPORT_FUNCTION(crypto_kdf_derive_from_key, sn_crypto_kdf_derive_from_key) | |
| 3187 | SN_EXPORT_UINT32(crypto_kdf_BYTES_MIN, crypto_kdf_BYTES_MIN) | |
| 3188 | SN_EXPORT_UINT32(crypto_kdf_BYTES_MAX, crypto_kdf_BYTES_MAX) | |
| 3189 | SN_EXPORT_UINT32(crypto_kdf_CONTEXTBYTES, crypto_kdf_CONTEXTBYTES) | |
| 3190 | SN_EXPORT_UINT32(crypto_kdf_KEYBYTES, crypto_kdf_KEYBYTES) | |
| 3191 | SN_EXPORT_STRING(crypto_kdf_PRIMITIVE, crypto_kdf_PRIMITIVE) | |
| 3192 | ||
| 3193 | // crypto_kx | |
| 3194 | ||
| 3195 | SN_EXPORT_FUNCTION(crypto_kx_keypair, sn_crypto_kx_keypair) | |
| 3196 | SN_EXPORT_FUNCTION(crypto_kx_seed_keypair, sn_crypto_kx_seed_keypair) | |
| 3197 | SN_EXPORT_FUNCTION(crypto_kx_client_session_keys, sn_crypto_kx_client_session_keys) | |
| 3198 | SN_EXPORT_FUNCTION(crypto_kx_server_session_keys, sn_crypto_kx_server_session_keys) | |
| 3199 | SN_EXPORT_UINT32(crypto_kx_PUBLICKEYBYTES, crypto_kx_PUBLICKEYBYTES) | |
| 3200 | SN_EXPORT_UINT32(crypto_kx_SECRETKEYBYTES, crypto_kx_SECRETKEYBYTES) | |
| 3201 | SN_EXPORT_UINT32(crypto_kx_SEEDBYTES, crypto_kx_SEEDBYTES) | |
| 3202 | SN_EXPORT_UINT32(crypto_kx_SESSIONKEYBYTES, crypto_kx_SESSIONKEYBYTES) | |
| 3203 | SN_EXPORT_STRING(crypto_kx_PRIMITIVE, crypto_kx_PRIMITIVE) | |
| 3204 | ||
| 3205 | // crypto_generichash | |
| 3206 | ||
| 3207 | SN_EXPORT_FUNCTION(crypto_generichash, sn_crypto_generichash) | |
| 3208 | SN_EXPORT_FUNCTION(crypto_generichash_batch, sn_crypto_generichash_batch) | |
| 3209 | SN_EXPORT_FUNCTION(crypto_generichash_keygen, sn_crypto_generichash_keygen) | |
| 3210 | SN_EXPORT_FUNCTION(crypto_generichash_init, sn_crypto_generichash_init) | |
| 3211 | SN_EXPORT_FUNCTION(crypto_generichash_update, sn_crypto_generichash_update) | |
| 3212 | SN_EXPORT_FUNCTION(crypto_generichash_final, sn_crypto_generichash_final) | |
| 3213 | SN_EXPORT_UINT32(crypto_generichash_STATEBYTES, sizeof(crypto_generichash_state)) | |
| 3214 | SN_EXPORT_STRING(crypto_generichash_PRIMITIVE, crypto_generichash_PRIMITIVE) | |
| 3215 | SN_EXPORT_UINT32(crypto_generichash_BYTES_MIN, crypto_generichash_BYTES_MIN) | |
| 3216 | SN_EXPORT_UINT32(crypto_generichash_BYTES_MAX, crypto_generichash_BYTES_MAX) | |
| 3217 | SN_EXPORT_UINT32(crypto_generichash_BYTES, crypto_generichash_BYTES) | |
| 3218 | SN_EXPORT_UINT32(crypto_generichash_KEYBYTES_MIN, crypto_generichash_KEYBYTES_MIN) | |
| 3219 | SN_EXPORT_UINT32(crypto_generichash_KEYBYTES_MAX, crypto_generichash_KEYBYTES_MAX) | |
| 3220 | SN_EXPORT_UINT32(crypto_generichash_KEYBYTES, crypto_generichash_KEYBYTES) | |
| 3221 | ||
| 3222 | // crypto_hash | |
| 3223 | ||
| 3224 | SN_EXPORT_FUNCTION(crypto_hash, sn_crypto_hash) | |
| 3225 | SN_EXPORT_UINT32(crypto_hash_BYTES, crypto_hash_BYTES) | |
| 3226 | SN_EXPORT_STRING(crypto_hash_PRIMITIVE, crypto_hash_PRIMITIVE) | |
| 3227 | ||
| 3228 | SN_EXPORT_FUNCTION(crypto_hash_sha256, sn_crypto_hash_sha256) | |
| 3229 | SN_EXPORT_FUNCTION(crypto_hash_sha256_init, sn_crypto_hash_sha256_init) | |
| 3230 | SN_EXPORT_FUNCTION(crypto_hash_sha256_update, sn_crypto_hash_sha256_update) | |
| 3231 | SN_EXPORT_FUNCTION(crypto_hash_sha256_final, sn_crypto_hash_sha256_final) | |
| 3232 | SN_EXPORT_UINT32(crypto_hash_sha256_STATEBYTES, sizeof(crypto_hash_sha256_state)) | |
| 3233 | SN_EXPORT_UINT32(crypto_hash_sha256_BYTES, crypto_hash_sha256_BYTES) | |
| 3234 | ||
| 3235 | SN_EXPORT_FUNCTION(crypto_hash_sha512, sn_crypto_hash_sha512) | |
| 3236 | SN_EXPORT_FUNCTION(crypto_hash_sha512_init, sn_crypto_hash_sha512_init) | |
| 3237 | SN_EXPORT_FUNCTION(crypto_hash_sha512_update, sn_crypto_hash_sha512_update) | |
| 3238 | SN_EXPORT_FUNCTION(crypto_hash_sha512_final, sn_crypto_hash_sha512_final) | |
| 3239 | SN_EXPORT_UINT32(crypto_hash_sha512_STATEBYTES, sizeof(crypto_hash_sha512_state)) | |
| 3240 | SN_EXPORT_UINT32(crypto_hash_sha512_BYTES, crypto_hash_sha512_BYTES) | |
| 3241 | ||
| 3242 | // crypto_onetimeauth | |
| 3243 | ||
| 3244 | SN_EXPORT_FUNCTION(crypto_onetimeauth, sn_crypto_onetimeauth) | |
| 3245 | SN_EXPORT_FUNCTION(crypto_onetimeauth_verify, sn_crypto_onetimeauth_verify) | |
| 3246 | SN_EXPORT_FUNCTION(crypto_onetimeauth_init, sn_crypto_onetimeauth_init) | |
| 3247 | SN_EXPORT_FUNCTION(crypto_onetimeauth_update, sn_crypto_onetimeauth_update) | |
| 3248 | SN_EXPORT_FUNCTION(crypto_onetimeauth_final, sn_crypto_onetimeauth_final) | |
| 3249 | SN_EXPORT_UINT32(crypto_onetimeauth_STATEBYTES, sizeof(crypto_onetimeauth_state)) | |
| 3250 | SN_EXPORT_UINT32(crypto_onetimeauth_BYTES, crypto_onetimeauth_BYTES) | |
| 3251 | SN_EXPORT_UINT32(crypto_onetimeauth_KEYBYTES, crypto_onetimeauth_KEYBYTES) | |
| 3252 | SN_EXPORT_STRING(crypto_onetimeauth_PRIMITIVE, crypto_onetimeauth_PRIMITIVE) | |
| 3253 | ||
| 3254 | // crypto_pwhash | |
| 3255 | ||
| 3256 | SN_EXPORT_FUNCTION(crypto_pwhash, sn_crypto_pwhash) | |
| 3257 | SN_EXPORT_FUNCTION(crypto_pwhash_str, sn_crypto_pwhash_str) | |
| 3258 | SN_EXPORT_FUNCTION(crypto_pwhash_str_verify, sn_crypto_pwhash_str_verify) | |
| 3259 | SN_EXPORT_FUNCTION(crypto_pwhash_str_needs_rehash, sn_crypto_pwhash_str_needs_rehash) | |
| 3260 | SN_EXPORT_FUNCTION(crypto_pwhash_async, sn_crypto_pwhash_async) | |
| 3261 | SN_EXPORT_FUNCTION(crypto_pwhash_str_async, sn_crypto_pwhash_str_async) | |
| 3262 | SN_EXPORT_FUNCTION(crypto_pwhash_str_verify_async, sn_crypto_pwhash_str_verify_async) | |
| 3263 | SN_EXPORT_UINT32(crypto_pwhash_ALG_ARGON2I13, crypto_pwhash_ALG_ARGON2I13) | |
| 3264 | SN_EXPORT_UINT32(crypto_pwhash_ALG_ARGON2ID13, crypto_pwhash_ALG_ARGON2ID13) | |
| 3265 | SN_EXPORT_UINT32(crypto_pwhash_ALG_DEFAULT, crypto_pwhash_ALG_DEFAULT) | |
| 3266 | SN_EXPORT_UINT32(crypto_pwhash_BYTES_MIN, crypto_pwhash_BYTES_MIN) | |
| 3267 | SN_EXPORT_UINT32(crypto_pwhash_BYTES_MAX, crypto_pwhash_BYTES_MAX) | |
| 3268 | SN_EXPORT_UINT32(crypto_pwhash_PASSWD_MIN, crypto_pwhash_PASSWD_MIN) | |
| 3269 | SN_EXPORT_UINT32(crypto_pwhash_PASSWD_MAX, crypto_pwhash_PASSWD_MAX) | |
| 3270 | SN_EXPORT_UINT32(crypto_pwhash_SALTBYTES, crypto_pwhash_SALTBYTES) | |
| 3271 | SN_EXPORT_UINT32(crypto_pwhash_STRBYTES, crypto_pwhash_STRBYTES) | |
| 3272 | SN_EXPORT_STRING(crypto_pwhash_STRPREFIX, crypto_pwhash_STRPREFIX) | |
| 3273 | SN_EXPORT_UINT32(crypto_pwhash_OPSLIMIT_MIN, crypto_pwhash_OPSLIMIT_MIN) | |
| 3274 | SN_EXPORT_UINT32(crypto_pwhash_OPSLIMIT_MAX, crypto_pwhash_OPSLIMIT_MAX) | |
| 3275 | SN_EXPORT_UINT32(crypto_pwhash_MEMLIMIT_MIN, crypto_pwhash_MEMLIMIT_MIN) | |
| 3276 | SN_EXPORT_UINT32(crypto_pwhash_MEMLIMIT_MAX, crypto_pwhash_MEMLIMIT_MAX) | |
| 3277 | SN_EXPORT_UINT32(crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_OPSLIMIT_INTERACTIVE) | |
| 3278 | SN_EXPORT_UINT32(crypto_pwhash_MEMLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE) | |
| 3279 | SN_EXPORT_UINT32(crypto_pwhash_OPSLIMIT_MODERATE, crypto_pwhash_OPSLIMIT_MODERATE) | |
| 3280 | SN_EXPORT_UINT32(crypto_pwhash_MEMLIMIT_MODERATE, crypto_pwhash_MEMLIMIT_MODERATE) | |
| 3281 | SN_EXPORT_UINT32(crypto_pwhash_OPSLIMIT_SENSITIVE, crypto_pwhash_OPSLIMIT_SENSITIVE) | |
| 3282 | SN_EXPORT_UINT32(crypto_pwhash_MEMLIMIT_SENSITIVE, crypto_pwhash_MEMLIMIT_SENSITIVE) | |
| 3283 | SN_EXPORT_STRING(crypto_pwhash_PRIMITIVE, crypto_pwhash_PRIMITIVE) | |
| 3284 | ||
| 3285 | SN_EXPORT_FUNCTION(crypto_pwhash_scryptsalsa208sha256, sn_crypto_pwhash_scryptsalsa208sha256) | |
| 3286 | SN_EXPORT_FUNCTION(crypto_pwhash_scryptsalsa208sha256_str, sn_crypto_pwhash_scryptsalsa208sha256_str) | |
| 3287 | SN_EXPORT_FUNCTION(crypto_pwhash_scryptsalsa208sha256_str_verify, sn_crypto_pwhash_scryptsalsa208sha256_str_verify) | |
| 3288 | SN_EXPORT_FUNCTION(crypto_pwhash_scryptsalsa208sha256_str_needs_rehash, sn_crypto_pwhash_scryptsalsa208sha256_str_needs_rehash) | |
| 3289 | SN_EXPORT_FUNCTION(crypto_pwhash_scryptsalsa208sha256_async, sn_crypto_pwhash_scryptsalsa208sha256_async) | |
| 3290 | SN_EXPORT_FUNCTION(crypto_pwhash_scryptsalsa208sha256_str_async, sn_crypto_pwhash_scryptsalsa208sha256_str_async) | |
| 3291 | SN_EXPORT_FUNCTION(crypto_pwhash_scryptsalsa208sha256_str_verify_async, sn_crypto_pwhash_scryptsalsa208sha256_str_verify_async) | |
| 3292 | SN_EXPORT_UINT32(crypto_pwhash_scryptsalsa208sha256_BYTES_MIN, crypto_pwhash_scryptsalsa208sha256_BYTES_MIN) | |
| 3293 | SN_EXPORT_UINT32(crypto_pwhash_scryptsalsa208sha256_BYTES_MAX, crypto_pwhash_scryptsalsa208sha256_BYTES_MAX) | |
| 3294 | SN_EXPORT_UINT32(crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN, crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN) | |
| 3295 | SN_EXPORT_UINT32(crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX, crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX) | |
| 3296 | SN_EXPORT_UINT32(crypto_pwhash_scryptsalsa208sha256_SALTBYTES, crypto_pwhash_scryptsalsa208sha256_SALTBYTES) | |
| 3297 | SN_EXPORT_UINT32(crypto_pwhash_scryptsalsa208sha256_STRBYTES, crypto_pwhash_scryptsalsa208sha256_STRBYTES) | |
| 3298 | SN_EXPORT_STRING(crypto_pwhash_scryptsalsa208sha256_STRPREFIX, crypto_pwhash_scryptsalsa208sha256_STRPREFIX) | |
| 3299 | SN_EXPORT_UINT32(crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN) | |
| 3300 | SN_EXPORT_UINT32(crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX) | |
| 3301 | SN_EXPORT_UINT32(crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN) | |
| 3302 | SN_EXPORT_UINT32(crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX) | |
| 3303 | SN_EXPORT_UINT32(crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE) | |
| 3304 | SN_EXPORT_UINT32(crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE, crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) | |
| 3305 | SN_EXPORT_UINT32(crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE, crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE) | |
| 3306 | SN_EXPORT_UINT32(crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE, crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE) | |
| 3307 | ||
| 3308 | // crypto_scalarmult | |
| 3309 | ||
| 3310 | SN_EXPORT_FUNCTION(crypto_scalarmult_base, sn_crypto_scalarmult_base) | |
| 3311 | SN_EXPORT_FUNCTION(crypto_scalarmult, sn_crypto_scalarmult) | |
| 3312 | SN_EXPORT_STRING(crypto_scalarmult_PRIMITIVE, crypto_scalarmult_PRIMITIVE) | |
| 3313 | SN_EXPORT_UINT32(crypto_scalarmult_BYTES, crypto_scalarmult_BYTES) | |
| 3314 | SN_EXPORT_UINT32(crypto_scalarmult_SCALARBYTES, crypto_scalarmult_SCALARBYTES) | |
| 3315 | ||
| 3316 | SN_EXPORT_FUNCTION(crypto_scalarmult_ed25519_base, sn_crypto_scalarmult_ed25519_base) | |
| 3317 | SN_EXPORT_FUNCTION(crypto_scalarmult_ed25519, sn_crypto_scalarmult_ed25519) | |
| 3318 | SN_EXPORT_FUNCTION(crypto_scalarmult_ed25519_base_noclamp, sn_crypto_scalarmult_ed25519_base_noclamp) | |
| 3319 | SN_EXPORT_FUNCTION(crypto_scalarmult_ed25519_noclamp, sn_crypto_scalarmult_ed25519_noclamp) | |
| 3320 | SN_EXPORT_UINT32(crypto_scalarmult_ed25519_BYTES, crypto_scalarmult_ed25519_BYTES) | |
| 3321 | SN_EXPORT_UINT32(crypto_scalarmult_ed25519_SCALARBYTES, crypto_scalarmult_ed25519_SCALARBYTES) | |
| 3322 | ||
| 3323 | // crypto_secretbox | |
| 3324 | ||
| 3325 | SN_EXPORT_FUNCTION(crypto_secretbox_easy, sn_crypto_secretbox_easy) | |
| 3326 | SN_EXPORT_FUNCTION(crypto_secretbox_open_easy, sn_crypto_secretbox_open_easy) | |
| 3327 | SN_EXPORT_FUNCTION(crypto_secretbox_detached, sn_crypto_secretbox_detached) | |
| 3328 | SN_EXPORT_FUNCTION(crypto_secretbox_open_detached, sn_crypto_secretbox_open_detached) | |
| 3329 | SN_EXPORT_UINT32(crypto_secretbox_KEYBYTES, crypto_secretbox_KEYBYTES) | |
| 3330 | SN_EXPORT_UINT32(crypto_secretbox_NONCEBYTES, crypto_secretbox_NONCEBYTES) | |
| 3331 | SN_EXPORT_UINT32(crypto_secretbox_MACBYTES, crypto_secretbox_MACBYTES) | |
| 3332 | SN_EXPORT_STRING(crypto_secretbox_PRIMITIVE, crypto_secretbox_PRIMITIVE) | |
| 3333 | ||
| 3334 | // crypto_secretstream | |
| 3335 | ||
| 3336 | SN_EXPORT_FUNCTION(crypto_secretstream_xchacha20poly1305_keygen, sn_crypto_secretstream_xchacha20poly1305_keygen) | |
| 3337 | SN_EXPORT_FUNCTION(crypto_secretstream_xchacha20poly1305_init_push, sn_crypto_secretstream_xchacha20poly1305_init_push) | |
| 3338 | SN_EXPORT_FUNCTION(crypto_secretstream_xchacha20poly1305_init_pull, sn_crypto_secretstream_xchacha20poly1305_init_pull) | |
| 3339 | SN_EXPORT_FUNCTION(crypto_secretstream_xchacha20poly1305_push, sn_crypto_secretstream_xchacha20poly1305_push) | |
| 3340 | SN_EXPORT_FUNCTION(crypto_secretstream_xchacha20poly1305_pull, sn_crypto_secretstream_xchacha20poly1305_pull) | |
| 3341 | SN_EXPORT_FUNCTION(crypto_secretstream_xchacha20poly1305_rekey, sn_crypto_secretstream_xchacha20poly1305_rekey) | |
| 3342 | SN_EXPORT_UINT32(crypto_secretstream_xchacha20poly1305_STATEBYTES, sizeof(crypto_secretstream_xchacha20poly1305_state)) | |
| 3343 | SN_EXPORT_UINT32(crypto_secretstream_xchacha20poly1305_ABYTES, crypto_secretstream_xchacha20poly1305_ABYTES) | |
| 3344 | SN_EXPORT_UINT32(crypto_secretstream_xchacha20poly1305_HEADERBYTES, crypto_secretstream_xchacha20poly1305_HEADERBYTES) | |
| 3345 | SN_EXPORT_UINT32(crypto_secretstream_xchacha20poly1305_KEYBYTES, crypto_secretstream_xchacha20poly1305_KEYBYTES) | |
| 3346 | SN_EXPORT_UINT32(crypto_secretstream_xchacha20poly1305_TAGBYTES, 1) | |
| 3347 | SN_EXPORT_UINT64(crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX, crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) | |
| 3348 | SN_EXPORT_UINT32(crypto_secretstream_xchacha20poly1305_TAG_MESSAGE, crypto_secretstream_xchacha20poly1305_TAG_MESSAGE) | |
| 3349 | SN_EXPORT_UINT32(crypto_secretstream_xchacha20poly1305_TAG_PUSH, crypto_secretstream_xchacha20poly1305_TAG_PUSH) | |
| 3350 | SN_EXPORT_UINT32(crypto_secretstream_xchacha20poly1305_TAG_REKEY, crypto_secretstream_xchacha20poly1305_TAG_REKEY) | |
| 3351 | SN_EXPORT_UINT32(crypto_secretstream_xchacha20poly1305_TAG_FINAL, crypto_secretstream_xchacha20poly1305_TAG_FINAL) | |
| 3352 | ||
| 3353 | // crypto_shorthash | |
| 3354 | ||
| 3355 | SN_EXPORT_FUNCTION(crypto_shorthash, sn_crypto_shorthash) | |
| 3356 | SN_EXPORT_UINT32(crypto_shorthash_BYTES, crypto_shorthash_BYTES) | |
| 3357 | SN_EXPORT_UINT32(crypto_shorthash_KEYBYTES, crypto_shorthash_KEYBYTES) | |
| 3358 | SN_EXPORT_STRING(crypto_shorthash_PRIMITIVE, crypto_shorthash_PRIMITIVE) | |
| 3359 | ||
| 3360 | // crypto_sign | |
| 3361 | ||
| 3362 | SN_EXPORT_FUNCTION(crypto_sign_keypair, sn_crypto_sign_keypair) | |
| 3363 | SN_EXPORT_FUNCTION(crypto_sign_seed_keypair, sn_crypto_sign_seed_keypair) | |
| 3364 | SN_EXPORT_FUNCTION(crypto_sign, sn_crypto_sign) | |
| 3365 | SN_EXPORT_FUNCTION(crypto_sign_open, sn_crypto_sign_open) | |
| 3366 | SN_EXPORT_FUNCTION(crypto_sign_detached, sn_crypto_sign_detached) | |
| 3367 | SN_EXPORT_FUNCTION(crypto_sign_verify_detached, sn_crypto_sign_verify_detached) | |
| 3368 | SN_EXPORT_FUNCTION(crypto_sign_ed25519_sk_to_pk, sn_crypto_sign_ed25519_sk_to_pk) | |
| 3369 | SN_EXPORT_FUNCTION(crypto_sign_ed25519_pk_to_curve25519, sn_crypto_sign_ed25519_pk_to_curve25519) | |
| 3370 | SN_EXPORT_FUNCTION(crypto_sign_ed25519_sk_to_curve25519, sn_crypto_sign_ed25519_sk_to_curve25519) | |
| 3371 | SN_EXPORT_UINT32(crypto_sign_SEEDBYTES, crypto_sign_SEEDBYTES) | |
| 3372 | SN_EXPORT_UINT32(crypto_sign_PUBLICKEYBYTES, crypto_sign_PUBLICKEYBYTES) | |
| 3373 | SN_EXPORT_UINT32(crypto_sign_SECRETKEYBYTES, crypto_sign_SECRETKEYBYTES) | |
| 3374 | SN_EXPORT_UINT32(crypto_sign_BYTES, crypto_sign_BYTES) | |
| 3375 | ||
| 3376 | // crypto_stream | |
| 3377 | ||
| 3378 | SN_EXPORT_FUNCTION(crypto_stream, sn_crypto_stream) | |
| 3379 | SN_EXPORT_UINT32(crypto_stream_KEYBYTES, crypto_stream_KEYBYTES) | |
| 3380 | SN_EXPORT_UINT32(crypto_stream_NONCEBYTES, crypto_stream_NONCEBYTES) | |
| 3381 | SN_EXPORT_STRING(crypto_stream_PRIMITIVE, crypto_stream_PRIMITIVE) | |
| 3382 | ||
| 3383 | SN_EXPORT_FUNCTION(crypto_stream_xor, sn_crypto_stream_xor) | |
| 3384 | SN_EXPORT_FUNCTION(crypto_stream_xor_init, sn_crypto_stream_xor_wrap_init) | |
| 3385 | SN_EXPORT_FUNCTION(crypto_stream_xor_update, sn_crypto_stream_xor_wrap_update) | |
| 3386 | SN_EXPORT_FUNCTION(crypto_stream_xor_final, sn_crypto_stream_xor_wrap_final) | |
| 3387 | SN_EXPORT_UINT32(crypto_stream_xor_STATEBYTES, sizeof(sn_crypto_stream_xor_state)) | |
| 3388 | ||
| 3389 | SN_EXPORT_FUNCTION(crypto_stream_chacha20, sn_crypto_stream_chacha20) | |
| 3390 | SN_EXPORT_UINT32(crypto_stream_chacha20_KEYBYTES, crypto_stream_chacha20_KEYBYTES) | |
| 3391 | SN_EXPORT_UINT32(crypto_stream_chacha20_NONCEBYTES, crypto_stream_chacha20_NONCEBYTES) | |
| 3392 | SN_EXPORT_UINT64(crypto_stream_chacha20_MESSAGEBYTES_MAX, crypto_stream_chacha20_MESSAGEBYTES_MAX) | |
| 3393 | ||
| 3394 | SN_EXPORT_FUNCTION(crypto_stream_chacha20_xor, sn_crypto_stream_chacha20_xor) | |
| 3395 | SN_EXPORT_FUNCTION(crypto_stream_chacha20_xor_ic, sn_crypto_stream_chacha20_xor_ic) | |
| 3396 | SN_EXPORT_FUNCTION(crypto_stream_chacha20_xor_init, sn_crypto_stream_chacha20_xor_wrap_init) | |
| 3397 | SN_EXPORT_FUNCTION(crypto_stream_chacha20_xor_update, sn_crypto_stream_chacha20_xor_wrap_update) | |
| 3398 | SN_EXPORT_FUNCTION(crypto_stream_chacha20_xor_final, sn_crypto_stream_chacha20_xor_wrap_final) | |
| 3399 | SN_EXPORT_UINT32(crypto_stream_chacha20_xor_STATEBYTES, sizeof(sn_crypto_stream_chacha20_xor_state)) | |
| 3400 | ||
| 3401 | SN_EXPORT_FUNCTION(crypto_stream_chacha20_ietf, sn_crypto_stream_chacha20_ietf) | |
| 3402 | SN_EXPORT_UINT32(crypto_stream_chacha20_ietf_KEYBYTES, crypto_stream_chacha20_ietf_KEYBYTES) | |
| 3403 | SN_EXPORT_UINT32(crypto_stream_chacha20_ietf_NONCEBYTES, crypto_stream_chacha20_ietf_NONCEBYTES) | |
| 3404 | SN_EXPORT_UINT64(crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX, crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX) | |
| 3405 | SN_EXPORT_UINT32(crypto_stream_chacha20_ietf_xor_STATEBYTES, sizeof(sn_crypto_stream_chacha20_ietf_xor_state)) | |
| 3406 | ||
| 3407 | SN_EXPORT_FUNCTION(crypto_stream_chacha20_ietf_xor, sn_crypto_stream_chacha20_ietf_xor) | |
| 3408 | SN_EXPORT_FUNCTION(crypto_stream_chacha20_ietf_xor_ic, sn_crypto_stream_chacha20_ietf_xor_ic) | |
| 3409 | SN_EXPORT_FUNCTION(crypto_stream_chacha20_ietf_xor_init, sn_crypto_stream_chacha20_ietf_xor_wrap_init) | |
| 3410 | SN_EXPORT_FUNCTION(crypto_stream_chacha20_ietf_xor_update, sn_crypto_stream_chacha20_ietf_xor_wrap_update) | |
| 3411 | SN_EXPORT_FUNCTION(crypto_stream_chacha20_ietf_xor_final, sn_crypto_stream_chacha20_ietf_xor_wrap_final) | |
| 3412 | ||
| 3413 | SN_EXPORT_FUNCTION(crypto_stream_xchacha20, sn_crypto_stream_xchacha20) | |
| 3414 | SN_EXPORT_UINT32(crypto_stream_xchacha20_KEYBYTES, crypto_stream_xchacha20_KEYBYTES) | |
| 3415 | SN_EXPORT_UINT32(crypto_stream_xchacha20_NONCEBYTES, crypto_stream_xchacha20_NONCEBYTES) | |
| 3416 | SN_EXPORT_UINT64(crypto_stream_xchacha20_MESSAGEBYTES_MAX, crypto_stream_xchacha20_MESSAGEBYTES_MAX) | |
| 3417 | ||
| 3418 | SN_EXPORT_FUNCTION(crypto_stream_xchacha20_xor, sn_crypto_stream_xchacha20_xor) | |
| 3419 | SN_EXPORT_FUNCTION(crypto_stream_xchacha20_xor_ic, sn_crypto_stream_xchacha20_xor_ic) | |
| 3420 | SN_EXPORT_FUNCTION(crypto_stream_xchacha20_xor_init, sn_crypto_stream_xchacha20_xor_wrap_init) | |
| 3421 | SN_EXPORT_FUNCTION(crypto_stream_xchacha20_xor_update, sn_crypto_stream_xchacha20_xor_wrap_update) | |
| 3422 | SN_EXPORT_FUNCTION(crypto_stream_xchacha20_xor_final, sn_crypto_stream_xchacha20_xor_wrap_final) | |
| 3423 | SN_EXPORT_FUNCTION(crypto_stream_xchacha20, sn_crypto_stream_xchacha20) | |
| 3424 | SN_EXPORT_UINT32(crypto_stream_xchacha20_xor_STATEBYTES, sizeof(sn_crypto_stream_xchacha20_xor_state)) | |
| 3425 | ||
| 3426 | SN_EXPORT_FUNCTION(crypto_stream_salsa20, sn_crypto_stream_salsa20) | |
| 3427 | SN_EXPORT_UINT32(crypto_stream_salsa20_KEYBYTES, crypto_stream_salsa20_KEYBYTES) | |
| 3428 | SN_EXPORT_UINT32(crypto_stream_salsa20_NONCEBYTES, crypto_stream_salsa20_NONCEBYTES) | |
| 3429 | SN_EXPORT_UINT64(crypto_stream_salsa20_MESSAGEBYTES_MAX, crypto_stream_salsa20_MESSAGEBYTES_MAX) | |
| 3430 | ||
| 3431 | SN_EXPORT_FUNCTION(crypto_stream_salsa20_xor, sn_crypto_stream_salsa20_xor) | |
| 3432 | SN_EXPORT_FUNCTION(crypto_stream_salsa20_xor_ic, sn_crypto_stream_salsa20_xor_ic) | |
| 3433 | SN_EXPORT_FUNCTION(crypto_stream_salsa20_xor_init, sn_crypto_stream_salsa20_xor_wrap_init) | |
| 3434 | SN_EXPORT_FUNCTION(crypto_stream_salsa20_xor_update, sn_crypto_stream_salsa20_xor_wrap_update) | |
| 3435 | SN_EXPORT_FUNCTION(crypto_stream_salsa20_xor_final, sn_crypto_stream_salsa20_xor_wrap_final) | |
| 3436 | SN_EXPORT_UINT32(crypto_stream_salsa20_xor_STATEBYTES, sizeof(sn_crypto_stream_salsa20_xor_state)) | |
| 3437 | ||
| 3438 | // extensions | |
| 3439 | ||
| 3440 | // tweak | |
| 3441 | ||
| 3442 | SN_EXPORT_FUNCTION(extension_tweak_ed25519_base, sn_extension_tweak_ed25519_base) | |
| 3443 | SN_EXPORT_FUNCTION(extension_tweak_ed25519_sign_detached, sn_extension_tweak_ed25519_sign_detached) | |
| 3444 | SN_EXPORT_FUNCTION(extension_tweak_ed25519_sk_to_scalar, sn_extension_tweak_ed25519_sk_to_scalar) | |
| 3445 | SN_EXPORT_FUNCTION(extension_tweak_ed25519_scalar, sn_extension_tweak_ed25519_scalar) | |
| 3446 | SN_EXPORT_FUNCTION(extension_tweak_ed25519_pk, sn_extension_tweak_ed25519_pk) | |
| 3447 | SN_EXPORT_FUNCTION(extension_tweak_ed25519_keypair, sn_extension_tweak_ed25519_keypair) | |
| 3448 | SN_EXPORT_FUNCTION(extension_tweak_ed25519_scalar_add, sn_extension_tweak_ed25519_scalar_add) | |
| 3449 | SN_EXPORT_FUNCTION(extension_tweak_ed25519_pk_add, sn_extension_tweak_ed25519_pk_add) | |
| 3450 | SN_EXPORT_FUNCTION(extension_tweak_ed25519_keypair_add, sn_extension_tweak_ed25519_keypair_add) | |
| 3451 | SN_EXPORT_UINT32(extension_tweak_ed25519_BYTES, sn__extension_tweak_ed25519_BYTES) | |
| 3452 | SN_EXPORT_UINT32(extension_tweak_ed25519_SCALARBYTES, sn__extension_tweak_ed25519_SCALARBYTES) | |
| 3453 | ||
| 3454 | // pbkdf2 | |
| 3455 | ||
| 3456 | SN_EXPORT_FUNCTION(extension_pbkdf2_sha512, sn_extension_pbkdf2_sha512) | |
| 3457 | SN_EXPORT_FUNCTION(extension_pbkdf2_sha512_async, sn_extension_pbkdf2_sha512_async) | |
| 3458 | SN_EXPORT_UINT32(extension_pbkdf2_sha512_SALTBYTES, sn__extension_pbkdf2_sha512_SALTBYTES) | |
| 3459 | SN_EXPORT_UINT32(extension_pbkdf2_sha512_HASHBYTES, sn__extension_pbkdf2_sha512_HASHBYTES) | |
| 3460 | SN_EXPORT_UINT32(extension_pbkdf2_sha512_ITERATIONS_MIN, sn__extension_pbkdf2_sha512_ITERATIONS_MIN) | |
| 3461 | SN_EXPORT_UINT64(extension_pbkdf2_sha512_BYTES_MAX, sn__extension_pbkdf2_sha512_BYTES_MAX) | |
| 3462 | ||
| 3463 | return exports; | |
| 3464 | } | |
| 3465 | ||
| 3466 | static napi_value Init(napi_env env, napi_value exports) { | |
| 3467 | return create_sodium_native(env); | |
| 3468 | } | |
| 3469 | ||
| 3470 | NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) |
| … | … | @@ -0,0 +1,90 @@ |
| 1 | /*- | |
| 2 | * Copyright 2005,2007,2009 Colin Percival | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 24 | * SUCH DAMAGE. | |
| 25 | */ | |
| 26 | ||
| 27 | /* | |
| 28 | * Adapted from libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf-sha256.c | |
| 29 | */ | |
| 30 | ||
| 31 | #include <string.h> | |
| 32 | #include <sodium.h> | |
| 33 | ||
| 34 | #include "pbkdf2.h" | |
| 35 | ||
| 36 | /** | |
| 37 | * pbkdf2_sha512(passwd, passwdlen, salt, saltlen, c, buf, dkLen): | |
| 38 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and | |
| 39 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). | |
| 40 | */ | |
| 41 | int | |
| 42 | sn__extension_pbkdf2_sha512(const unsigned char *passwd, size_t passwdlen, | |
| 43 | const unsigned char *salt, size_t saltlen, uint64_t c, | |
| 44 | unsigned char *buf, size_t dkLen) | |
| 45 | { | |
| 46 | crypto_auth_hmacsha512_state PShctx, hctx; | |
| 47 | size_t i; | |
| 48 | unsigned char ivec[4]; | |
| 49 | unsigned char U[64]; | |
| 50 | unsigned char T[64]; | |
| 51 | uint64_t j; | |
| 52 | unsigned int k; | |
| 53 | size_t clen; | |
| 54 | ||
| 55 | if (dkLen > sn__extension_pbkdf2_sha512_BYTES_MAX) { | |
| 56 | return -1; | |
| 57 | } | |
| 58 | ||
| 59 | crypto_auth_hmacsha512_init(&PShctx, passwd, passwdlen); | |
| 60 | crypto_auth_hmacsha512_update(&PShctx, salt, saltlen); | |
| 61 | ||
| 62 | for (i = 0; i * crypto_auth_hmacsha512_BYTES < dkLen; i++) { | |
| 63 | SN_PBKDF2_STORE32_BE(ivec, (uint32_t)(i + 1)); | |
| 64 | memcpy(&hctx, &PShctx, sizeof(crypto_auth_hmacsha512_state)); | |
| 65 | crypto_auth_hmacsha512_update(&hctx, ivec, 4); | |
| 66 | crypto_auth_hmacsha512_final(&hctx, U); | |
| 67 | ||
| 68 | memcpy(T, U, crypto_auth_hmacsha512_BYTES); | |
| 69 | /* LCOV_EXCL_START */ | |
| 70 | for (j = 2; j <= c; j++) { | |
| 71 | crypto_auth_hmacsha512_init(&hctx, passwd, passwdlen); | |
| 72 | crypto_auth_hmacsha512_update(&hctx, U, crypto_auth_hmacsha512_BYTES); | |
| 73 | crypto_auth_hmacsha512_final(&hctx, U); | |
| 74 | ||
| 75 | for (k = 0; k < crypto_auth_hmacsha512_BYTES; k++) { | |
| 76 | T[k] ^= U[k]; | |
| 77 | } | |
| 78 | } | |
| 79 | /* LCOV_EXCL_STOP */ | |
| 80 | ||
| 81 | clen = dkLen - i * crypto_auth_hmacsha512_BYTES; | |
| 82 | if (clen > crypto_auth_hmacsha512_BYTES) { | |
| 83 | clen = crypto_auth_hmacsha512_BYTES; | |
| 84 | } | |
| 85 | memcpy(&buf[i * crypto_auth_hmacsha512_BYTES], T, clen); | |
| 86 | } | |
| 87 | sodium_memzero((void *) &PShctx, sizeof PShctx); | |
| 88 | ||
| 89 | return 0; | |
| 90 | } |
| … | … | @@ -0,0 +1,54 @@ |
| 1 | /*- | |
| 2 | * Copyright 2005,2007,2009 Colin Percival | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 24 | * SUCH DAMAGE. | |
| 25 | * | |
| 26 | */ | |
| 27 | ||
| 28 | /* | |
| 29 | * Adapted from libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf-sha256.c | |
| 30 | */ | |
| 31 | ||
| 32 | #include <sodium.h> | |
| 33 | ||
| 34 | #define SN_PBKDF2_STORE32_BE(buf, n32) \ | |
| 35 | buf[0] = n32 >> 24 & 0xff; \ | |
| 36 | buf[1] = n32 >> 16 & 0xff; \ | |
| 37 | buf[2] = n32 >> 8 & 0xff; \ | |
| 38 | buf[3] = n32 >> 0 & 0xff; | |
| 39 | ||
| 40 | #define sn__extension_pbkdf2_sha512_SALTBYTES 16U | |
| 41 | ||
| 42 | #define sn__extension_pbkdf2_sha512_HASHBYTES crypto_hash_sha512_BYTES | |
| 43 | ||
| 44 | #define sn__extension_pbkdf2_sha512_ITERATIONS_MIN 1U | |
| 45 | ||
| 46 | #define sn__extension_pbkdf2_sha512_BYTES_MAX 0x3fffffffc0ULL | |
| 47 | ||
| 48 | /** | |
| 49 | * extension_pbkdf2_sha512(passwd, passwdlen, salt, saltlen, c, buf, dkLen): | |
| 50 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and | |
| 51 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). | |
| 52 | */ | |
| 53 | int sn__extension_pbkdf2_sha512(const unsigned char *, size_t, const unsigned char *, size_t, | |
| 54 | uint64_t, unsigned char *, size_t); |
| … | … | @@ -0,0 +1,206 @@ |
| 1 | #include "tweak.h" | |
| 2 | ||
| 3 | /* | |
| 4 | *EXPERIMENTAL API* | |
| 5 | ||
| 6 | This module is an experimental implementation of a key tweaking protocol | |
| 7 | over ed25519 keys. The signature algorithm has been reimplemented from | |
| 8 | libsodium, but the nonce generation algorithm is *non-standard*. | |
| 9 | ||
| 10 | Use at your own risk | |
| 11 | */ | |
| 12 | ||
| 13 | static void _extension_tweak_nonce (unsigned char *nonce, const unsigned char *n, | |
| 14 | const unsigned char *m, unsigned long long mlen) | |
| 15 | { | |
| 16 | // dom2(x, y) with x = 0 (not prehashed) and y = "crypto_tweak_ed25519" | |
| 17 | static const unsigned char TWEAK_PREFIX[32 + 2 + 20] = { | |
| 18 | 'S', 'i', 'g', 'E', 'd', '2', '5', '5', '1', '9', ' ', | |
| 19 | 'n', 'o', ' ', 'E', 'd', '2', '5', '5', '1', '9', ' ', | |
| 20 | 'c', 'o', 'l', 'l', 'i', 's', 'i', 'o', 'n', 's', 0, | |
| 21 | 20, 'c', 'r', 'y', 'p', 't', 'o', '_', 't', 'w', 'e', | |
| 22 | 'a', 'k', '_', 'e', 'd', '2', '5', '5', '1', '9' | |
| 23 | }; | |
| 24 | ||
| 25 | crypto_hash_sha512_state hs; | |
| 26 | ||
| 27 | crypto_hash_sha512_init(&hs); | |
| 28 | crypto_hash_sha512_update(&hs, TWEAK_PREFIX, sizeof TWEAK_PREFIX); | |
| 29 | crypto_hash_sha512_update(&hs, n, 32); | |
| 30 | crypto_hash_sha512_update(&hs, m, mlen); | |
| 31 | crypto_hash_sha512_final(&hs, nonce); | |
| 32 | } | |
| 33 | ||
| 34 | static inline void | |
| 35 | _crypto_sign_ed25519_clamp(unsigned char k[32]) | |
| 36 | { | |
| 37 | k[0] &= 248; | |
| 38 | k[31] &= 127; | |
| 39 | k[31] |= 64; | |
| 40 | } | |
| 41 | ||
| 42 | static void _extension_tweak_ed25519(unsigned char *q, unsigned char *n, | |
| 43 | const unsigned char *ns, unsigned long long nslen) | |
| 44 | { | |
| 45 | sodium_memzero(q, sizeof q); | |
| 46 | ||
| 47 | crypto_hash(n, ns, nslen); | |
| 48 | n[31] &= 127; // clear highest bit | |
| 49 | ||
| 50 | crypto_scalarmult_ed25519_base_noclamp(q, n); | |
| 51 | ||
| 52 | // hash tweak until we get a valid tweaked q | |
| 53 | while (crypto_core_ed25519_is_valid_point(q) != 1) { | |
| 54 | crypto_hash(n, n, 32); | |
| 55 | n[31] &= 127; // clear highest bit | |
| 56 | ||
| 57 | crypto_scalarmult_ed25519_base_noclamp(q, n); | |
| 58 | } | |
| 59 | } | |
| 60 | ||
| 61 | void sn__extension_tweak_ed25519_base(unsigned char *pk, unsigned char *scalar, | |
| 62 | const unsigned char *ns, unsigned long long nslen) | |
| 63 | { | |
| 64 | unsigned char n64[64]; | |
| 65 | ||
| 66 | _extension_tweak_ed25519(pk, n64, ns, nslen); | |
| 67 | ||
| 68 | SN_TWEAK_COPY_32(scalar, n64) | |
| 69 | } | |
| 70 | ||
| 71 | int sn__extension_tweak_ed25519_sign_detached(unsigned char *sig, unsigned long long *siglen_p, | |
| 72 | const unsigned char *m, unsigned long long mlen, | |
| 73 | const unsigned char *n, unsigned char *pk) | |
| 74 | { | |
| 75 | crypto_hash_sha512_state hs; | |
| 76 | ||
| 77 | unsigned char nonce[64]; | |
| 78 | unsigned char R[32]; | |
| 79 | unsigned char hram[64]; | |
| 80 | unsigned char _pk[32]; | |
| 81 | ||
| 82 | // check if pk was passed | |
| 83 | if (pk == NULL) { | |
| 84 | pk = _pk; | |
| 85 | ||
| 86 | // derive pk from scalar | |
| 87 | if (crypto_scalarmult_ed25519_base_noclamp(pk, n) != 0) { | |
| 88 | return -1; | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | _extension_tweak_nonce(nonce, n, m, mlen); | |
| 93 | crypto_core_ed25519_scalar_reduce(nonce, nonce); | |
| 94 | ||
| 95 | // R = G ^ nonce : curve point from nonce | |
| 96 | if (crypto_scalarmult_ed25519_base_noclamp(R, nonce) != 0) { | |
| 97 | return -1; | |
| 98 | } | |
| 99 | ||
| 100 | // generate challenge as h(ram) = hash(R, pk, message) | |
| 101 | crypto_hash_sha512_init(&hs); | |
| 102 | crypto_hash_sha512_update(&hs, R, 32); | |
| 103 | crypto_hash_sha512_update(&hs, pk, 32); | |
| 104 | crypto_hash_sha512_update(&hs, m, mlen); | |
| 105 | ||
| 106 | crypto_hash_sha512_final(&hs, hram); | |
| 107 | ||
| 108 | crypto_core_ed25519_scalar_reduce(hram, hram); | |
| 109 | ||
| 110 | // sig = nonce + n * h(ram) | |
| 111 | crypto_core_ed25519_scalar_mul(sig, hram, n); | |
| 112 | crypto_core_ed25519_scalar_add(sig + 32, nonce, sig); | |
| 113 | ||
| 114 | SN_TWEAK_COPY_32(sig, R) | |
| 115 | ||
| 116 | if (siglen_p != NULL) { | |
| 117 | *siglen_p = 64U; | |
| 118 | } | |
| 119 | ||
| 120 | return 0; | |
| 121 | } | |
| 122 | ||
| 123 | // tweak a secret key | |
| 124 | void sn__extension_tweak_ed25519_sk_to_scalar(unsigned char *n, const unsigned char *sk) | |
| 125 | { | |
| 126 | unsigned char n64[64]; | |
| 127 | ||
| 128 | // get sk scalar from seed, cf. crypto_sign_keypair_seed | |
| 129 | crypto_hash(n64, sk, 32); | |
| 130 | _crypto_sign_ed25519_clamp(n64); | |
| 131 | ||
| 132 | SN_TWEAK_COPY_32(n, n64) | |
| 133 | } | |
| 134 | ||
| 135 | // tweak a secret key | |
| 136 | void sn__extension_tweak_ed25519_scalar(unsigned char *scalar_out, | |
| 137 | const unsigned char *scalar, | |
| 138 | const unsigned char *ns, | |
| 139 | unsigned long long nslen) | |
| 140 | { | |
| 141 | unsigned char n[64]; | |
| 142 | unsigned char q[32]; | |
| 143 | ||
| 144 | _extension_tweak_ed25519(q, n, ns, nslen); | |
| 145 | crypto_core_ed25519_scalar_add(scalar_out, scalar, n); | |
| 146 | } | |
| 147 | ||
| 148 | // tweak a public key | |
| 149 | int sn__extension_tweak_ed25519_pk(unsigned char *tpk, | |
| 150 | const unsigned char *pk, | |
| 151 | const unsigned char *ns, | |
| 152 | unsigned long long nslen) | |
| 153 | { | |
| 154 | unsigned char n[64]; | |
| 155 | unsigned char q[32]; | |
| 156 | ||
| 157 | _extension_tweak_ed25519(q, n, ns, nslen); | |
| 158 | return crypto_core_ed25519_add(tpk, q, pk); | |
| 159 | } | |
| 160 | ||
| 161 | ||
| 162 | void sn__extension_tweak_ed25519_keypair(unsigned char *pk, unsigned char *scalar_out, | |
| 163 | unsigned char *scalar, const unsigned char *ns, | |
| 164 | unsigned long long nslen) | |
| 165 | { | |
| 166 | unsigned char n64[64]; | |
| 167 | ||
| 168 | crypto_hash(n64, ns, nslen); | |
| 169 | n64[31] &= 127; // clear highest bit | |
| 170 | ||
| 171 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, n64); | |
| 172 | crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 173 | ||
| 174 | // hash tweak until we get a valid tweaked point | |
| 175 | while (crypto_core_ed25519_is_valid_point(pk) != 1) { | |
| 176 | crypto_hash(n64, n64, 32); | |
| 177 | n64[31] &= 127; // clear highest bit | |
| 178 | ||
| 179 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, n64); | |
| 180 | crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 181 | } | |
| 182 | } | |
| 183 | ||
| 184 | // add tweak to scalar | |
| 185 | void sn__extension_tweak_ed25519_scalar_add(unsigned char *scalar_out, | |
| 186 | const unsigned char *scalar, | |
| 187 | const unsigned char *n) | |
| 188 | { | |
| 189 | crypto_core_ed25519_scalar_add(scalar_out, scalar, n); | |
| 190 | } | |
| 191 | ||
| 192 | // add tweak point to public key | |
| 193 | int sn__extension_tweak_ed25519_pk_add(unsigned char *tpk, | |
| 194 | const unsigned char *pk, | |
| 195 | const unsigned char *q) | |
| 196 | { | |
| 197 | return crypto_core_ed25519_add(tpk, pk, q); | |
| 198 | } | |
| 199 | ||
| 200 | ||
| 201 | int sn__extension_tweak_ed25519_keypair_add(unsigned char *pk, unsigned char *scalar_out, | |
| 202 | unsigned char *scalar, const unsigned char *tweak) | |
| 203 | { | |
| 204 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, tweak); | |
| 205 | return crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 206 | } |
| … | … | @@ -0,0 +1,54 @@ |
| 1 | #include <sodium.h> | |
| 2 | ||
| 3 | // copy 32 bytes using int64_t pointers | |
| 4 | #define SN_TWEAK_COPY_32(a, b) \ | |
| 5 | { \ | |
| 6 | long long *dst = (long long *) a; \ | |
| 7 | long long *src = (long long *) b; \ | |
| 8 | dst[0] = src[0]; \ | |
| 9 | dst[1] = src[1]; \ | |
| 10 | dst[2] = src[2]; \ | |
| 11 | dst[3] = src[3]; \ | |
| 12 | } | |
| 13 | ||
| 14 | #define sn__extension_tweak_ed25519_BYTES crypto_sign_ed25519_PUBLICKEYBYTES | |
| 15 | ||
| 16 | #define sn__extension_tweak_ed25519_SCALARBYTES crypto_scalarmult_ed25519_SCALARBYTES | |
| 17 | ||
| 18 | int sn__extension_tweak_ed25519_sign_detached(unsigned char *sig, unsigned long long *siglen_p, | |
| 19 | const unsigned char *m, unsigned long long mlen, | |
| 20 | const unsigned char *n, unsigned char *pk); | |
| 21 | ||
| 22 | void sn__extension_tweak_ed25519_base(unsigned char *pk, unsigned char *scalar, | |
| 23 | const unsigned char *ns, unsigned long long nslen); | |
| 24 | ||
| 25 | void sn__extension_tweak_ed25519_sk_to_scalar(unsigned char *scalar, const unsigned char *sk); | |
| 26 | ||
| 27 | // tweak a secret key | |
| 28 | void sn__extension_tweak_ed25519_scalar(unsigned char *scalar_out, | |
| 29 | const unsigned char *scalar, | |
| 30 | const unsigned char *ns, | |
| 31 | unsigned long long nslen); | |
| 32 | ||
| 33 | // tweak a public key | |
| 34 | int sn__extension_tweak_ed25519_pk(unsigned char *tpk, | |
| 35 | const unsigned char *pk, | |
| 36 | const unsigned char *ns, | |
| 37 | unsigned long long nslen); | |
| 38 | ||
| 39 | void sn__extension_tweak_ed25519_keypair(unsigned char *pk, unsigned char *scalar_out, | |
| 40 | unsigned char *scalar, const unsigned char *ns, | |
| 41 | unsigned long long nslen); | |
| 42 | ||
| 43 | // add tweak scalar to private key | |
| 44 | void sn__extension_tweak_ed25519_scalar_add(unsigned char *scalar_out, | |
| 45 | const unsigned char *scalar, | |
| 46 | const unsigned char *n); | |
| 47 | ||
| 48 | // add tweak point to public key | |
| 49 | int sn__extension_tweak_ed25519_pk_add(unsigned char *tpk, | |
| 50 | const unsigned char *pk, | |
| 51 | const unsigned char *q); | |
| 52 | ||
| 53 | int sn__extension_tweak_ed25519_keypair_add(unsigned char *pk, unsigned char *scalar_out, | |
| 54 | unsigned char *scalar, const unsigned char *tweak); |
| … | … | @@ -0,0 +1,3 @@ |
| 1 | require.addon = require('require-addon') | |
| 2 | ||
| 3 | module.exports = require.addon('.', __filename) |
| … | … | @@ -0,0 +1,319 @@ |
| 1 | #define SN_STATUS_THROWS(call, message) \ | |
| 2 | if ((call) != napi_ok) { \ | |
| 3 | napi_throw_error(env, NULL, message); \ | |
| 4 | return NULL; \ | |
| 5 | } | |
| 6 | ||
| 7 | #define SN_STATUS_THROWS_VOID(call, message) \ | |
| 8 | if ((call) != napi_ok) { \ | |
| 9 | napi_throw_error(env, NULL, message); \ | |
| 10 | return; \ | |
| 11 | } | |
| 12 | ||
| 13 | #define SN_THROWS(condition, message) \ | |
| 14 | if ((condition)) { \ | |
| 15 | napi_throw_error(env, NULL, message); \ | |
| 16 | return NULL; \ | |
| 17 | } | |
| 18 | ||
| 19 | #define SN_BUFFER_CAST(type, name, val) \ | |
| 20 | type name; \ | |
| 21 | size_t name##_size; \ | |
| 22 | SN_STATUS_THROWS(napi_get_buffer_info(env, val, (void **) &name, &name##_size), "") | |
| 23 | ||
| 24 | #define SN_TYPE_ASSERT(name, var, type, message) \ | |
| 25 | napi_valuetype name##_valuetype; \ | |
| 26 | SN_STATUS_THROWS(napi_typeof(env, var, &name##_valuetype), ""); \ | |
| 27 | if (name##_valuetype != type) { \ | |
| 28 | napi_throw_type_error(env, NULL, message); \ | |
| 29 | return NULL; \ | |
| 30 | } | |
| 31 | ||
| 32 | #define SN_TYPEDARRAY_ASSERT(name, var, message) \ | |
| 33 | bool name##_is_typedarray; \ | |
| 34 | SN_STATUS_THROWS(napi_is_typedarray(env, var, &name##_is_typedarray), ""); \ | |
| 35 | if (name##_is_typedarray != true) { \ | |
| 36 | napi_throw_type_error(env, NULL, message); \ | |
| 37 | return NULL; \ | |
| 38 | } | |
| 39 | ||
| 40 | #define SN_ASSERT_MIN_LENGTH(length, constant, name) \ | |
| 41 | SN_THROWS(length < constant, #name " must be at least " #constant " bytes long") | |
| 42 | ||
| 43 | ||
| 44 | #define SN_ASSERT_MAX_LENGTH(length, constant, name) \ | |
| 45 | SN_THROWS(length > constant, #name " must be at most " #constant " bytes long") | |
| 46 | ||
| 47 | ||
| 48 | #define SN_ASSERT_LENGTH(length, constant, name) \ | |
| 49 | SN_THROWS(length != constant, #name " must be " #constant " bytes long") | |
| 50 | ||
| 51 | #define SN_RANGE_THROWS(condition, message) \ | |
| 52 | if (condition) { \ | |
| 53 | napi_throw_range_error(env, NULL, message); \ | |
| 54 | return NULL; \ | |
| 55 | } | |
| 56 | ||
| 57 | #define SN_ARGV(n, method_name) \ | |
| 58 | napi_value argv[n]; \ | |
| 59 | size_t argc = n; \ | |
| 60 | SN_STATUS_THROWS(napi_get_cb_info(env, info, &argc, argv, NULL, NULL), ""); \ | |
| 61 | if (argc != n) { \ | |
| 62 | napi_throw_type_error(env, NULL, #method_name " requires " #n " argument(s)"); \ | |
| 63 | return NULL; \ | |
| 64 | } | |
| 65 | ||
| 66 | #define SN_ARGV_OPTS(required, total, method_name) \ | |
| 67 | napi_value argv[total]; \ | |
| 68 | size_t argc = total; \ | |
| 69 | SN_STATUS_THROWS(napi_get_cb_info(env, info, &argc, argv, NULL, NULL), ""); \ | |
| 70 | if (argc < required) { \ | |
| 71 | napi_throw_type_error(env, NULL, #method_name " requires at least " #required " argument(s)"); \ | |
| 72 | return NULL; \ | |
| 73 | } | |
| 74 | ||
| 75 | #define SN_EXPORT_FUNCTION(name, cb) \ | |
| 76 | { \ | |
| 77 | napi_value name##_fn; \ | |
| 78 | SN_STATUS_THROWS(napi_create_function(env, #name, NAPI_AUTO_LENGTH, cb, NULL, &name##_fn), "") \ | |
| 79 | SN_STATUS_THROWS(napi_set_named_property(env, exports, #name, name##_fn), "") \ | |
| 80 | } | |
| 81 | ||
| 82 | #define SN_EXPORT_UINT32(name, num) \ | |
| 83 | { \ | |
| 84 | napi_value name##_num; \ | |
| 85 | SN_STATUS_THROWS(napi_create_uint32(env, (uint32_t) num, &name##_num), "") \ | |
| 86 | SN_STATUS_THROWS(napi_set_named_property(env, exports, #name, name##_num), "") \ | |
| 87 | } | |
| 88 | ||
| 89 | #define SN_EXPORT_UINT64(name, num) \ | |
| 90 | { \ | |
| 91 | napi_value name##_num; \ | |
| 92 | uint64_t max = 0x1fffffffffffffULL; \ | |
| 93 | SN_STATUS_THROWS(napi_create_int64(env, (uint64_t) (max < num ? max : num), &name##_num), "") \ | |
| 94 | SN_STATUS_THROWS(napi_set_named_property(env, exports, #name, name##_num), "") \ | |
| 95 | } | |
| 96 | ||
| 97 | #define SN_EXPORT_STRING(name, string) \ | |
| 98 | { \ | |
| 99 | napi_value name##_string; \ | |
| 100 | SN_STATUS_THROWS(napi_create_string_utf8(env, string, NAPI_AUTO_LENGTH, &name##_string), "") \ | |
| 101 | SN_STATUS_THROWS(napi_set_named_property(env, exports, #name, name##_string), "") \ | |
| 102 | } | |
| 103 | ||
| 104 | #define SN_ARGV_CHECK_NULL(name, index) \ | |
| 105 | napi_valuetype name##_valuetype; \ | |
| 106 | SN_STATUS_THROWS(napi_typeof(env, argv[index], &name##_valuetype), "") \ | |
| 107 | bool name##_is_null = name##_valuetype == napi_null; | |
| 108 | ||
| 109 | #define SN_ARGV_OPTS_TYPEDARRAY(name, index) \ | |
| 110 | napi_valuetype name##_valuetype; \ | |
| 111 | void *name##_data = NULL; \ | |
| 112 | size_t name##_size = 0; \ | |
| 113 | SN_STATUS_THROWS(napi_typeof(env, argv[index], &name##_valuetype), "") \ | |
| 114 | if (name##_valuetype != napi_null && name##_valuetype != napi_undefined) { \ | |
| 115 | napi_value name##_argv = argv[index]; \ | |
| 116 | SN_TYPEDARRAY_ASSERT(name, name##_argv, #name " must be an instance of TypedArray") \ | |
| 117 | SN_OPT_TYPEDARRAY(name, name##_argv) \ | |
| 118 | } | |
| 119 | ||
| 120 | #define SN_TYPEDARRAY(name, var) \ | |
| 121 | napi_typedarray_type name##_type; \ | |
| 122 | size_t name##_length; \ | |
| 123 | void *name##_data; \ | |
| 124 | napi_get_typedarray_info(env, (var), &name##_type, &name##_length, &name##_data, NULL, NULL); \ | |
| 125 | uint8_t name##_width = typedarray_width(name##_type); \ | |
| 126 | SN_THROWS(name##_width == 0, "Unexpected TypedArray type") \ | |
| 127 | size_t name##_size = name##_length * name##_width; | |
| 128 | ||
| 129 | #define SN_TYPEDARRAY_PTR(name, var) \ | |
| 130 | napi_typedarray_type name##_type; \ | |
| 131 | size_t name##_length; \ | |
| 132 | void *name##_data; \ | |
| 133 | napi_get_typedarray_info(env, (var), &name##_type, &name##_length, &name##_data, NULL, NULL); \ | |
| 134 | uint8_t name##_width = typedarray_width(name##_type); \ | |
| 135 | SN_THROWS(name##_width == 0, "Unexpected TypedArray type") \ | |
| 136 | ||
| 137 | #define SN_OPT_TYPEDARRAY(name, var) \ | |
| 138 | napi_typedarray_type name##_type; \ | |
| 139 | size_t name##_length; \ | |
| 140 | napi_get_typedarray_info(env, (var), &name##_type, &name##_length, &name##_data, NULL, NULL); \ | |
| 141 | uint8_t name##_width = typedarray_width(name##_type); \ | |
| 142 | SN_THROWS(name##_width == 0, "Unexpected TypedArray type") \ | |
| 143 | name##_size = name##_length * name##_width; | |
| 144 | ||
| 145 | #define SN_UINT8(name, val) \ | |
| 146 | uint32_t name##_int32; \ | |
| 147 | if (napi_get_value_uint32(env, val, &name##_int32) != napi_ok) { \ | |
| 148 | napi_throw_error(env, "EINVAL", "Expected number"); \ | |
| 149 | return NULL; \ | |
| 150 | } \ | |
| 151 | SN_THROWS(name##_int32 > 255, "expect uint8") \ | |
| 152 | unsigned char name = 0xff & name##_int32; | |
| 153 | ||
| 154 | #define SN_UINT32(name, val) \ | |
| 155 | uint32_t name; \ | |
| 156 | if (napi_get_value_uint32(env, val, &name) != napi_ok) { \ | |
| 157 | napi_throw_error(env, "EINVAL", "Expected number"); \ | |
| 158 | return NULL; \ | |
| 159 | } | |
| 160 | ||
| 161 | #define SN_OPT_UINT32(name, val) \ | |
| 162 | if (napi_get_value_uint32(env, val, (uint32_t *) &name) != napi_ok) { \ | |
| 163 | napi_throw_error(env, "EINVAL", "Expected number"); \ | |
| 164 | return NULL; \ | |
| 165 | } | |
| 166 | ||
| 167 | #define SN_UINT64(name, val) \ | |
| 168 | int64_t name##_i64; \ | |
| 169 | if (napi_get_value_int64(env, val, &name##_i64) != napi_ok) { \ | |
| 170 | napi_throw_error(env, "EINVAL", "Expected number"); \ | |
| 171 | return NULL; \ | |
| 172 | } \ | |
| 173 | if (name##_i64 < 0) { \ | |
| 174 | napi_throw_error(env, "EINVAL", "Expected positive number"); \ | |
| 175 | return NULL; \ | |
| 176 | } \ | |
| 177 | uint64_t name = (uint64_t) name##_i64; | |
| 178 | ||
| 179 | #define SN_ARGV_TYPEDARRAY(name, index) \ | |
| 180 | napi_value name##_argv = argv[index]; \ | |
| 181 | SN_TYPEDARRAY_ASSERT(name, name##_argv, #name " must be an instance of TypedArray") \ | |
| 182 | SN_TYPEDARRAY(name, name##_argv) | |
| 183 | ||
| 184 | #define SN_ARGV_TYPEDARRAY_PTR(name, index) \ | |
| 185 | napi_value name##_argv = argv[index]; \ | |
| 186 | SN_TYPEDARRAY_ASSERT(name, name##_argv, #name " must be an instance of TypedArray") \ | |
| 187 | SN_TYPEDARRAY_PTR(name, name##_argv) | |
| 188 | ||
| 189 | #define SN_ARGV_BUFFER_CAST(type, name, index) \ | |
| 190 | napi_value name##_argv = argv[index]; \ | |
| 191 | SN_BUFFER_CAST(type, name, name##_argv) | |
| 192 | ||
| 193 | #define SN_OPT_ARGV_TYPEDARRAY(name, index) \ | |
| 194 | napi_value name##_argv = argv[index]; \ | |
| 195 | SN_TYPEDARRAY_ASSERT(name, name##_argv, #name " must be an instance of TypedArray") \ | |
| 196 | SN_OPT_TYPEDARRAY(name, name##_argv) | |
| 197 | ||
| 198 | #define SN_ARGV_UINT8(name, index) \ | |
| 199 | napi_value name##_argv = argv[index]; \ | |
| 200 | SN_TYPE_ASSERT(name, name##_argv, napi_number, #name " must be an instance of Number") \ | |
| 201 | SN_UINT8(name, name##_argv) | |
| 202 | ||
| 203 | #define SN_ARGV_UINT32(name, index) \ | |
| 204 | napi_value name##_argv = argv[index]; \ | |
| 205 | SN_TYPE_ASSERT(name, name##_argv, napi_number, #name " must be an instance of Number") \ | |
| 206 | SN_UINT32(name, name##_argv) | |
| 207 | ||
| 208 | #define SN_ARGV_UINT64(name, index) \ | |
| 209 | napi_value name##_argv = argv[index]; \ | |
| 210 | SN_TYPE_ASSERT(name, name##_argv, napi_number, #name " must be an instance of Number") \ | |
| 211 | SN_UINT64(name, name##_argv) | |
| 212 | ||
| 213 | #define SN_OPT_ARGV_UINT32(name, index) \ | |
| 214 | napi_value name##_argv = argv[index]; \ | |
| 215 | SN_TYPE_ASSERT(name, name##_argv, napi_number, #name " must be an instance of Number") \ | |
| 216 | SN_OPT_UINT32(name, name##_argv) | |
| 217 | ||
| 218 | #define SN_CALL(call, message) \ | |
| 219 | int success = call; \ | |
| 220 | SN_THROWS(success != 0, message) | |
| 221 | ||
| 222 | #define SN_CALL_FUNCTION(env, ctx, cb, n, argv, res) \ | |
| 223 | if (napi_make_callback(env, NULL, ctx, cb, n, argv, res) == napi_pending_exception) { \ | |
| 224 | napi_value fatal_exception; \ | |
| 225 | napi_get_and_clear_last_exception(env, &fatal_exception); \ | |
| 226 | napi_fatal_exception(env, fatal_exception); \ | |
| 227 | } | |
| 228 | ||
| 229 | #define SN_RETURN(call, message) \ | |
| 230 | int success = call; \ | |
| 231 | SN_THROWS(success != 0, message) \ | |
| 232 | return NULL; | |
| 233 | ||
| 234 | #define SN_RETURN_BOOLEAN(call) \ | |
| 235 | int success = call; \ | |
| 236 | napi_value result; \ | |
| 237 | SN_THROWS(napi_get_boolean(env, success == 0, &result) != napi_ok, "result not boolean") \ | |
| 238 | return result; | |
| 239 | ||
| 240 | #define SN_RETURN_BOOLEAN_FROM_1(call) \ | |
| 241 | int success = call; \ | |
| 242 | napi_value result; \ | |
| 243 | SN_THROWS(napi_get_boolean(env, success != 0, &result) != napi_ok, "result not boolean") \ | |
| 244 | return result; | |
| 245 | ||
| 246 | #define SN_ASYNC_CHECK_FOR_ERROR(message) \ | |
| 247 | if (req->n == 0) { \ | |
| 248 | napi_get_null(req->env, &argv[0]); \ | |
| 249 | } else { \ | |
| 250 | napi_value err_msg; \ | |
| 251 | napi_create_string_utf8(req->env, #message, NAPI_AUTO_LENGTH, &err_msg); \ | |
| 252 | napi_create_error(req->env, NULL, err_msg, &argv[0]); \ | |
| 253 | } | |
| 254 | ||
| 255 | #define SN_CALLBACK_CHECK_FOR_ERROR(message) \ | |
| 256 | if (task->code == 0) { \ | |
| 257 | napi_get_null(req->env, &argv[0]); \ | |
| 258 | } else { \ | |
| 259 | napi_value err_msg; \ | |
| 260 | napi_create_string_utf8(req->env, #message, NAPI_AUTO_LENGTH, &err_msg); \ | |
| 261 | napi_create_error(req->env, NULL, err_msg, &argv[0]); \ | |
| 262 | } | |
| 263 | ||
| 264 | #define SN_QUEUE_WORK(req, execute, complete) \ | |
| 265 | uv_loop_t *loop; \ | |
| 266 | napi_get_uv_event_loop(env, &loop); \ | |
| 267 | uv_queue_work(loop, (uv_work_t *) req, execute, complete); | |
| 268 | ||
| 269 | #define SN_QUEUE_TASK(task, execute, complete) \ | |
| 270 | uv_loop_t *loop; \ | |
| 271 | napi_get_uv_event_loop(env, &loop); \ | |
| 272 | uv_queue_work(loop, (uv_work_t *) task, execute, complete); | |
| 273 | ||
| 274 | #define SN_ASYNC_TASK(cb_pos) \ | |
| 275 | task->req = (void *) req; \ | |
| 276 | napi_value promise; \ | |
| 277 | if (argc > cb_pos) { \ | |
| 278 | task->type = sn_async_task_callback; \ | |
| 279 | promise = NULL; \ | |
| 280 | napi_value cb = argv[cb_pos]; \ | |
| 281 | napi_valuetype type; \ | |
| 282 | SN_STATUS_THROWS(napi_typeof(env, cb, &type), "") \ | |
| 283 | if (type != napi_function) { \ | |
| 284 | napi_throw_error(env, "EINVAL", "Callback must be a function"); \ | |
| 285 | return NULL; \ | |
| 286 | } \ | |
| 287 | SN_STATUS_THROWS(napi_create_reference(env, cb, 1, &task->cb), "") \ | |
| 288 | } else { \ | |
| 289 | task->type = sn_async_task_promise; \ | |
| 290 | napi_create_promise(env, &task->deferred, &promise); \ | |
| 291 | } | |
| 292 | ||
| 293 | #define SN_ASYNC_COMPLETE(message) \ | |
| 294 | napi_value argv[1]; \ | |
| 295 | switch (task->type) { \ | |
| 296 | case sn_async_task_promise: { \ | |
| 297 | if (task->code == 0) { \ | |
| 298 | napi_get_null(req->env, &argv[0]); \ | |
| 299 | napi_resolve_deferred(req->env, task->deferred, argv[0]); \ | |
| 300 | } else { \ | |
| 301 | napi_value err_msg; \ | |
| 302 | napi_create_string_utf8(req->env, #message, NAPI_AUTO_LENGTH, &err_msg); \ | |
| 303 | napi_create_error(req->env, NULL, err_msg, &argv[0]); \ | |
| 304 | napi_reject_deferred(req->env, task->deferred, argv[0]); \ | |
| 305 | } \ | |
| 306 | task->deferred = NULL; \ | |
| 307 | break; \ | |
| 308 | } \ | |
| 309 | case sn_async_task_callback: { \ | |
| 310 | SN_CALLBACK_CHECK_FOR_ERROR(#message) \ | |
| 311 | napi_value callback; \ | |
| 312 | napi_get_reference_value(req->env, task->cb, &callback); \ | |
| 313 | napi_value return_val; \ | |
| 314 | SN_CALL_FUNCTION(req->env, global, callback, 1, argv, &return_val) \ | |
| 315 | napi_close_handle_scope(req->env, scope); \ | |
| 316 | napi_delete_reference(req->env, task->cb); \ | |
| 317 | break; \ | |
| 318 | } \ | |
| 319 | } |
| … | … | @@ -0,0 +1,47 @@ |
| 1 | { | |
| 2 | "name": "sodium-native", | |
| 3 | "version": "4.3.3", | |
| 4 | "description": "Low level bindings for libsodium", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js", | |
| 8 | "binding.c", | |
| 9 | "macros.h", | |
| 10 | "extensions", | |
| 11 | "prebuilds", | |
| 12 | "CMakeLists.txt" | |
| 13 | ], | |
| 14 | "addon": true, | |
| 15 | "dependencies": { | |
| 16 | "require-addon": "^1.1.0" | |
| 17 | }, | |
| 18 | "devDependencies": { | |
| 19 | "brittle": "^3.5.0", | |
| 20 | "cmake-bare": "^1.1.10", | |
| 21 | "cmake-fetch": "^1.0.1", | |
| 22 | "cmake-napi": "^1.0.5", | |
| 23 | "standard": "^17.1.0" | |
| 24 | }, | |
| 25 | "scripts": { | |
| 26 | "test": "standard && brittle test/*.js" | |
| 27 | }, | |
| 28 | "standard": { | |
| 29 | "ignore": [ | |
| 30 | "/test/fixtures/*.js" | |
| 31 | ] | |
| 32 | }, | |
| 33 | "repository": { | |
| 34 | "type": "git", | |
| 35 | "url": "https://github.com/holepunchto/sodium-native.git" | |
| 36 | }, | |
| 37 | "contributors": [ | |
| 38 | "Emil Bay <github@tixz.dk> (http://bayes.dk)", | |
| 39 | "Mathias Buus <mathiasbuus@gmail.com> (https://mafinto.sh)", | |
| 40 | "Christophe Diederichs <chm-diederichs@hyperdivision.dk>" | |
| 41 | ], | |
| 42 | "license": "MIT", | |
| 43 | "bugs": { | |
| 44 | "url": "https://github.com/holepunchto/sodium-native/issues" | |
| 45 | }, | |
| 46 | "homepage": "https://github.com/holepunchto/sodium-native" | |
| 47 | } |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2021 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,49 @@ |
| 1 | # sodium-secretstream | |
| 2 | ||
| 3 | Wraps libsodium's secretstream in a higher level abstraction | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install sodium-secretstream | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const { Pull, Push, keygen } = require('sodium-secretstream') | |
| 13 | ||
| 14 | const key = keygen() | |
| 15 | ||
| 16 | // the sender | |
| 17 | const push = new Push(key) | |
| 18 | ||
| 19 | // the receiver | |
| 20 | const pull = new Pull(key) | |
| 21 | ||
| 22 | // send header to the other side | |
| 23 | pull.init(push.header) | |
| 24 | ||
| 25 | // send the cipher to the other side | |
| 26 | const cipher1 = push.next(Buffer.from('test')) | |
| 27 | ||
| 28 | // prints "test" | |
| 29 | console.log(pull.next(cipher1)) | |
| 30 | ||
| 31 | // send the cipher to the other side | |
| 32 | const cipher2 = push.next(Buffer.from('test 2')) | |
| 33 | ||
| 34 | // prints "test 2" | |
| 35 | console.log(pull.next(cipher2)) | |
| 36 | ||
| 37 | // when done send a final signal | |
| 38 | const cipher3 = push.final() | |
| 39 | ||
| 40 | // prints <empty buffer> | |
| 41 | console.log(pull.next(cipher3)) | |
| 42 | ||
| 43 | // but sets pull.final to true | |
| 44 | console.log(pull.final) | |
| 45 | ``` | |
| 46 | ||
| 47 | ## License | |
| 48 | ||
| 49 | MIT |
| … | … | @@ -0,0 +1,33 @@ |
| 1 | const { Pull, Push, keygen } = require('./') | |
| 2 | ||
| 3 | const key = keygen() | |
| 4 | ||
| 5 | // the sender | |
| 6 | const push = new Push(key) | |
| 7 | ||
| 8 | // the receiver | |
| 9 | const pull = new Pull(key) | |
| 10 | ||
| 11 | // send header to the other side | |
| 12 | pull.init(push.header) | |
| 13 | ||
| 14 | // send the cipher to the other side | |
| 15 | const cipher1 = push.next(Buffer.from('test')) | |
| 16 | ||
| 17 | // prints "test" | |
| 18 | console.log(pull.next(cipher1)) | |
| 19 | ||
| 20 | // send the cipher to the other side | |
| 21 | const cipher2 = push.next(Buffer.from('test 2')) | |
| 22 | ||
| 23 | // prints "test 2" | |
| 24 | console.log(pull.next(cipher2)) | |
| 25 | ||
| 26 | // when done send a final signal | |
| 27 | const cipher3 = push.final() | |
| 28 | ||
| 29 | // prints <empty buffer> | |
| 30 | console.log(pull.next(cipher3)) | |
| 31 | ||
| 32 | // but sets pull.final to true | |
| 33 | console.log(pull.final) |
| … | … | @@ -0,0 +1,70 @@ |
| 1 | const sodium = require('sodium-universal') | |
| 2 | const b4a = require('b4a') | |
| 3 | ||
| 4 | const ABYTES = sodium.crypto_secretstream_xchacha20poly1305_ABYTES | |
| 5 | const TAG_MESSAGE = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE | |
| 6 | const TAG_FINAL = sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL | |
| 7 | const STATEBYTES = sodium.crypto_secretstream_xchacha20poly1305_STATEBYTES | |
| 8 | const HEADERBYTES = sodium.crypto_secretstream_xchacha20poly1305_HEADERBYTES | |
| 9 | const KEYBYTES = sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES | |
| 10 | const TAG_FINAL_BYTE = b4a.isBuffer(TAG_FINAL) ? TAG_FINAL[0] : TAG_FINAL | |
| 11 | ||
| 12 | const EMPTY = b4a.alloc(0) | |
| 13 | const TAG = b4a.alloc(1) | |
| 14 | ||
| 15 | class Push { | |
| 16 | constructor (key, state = b4a.allocUnsafeSlow(STATEBYTES), header = b4a.allocUnsafeSlow(HEADERBYTES)) { | |
| 17 | if (!TAG_FINAL) throw new Error('JavaScript sodium version needs to support crypto_secretstream_xchacha20poly') | |
| 18 | ||
| 19 | this.key = key | |
| 20 | this.state = state | |
| 21 | this.header = header | |
| 22 | ||
| 23 | sodium.crypto_secretstream_xchacha20poly1305_init_push(this.state, this.header, this.key) | |
| 24 | } | |
| 25 | ||
| 26 | next (message, cipher = b4a.allocUnsafe(message.byteLength + ABYTES)) { | |
| 27 | sodium.crypto_secretstream_xchacha20poly1305_push(this.state, cipher, message, null, TAG_MESSAGE) | |
| 28 | return cipher | |
| 29 | } | |
| 30 | ||
| 31 | final (message = EMPTY, cipher = b4a.allocUnsafe(ABYTES)) { | |
| 32 | sodium.crypto_secretstream_xchacha20poly1305_push(this.state, cipher, message, null, TAG_FINAL) | |
| 33 | return cipher | |
| 34 | } | |
| 35 | } | |
| 36 | ||
| 37 | class Pull { | |
| 38 | constructor (key, state = b4a.allocUnsafeSlow(STATEBYTES)) { | |
| 39 | if (!TAG_FINAL) throw new Error('JavaScript sodium version needs to support crypto_secretstream_xchacha20poly') | |
| 40 | ||
| 41 | this.key = key | |
| 42 | this.state = state | |
| 43 | this.final = false | |
| 44 | } | |
| 45 | ||
| 46 | init (header) { | |
| 47 | sodium.crypto_secretstream_xchacha20poly1305_init_pull(this.state, header, this.key) | |
| 48 | } | |
| 49 | ||
| 50 | next (cipher, message = b4a.allocUnsafe(cipher.byteLength - ABYTES)) { | |
| 51 | sodium.crypto_secretstream_xchacha20poly1305_pull(this.state, message, TAG, cipher, null) | |
| 52 | this.final = TAG[0] === TAG_FINAL_BYTE | |
| 53 | return message | |
| 54 | } | |
| 55 | } | |
| 56 | ||
| 57 | function keygen (buf = b4a.alloc(KEYBYTES)) { | |
| 58 | sodium.crypto_secretstream_xchacha20poly1305_keygen(buf) | |
| 59 | return buf | |
| 60 | } | |
| 61 | ||
| 62 | module.exports = { | |
| 63 | keygen, | |
| 64 | KEYBYTES, | |
| 65 | ABYTES, | |
| 66 | STATEBYTES, | |
| 67 | HEADERBYTES, | |
| 68 | Push, | |
| 69 | Pull | |
| 70 | } |
| … | … | @@ -0,0 +1,279 @@ |
| 1 | cmake_minimum_required(VERSION 3.25) | |
| 2 | ||
| 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) | |
| 4 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) | |
| 5 | find_package(cmake-napi REQUIRED PATHS node_modules/cmake-napi) | |
| 6 | find_package(cmake-npm REQUIRED PATHS node_modules/cmake-npm) | |
| 7 | ||
| 8 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) | |
| 9 | ||
| 10 | project(sodium_native C ASM CXX) | |
| 11 | ||
| 12 | fetch_package("github:jedisct1/libsodium#e18eee6" SOURCE_DIR sodium) | |
| 13 | ||
| 14 | fetch_package("github:holepunchto/libjstl#098664c") | |
| 15 | ||
| 16 | bare_target(target) | |
| 17 | ||
| 18 | if(target MATCHES "win32") | |
| 19 | add_compile_options(/MT$<$<CONFIG:Debug>:d>) | |
| 20 | endif() | |
| 21 | ||
| 22 | file(COPY_FILE "${sodium}/builds/msvc/version.h" "${sodium}/src/libsodium/include/sodium/version.h") | |
| 23 | ||
| 24 | file(GLOB_RECURSE sodium_headers CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.h") | |
| 25 | file(GLOB_RECURSE sodium_sources CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.c") | |
| 26 | file(GLOB_RECURSE sodium_asm_sources CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.S") | |
| 27 | ||
| 28 | add_library(sodium OBJECT) | |
| 29 | ||
| 30 | target_sources( | |
| 31 | sodium | |
| 32 | INTERFACE | |
| 33 | ${sodium_headers} | |
| 34 | PRIVATE | |
| 35 | ${sodium_sources} | |
| 36 | ) | |
| 37 | ||
| 38 | target_include_directories( | |
| 39 | sodium | |
| 40 | INTERFACE | |
| 41 | "${sodium}/src/libsodium/include" | |
| 42 | PRIVATE | |
| 43 | "${sodium}/src/libsodium/include/sodium" | |
| 44 | ) | |
| 45 | ||
| 46 | if(NOT target MATCHES "win32") | |
| 47 | target_compile_options( | |
| 48 | sodium | |
| 49 | PRIVATE | |
| 50 | -fvisibility=hidden | |
| 51 | -fno-strict-aliasing | |
| 52 | -fwrapv | |
| 53 | -flax-vector-conversions | |
| 54 | ) | |
| 55 | endif() | |
| 56 | ||
| 57 | target_compile_definitions( | |
| 58 | sodium | |
| 59 | PUBLIC | |
| 60 | SODIUM_STATIC=1 | |
| 61 | PRIVATE | |
| 62 | _GNU_SOURCE=1 | |
| 63 | CONFIGURED=1 | |
| 64 | DEV_MODE=1 | |
| 65 | HAVE_ATOMIC_OPS=1 | |
| 66 | HAVE_C11_MEMORY_FENCES=1 | |
| 67 | HAVE_CET_H=1 | |
| 68 | HAVE_GCC_MEMORY_FENCES=1 | |
| 69 | HAVE_INLINE_ASM=1 | |
| 70 | HAVE_INTTYPES_H=1 | |
| 71 | HAVE_STDINT_H=1 | |
| 72 | HAVE_TI_MODE=1 | |
| 73 | ) | |
| 74 | ||
| 75 | if(target MATCHES "darwin|ios") | |
| 76 | target_compile_definitions( | |
| 77 | sodium | |
| 78 | PRIVATE | |
| 79 | ASM_HIDE_SYMBOL=.private_extern | |
| 80 | TLS=_Thread_local | |
| 81 | HAVE_ARC4RANDOM=1 | |
| 82 | HAVE_ARC4RANDOM_BUF=1 | |
| 83 | HAVE_CATCHABLE_ABRT=1 | |
| 84 | HAVE_CATCHABLE_SEGV=1 | |
| 85 | HAVE_CLOCK_GETTIME=1 | |
| 86 | HAVE_GETPID=1 | |
| 87 | HAVE_MADVISE=1 | |
| 88 | HAVE_MEMSET_S=1 | |
| 89 | HAVE_MLOCK=1 | |
| 90 | HAVE_MMAP=1 | |
| 91 | HAVE_MPROTECT=1 | |
| 92 | HAVE_NANOSLEEP=1 | |
| 93 | HAVE_POSIX_MEMALIGN=1 | |
| 94 | HAVE_PTHREAD=1 | |
| 95 | HAVE_PTHREAD_PRIO_INHERIT=1 | |
| 96 | HAVE_RAISE=1 | |
| 97 | HAVE_SYSCONF=1 | |
| 98 | HAVE_SYS_MMAN_H=1 | |
| 99 | HAVE_SYS_PARAM_H=1 | |
| 100 | HAVE_WEAK_SYMBOLS=1 | |
| 101 | ) | |
| 102 | ||
| 103 | if(NOT target MATCHES "ios") | |
| 104 | target_compile_definitions( | |
| 105 | sodium | |
| 106 | PRIVATE | |
| 107 | HAVE_GETENTROPY=1 | |
| 108 | HAVE_SYS_RANDOM_H=1 | |
| 109 | ) | |
| 110 | endif() | |
| 111 | endif() | |
| 112 | ||
| 113 | if(target MATCHES "linux") | |
| 114 | target_compile_definitions( | |
| 115 | sodium | |
| 116 | PRIVATE | |
| 117 | ASM_HIDE_SYMBOL=.hidden | |
| 118 | TLS=_Thread_local | |
| 119 | HAVE_CATCHABLE_ABRT=1 | |
| 120 | HAVE_CATCHABLE_SEGV=1 | |
| 121 | HAVE_CLOCK_GETTIME=1 | |
| 122 | HAVE_GETPID=1 | |
| 123 | HAVE_MADVISE=1 | |
| 124 | HAVE_MLOCK=1 | |
| 125 | HAVE_MMAP=1 | |
| 126 | HAVE_MPROTECT=1 | |
| 127 | HAVE_NANOSLEEP=1 | |
| 128 | HAVE_POSIX_MEMALIGN=1 | |
| 129 | HAVE_PTHREAD_PRIO_INHERIT=1 | |
| 130 | HAVE_PTHREAD=1 | |
| 131 | HAVE_RAISE=1 | |
| 132 | HAVE_SYSCONF=1 | |
| 133 | HAVE_SYS_AUXV_H=1 | |
| 134 | HAVE_SYS_MMAN_H=1 | |
| 135 | HAVE_SYS_PARAM_H=1 | |
| 136 | HAVE_SYS_RANDOM_H=1 | |
| 137 | HAVE_WEAK_SYMBOLS=1 | |
| 138 | ) | |
| 139 | endif() | |
| 140 | ||
| 141 | if(target MATCHES "win32") | |
| 142 | target_compile_definitions( | |
| 143 | sodium | |
| 144 | PRIVATE | |
| 145 | _CRT_SECURE_NO_WARNINGS=1 | |
| 146 | HAVE_RAISE=1 | |
| 147 | ) | |
| 148 | endif() | |
| 149 | ||
| 150 | if(target MATCHES "x64") | |
| 151 | target_compile_definitions( | |
| 152 | sodium | |
| 153 | PRIVATE | |
| 154 | HAVE_CPUID=1 | |
| 155 | HAVE_RDRAND=1 | |
| 156 | HAVE_EMMINTRIN_H=1 # SSE2 | |
| 157 | HAVE_PMMINTRIN_H=1 # SSE3 | |
| 158 | HAVE_TMMINTRIN_H=1 # SSSE3 | |
| 159 | HAVE_SMMINTRIN_H=1 # SSE4.1 | |
| 160 | HAVE_WMMINTRIN_H=1 # AES | |
| 161 | HAVE_AVXINTRIN_H=1 # AVX | |
| 162 | HAVE_AVX2INTRIN_H=1 # AVX2 | |
| 163 | HAVE_AVX512FINTRIN_H # AVX512F | |
| 164 | ) | |
| 165 | ||
| 166 | if(NOT target MATCHES "win32") | |
| 167 | target_compile_definitions( | |
| 168 | sodium | |
| 169 | PRIVATE | |
| 170 | HAVE_AMD64_ASM=1 | |
| 171 | HAVE_AVX_ASM=1 | |
| 172 | ) | |
| 173 | ||
| 174 | target_sources( | |
| 175 | sodium | |
| 176 | PRIVATE | |
| 177 | ${sodium_asm_sources} | |
| 178 | ) | |
| 179 | endif() | |
| 180 | endif() | |
| 181 | ||
| 182 | if(target MATCHES "arm64") | |
| 183 | target_compile_definitions( | |
| 184 | sodium | |
| 185 | PRIVATE | |
| 186 | HAVE_ARMCRYPTO=1 | |
| 187 | ) | |
| 188 | endif() | |
| 189 | ||
| 190 | if(CMAKE_C_BYTE_ORDER MATCHES "BIG_ENDIAN") | |
| 191 | target_compile_definitions( | |
| 192 | sodium | |
| 193 | PRIVATE | |
| 194 | NATIVE_BIG_ENDIAN=1 | |
| 195 | ) | |
| 196 | else() | |
| 197 | target_compile_definitions( | |
| 198 | sodium | |
| 199 | PRIVATE | |
| 200 | NATIVE_LITTLE_ENDIAN=1 | |
| 201 | ) | |
| 202 | endif() | |
| 203 | ||
| 204 | if(target MATCHES "linux|android") | |
| 205 | target_link_options( | |
| 206 | sodium | |
| 207 | PUBLIC | |
| 208 | -Wl,-z,noexecstack | |
| 209 | ) | |
| 210 | endif() | |
| 211 | ||
| 212 | add_library(sodium_extensions OBJECT) | |
| 213 | ||
| 214 | target_sources( | |
| 215 | sodium_extensions | |
| 216 | PRIVATE | |
| 217 | extensions/tweak/tweak.c | |
| 218 | extensions/tweak/tweak.h | |
| 219 | extensions/pbkdf2/pbkdf2.c | |
| 220 | extensions/pbkdf2/pbkdf2.h | |
| 221 | ) | |
| 222 | ||
| 223 | target_link_libraries( | |
| 224 | sodium_extensions | |
| 225 | PUBLIC | |
| 226 | sodium | |
| 227 | ) | |
| 228 | ||
| 229 | add_bare_module(sodium_native_bare) | |
| 230 | ||
| 231 | target_sources( | |
| 232 | ${sodium_native_bare} | |
| 233 | PRIVATE | |
| 234 | binding.cc | |
| 235 | ) | |
| 236 | ||
| 237 | target_link_libraries( | |
| 238 | ${sodium_native_bare} | |
| 239 | PRIVATE | |
| 240 | $<TARGET_OBJECTS:sodium> | |
| 241 | $<TARGET_OBJECTS:sodium_extensions> | |
| 242 | jstl | |
| 243 | PUBLIC | |
| 244 | sodium | |
| 245 | sodium_extensions | |
| 246 | ) | |
| 247 | ||
| 248 | add_napi_module(sodium_native_node) | |
| 249 | ||
| 250 | target_sources( | |
| 251 | ${sodium_native_node} | |
| 252 | PRIVATE | |
| 253 | binding.cc | |
| 254 | ) | |
| 255 | ||
| 256 | target_compile_definitions( | |
| 257 | ${sodium_native_node} | |
| 258 | PRIVATE | |
| 259 | NAPI_VERSION=9 | |
| 260 | ) | |
| 261 | ||
| 262 | target_link_libraries( | |
| 263 | ${sodium_native_node} | |
| 264 | PRIVATE | |
| 265 | $<TARGET_OBJECTS:sodium> | |
| 266 | $<TARGET_OBJECTS:sodium_extensions> | |
| 267 | jstl | |
| 268 | PUBLIC | |
| 269 | sodium | |
| 270 | sodium_extensions | |
| 271 | ) | |
| 272 | ||
| 273 | resolve_node_module(bare-compat-napi compat) | |
| 274 | ||
| 275 | target_include_directories( | |
| 276 | ${sodium_native_node} | |
| 277 | PRIVATE | |
| 278 | "${compat}/include" | |
| 279 | ) |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2016 Mathias Buus and Emil Bay | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,51 @@ |
| 1 | # sodium-native | |
| 2 | ||
| 3 | Low level bindings for [libsodium](https://github.com/jedisct1/libsodium). | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install sodium-native | |
| 7 | ``` | |
| 8 | ||
| 9 | The goal of this project is to be thin, stable, unopionated wrapper around libsodium. | |
| 10 | ||
| 11 | All methods exposed are more or less a direct translation of the libsodium c-api. | |
| 12 | This means that most data types are buffers and you have to manage allocating return values and passing them in as arguments instead of receiving them as return values. | |
| 13 | ||
| 14 | This makes this API harder to use than other libsodium wrappers out there, but also means that you'll be able to get a lot of perf / memory improvements as you can do stuff like inline encryption / decryption, reuse buffers etc. | |
| 15 | ||
| 16 | This also makes this library useful as a foundation for more high level crypto abstractions that you want to make. | |
| 17 | ||
| 18 | ## Usage | |
| 19 | ||
| 20 | ```js | |
| 21 | var sodium = require('sodium-native') | |
| 22 | ||
| 23 | var nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES) | |
| 24 | var key = sodium.sodium_malloc(sodium.crypto_secretbox_KEYBYTES) // secure buffer | |
| 25 | var message = Buffer.from('Hello, World!') | |
| 26 | var ciphertext = Buffer.alloc(message.length + sodium.crypto_secretbox_MACBYTES) | |
| 27 | ||
| 28 | sodium.randombytes_buf(nonce) // insert random data into nonce | |
| 29 | sodium.randombytes_buf(key) // insert random data into key | |
| 30 | ||
| 31 | // encrypted message is stored in ciphertext. | |
| 32 | sodium.crypto_secretbox_easy(ciphertext, message, nonce, key) | |
| 33 | ||
| 34 | console.log('Encrypted message:', ciphertext) | |
| 35 | ||
| 36 | var plainText = Buffer.alloc(ciphertext.length - sodium.crypto_secretbox_MACBYTES) | |
| 37 | ||
| 38 | if (!sodium.crypto_secretbox_open_easy(plainText, ciphertext, nonce, key)) { | |
| 39 | console.log('Decryption failed!') | |
| 40 | } else { | |
| 41 | console.log('Decrypted message:', plainText, '(' + plainText.toString() + ')') | |
| 42 | } | |
| 43 | ``` | |
| 44 | ||
| 45 | ## Documentation | |
| 46 | ||
| 47 | Complete documentation may be found on the [sodium-friends website](https://sodium-friends.github.io/docs/docs/getstarted) | |
| 48 | ||
| 49 | ## License | |
| 50 | ||
| 51 | MIT |
| … | … | @@ -0,0 +1,4094 @@ |
| 1 | #include <assert.h> | |
| 2 | #include <bare.h> | |
| 3 | #include <js.h> | |
| 4 | #include <jstl.h> | |
| 5 | #include <sodium.h> | |
| 6 | #include <stdbool.h> | |
| 7 | #include <stdint.h> | |
| 8 | #include <string.h> | |
| 9 | #include <uv.h> | |
| 10 | ||
| 11 | #include "extensions/pbkdf2/pbkdf2.h" | |
| 12 | #include "extensions/tweak/tweak.h" | |
| 13 | ||
| 14 | #define assert_bounds(arraybuffer) \ | |
| 15 | assert(arraybuffer##_offset + arraybuffer##_len <= arraybuffer.size()) | |
| 16 | ||
| 17 | static inline void | |
| 18 | sn_sodium_memzero( | |
| 19 | js_env_t *, | |
| 20 | js_receiver_t, | |
| 21 | js_typedarray_span_t<> buf | |
| 22 | ) { | |
| 23 | sodium_memzero(buf.data(), buf.size_bytes()); | |
| 24 | } | |
| 25 | ||
| 26 | static inline int | |
| 27 | sn_sodium_mlock( | |
| 28 | js_env_t *, | |
| 29 | js_receiver_t, | |
| 30 | js_typedarray_span_t<> buf | |
| 31 | ) { | |
| 32 | return sodium_mlock(buf.data(), buf.size_bytes()); | |
| 33 | } | |
| 34 | ||
| 35 | static inline int | |
| 36 | sn_sodium_munlock( | |
| 37 | js_env_t *, | |
| 38 | js_receiver_t, | |
| 39 | js_typedarray_span_t<> buf | |
| 40 | ) { | |
| 41 | return sodium_munlock(buf.data(), buf.size_bytes()); | |
| 42 | } | |
| 43 | ||
| 44 | static void | |
| 45 | sn_external_arraybuffer_finalize(js_env_t *env, uint8_t *ptr) { | |
| 46 | sodium_free(ptr); | |
| 47 | } | |
| 48 | ||
| 49 | static js_arraybuffer_t | |
| 50 | sn_sodium_malloc(js_env_t *env, js_receiver_t, uint32_t len) { | |
| 51 | int err; | |
| 52 | ||
| 53 | auto ptr = reinterpret_cast<uint8_t *>(sodium_malloc(len)); | |
| 54 | ||
| 55 | js_arraybuffer_t buffer; | |
| 56 | ||
| 57 | err = js_create_external_arraybuffer<sn_external_arraybuffer_finalize>(env, ptr, len, buffer); | |
| 58 | assert(err == 0); | |
| 59 | ||
| 60 | return buffer; | |
| 61 | } | |
| 62 | ||
| 63 | static void | |
| 64 | sn_sodium_free(js_env_t *env, js_receiver_t, js_arraybuffer_t buf) { | |
| 65 | int err; | |
| 66 | ||
| 67 | std::span<uint8_t> view; | |
| 68 | err = js_get_arraybuffer_info(env, buf, view); | |
| 69 | assert(err == 0); | |
| 70 | ||
| 71 | if (view.empty()) return; | |
| 72 | ||
| 73 | err = js_detach_arraybuffer(env, buf); | |
| 74 | assert(err == 0); | |
| 75 | } | |
| 76 | ||
| 77 | static inline int | |
| 78 | sn_sodium_mprotect_noaccess(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 79 | return sodium_mprotect_noaccess(buf.data()); | |
| 80 | } | |
| 81 | ||
| 82 | static inline int | |
| 83 | sn_sodium_mprotect_readonly(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 84 | return sodium_mprotect_readonly(buf.data()); | |
| 85 | } | |
| 86 | ||
| 87 | static inline int | |
| 88 | sn_sodium_mprotect_readwrite(js_env_t *env, js_receiver_t, js_arraybuffer_span_t buf) { | |
| 89 | return sodium_mprotect_readwrite(buf.data()); | |
| 90 | } | |
| 91 | ||
| 92 | static inline uint32_t // TODO: test envless | |
| 93 | sn_randombytes_random(js_env_t *env, js_receiver_t) { | |
| 94 | return randombytes_random(); | |
| 95 | } | |
| 96 | ||
| 97 | static inline uint32_t // TODO: test envless | |
| 98 | sn_randombytes_uniform(js_env_t *env, js_receiver_t, uint32_t upper_bound) { | |
| 99 | return randombytes_uniform(upper_bound); | |
| 100 | } | |
| 101 | ||
| 102 | static inline void | |
| 103 | sn_randombytes_buf( | |
| 104 | js_env_t *env, | |
| 105 | js_receiver_t, | |
| 106 | js_arraybuffer_span_t buf, | |
| 107 | uint32_t buf_offset, | |
| 108 | uint32_t buf_len | |
| 109 | ) { | |
| 110 | assert_bounds(buf); | |
| 111 | ||
| 112 | randombytes_buf(&buf[buf_offset], buf_len); | |
| 113 | } | |
| 114 | ||
| 115 | static inline void | |
| 116 | sn_randombytes_buf_deterministic( | |
| 117 | js_env_t *env, | |
| 118 | js_receiver_t, | |
| 119 | ||
| 120 | js_arraybuffer_span_t buf, | |
| 121 | uint32_t buf_offset, | |
| 122 | uint32_t buf_len, | |
| 123 | ||
| 124 | js_arraybuffer_span_t seed, | |
| 125 | uint32_t seed_offset, | |
| 126 | uint32_t seed_len | |
| 127 | ) { | |
| 128 | assert_bounds(buf); | |
| 129 | assert_bounds(seed); | |
| 130 | ||
| 131 | assert(seed_len == randombytes_SEEDBYTES); | |
| 132 | ||
| 133 | randombytes_buf_deterministic(&buf[buf_offset], buf_len, &seed[seed_offset]); | |
| 134 | } | |
| 135 | ||
| 136 | static inline bool | |
| 137 | sn_sodium_memcmp(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 138 | if (a.size_bytes() != b.size_bytes()) return false; | |
| 139 | ||
| 140 | return sodium_memcmp(a.data(), b.data(), a.size_bytes()) == 0; | |
| 141 | } | |
| 142 | ||
| 143 | static inline void | |
| 144 | sn_sodium_increment(js_env_t *, js_receiver_t, js_typedarray_span_t<> n) { | |
| 145 | sodium_increment(n.data(), n.size_bytes()); | |
| 146 | } | |
| 147 | ||
| 148 | static inline void | |
| 149 | sn_sodium_add(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 150 | sodium_add(a.data(), b.data(), a.size_bytes()); | |
| 151 | } | |
| 152 | ||
| 153 | static inline void | |
| 154 | sn_sodium_sub(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 155 | sodium_sub(a.data(), b.data(), a.size_bytes()); | |
| 156 | } | |
| 157 | ||
| 158 | static inline int32_t | |
| 159 | sn_sodium_compare(js_env_t *, js_receiver_t, js_typedarray_span_t<> a, js_typedarray_span_t<> b) { | |
| 160 | return sodium_compare(a.data(), b.data(), a.size_bytes()); | |
| 161 | } | |
| 162 | ||
| 163 | static inline bool | |
| 164 | sn_sodium_is_zero(js_env_t *, js_receiver_t, js_typedarray_span_t<> buffer, uint32_t len) { | |
| 165 | assert(len <= buffer.size_bytes()); | |
| 166 | ||
| 167 | return sodium_is_zero(buffer.data(), len) != 0; | |
| 168 | } | |
| 169 | ||
| 170 | static inline uint32_t | |
| 171 | sn_sodium_pad(js_env_t *, js_receiver_t, js_typedarray_span_t<> buf, uint32_t unpadded_buflen, uint32_t blocksize) { | |
| 172 | size_t padded_buflen; | |
| 173 | ||
| 174 | sodium_pad(&padded_buflen, buf.data(), unpadded_buflen, blocksize, buf.size_bytes()); | |
| 175 | ||
| 176 | return padded_buflen; | |
| 177 | } | |
| 178 | ||
| 179 | static inline uint32_t | |
| 180 | sn_sodium_unpad(js_env_t *, js_receiver_t, js_typedarray_span_t<> buf, uint32_t padded_buflen, uint32_t blocksize) { | |
| 181 | size_t unpadded_buflen; | |
| 182 | ||
| 183 | sodium_unpad(&unpadded_buflen, buf.data(), padded_buflen, blocksize); | |
| 184 | ||
| 185 | return unpadded_buflen; | |
| 186 | } | |
| 187 | ||
| 188 | static inline int | |
| 189 | sn_crypto_sign_keypair(js_env_t *, js_receiver_t, js_typedarray_span_t<> pk, js_typedarray_span_t<> sk) { | |
| 190 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 191 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 192 | ||
| 193 | return crypto_sign_keypair(pk.data(), sk.data()); | |
| 194 | } | |
| 195 | ||
| 196 | static inline int | |
| 197 | sn_crypto_sign_seed_keypair( | |
| 198 | js_env_t *, | |
| 199 | js_receiver_t, | |
| 200 | js_typedarray_span_t<> pk, | |
| 201 | js_typedarray_span_t<> sk, | |
| 202 | js_typedarray_span_t<> seed | |
| 203 | ) { | |
| 204 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 205 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 206 | assert(seed.size_bytes() == crypto_sign_SEEDBYTES); | |
| 207 | ||
| 208 | return crypto_sign_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 209 | } | |
| 210 | ||
| 211 | static inline int | |
| 212 | sn_crypto_sign( | |
| 213 | js_env_t *, | |
| 214 | js_receiver_t, | |
| 215 | js_typedarray_span_t<> sm, | |
| 216 | js_typedarray_span_t<> m, | |
| 217 | js_typedarray_span_t<> sk | |
| 218 | ) { | |
| 219 | assert(sm.size_bytes() == crypto_sign_BYTES + m.size_bytes()); | |
| 220 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 221 | ||
| 222 | return crypto_sign(sm.data(), NULL, m.data(), m.size_bytes(), sk.data()); | |
| 223 | } | |
| 224 | ||
| 225 | static inline bool | |
| 226 | sn_crypto_sign_open( | |
| 227 | js_env_t *, | |
| 228 | js_receiver_t, | |
| 229 | js_typedarray_span_t<> m, | |
| 230 | js_typedarray_span_t<> sm, | |
| 231 | js_typedarray_span_t<> pk | |
| 232 | ) { | |
| 233 | assert(m.size_bytes() == sm.size_bytes() - crypto_sign_BYTES); | |
| 234 | assert(sm.size_bytes() >= crypto_sign_BYTES); | |
| 235 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 236 | ||
| 237 | return crypto_sign_open(m.data(), NULL, sm.data(), sm.size_bytes(), pk.data()) == 0; | |
| 238 | } | |
| 239 | ||
| 240 | static inline int | |
| 241 | sn_crypto_sign_detached( | |
| 242 | js_env_t *env, | |
| 243 | js_receiver_t, | |
| 244 | js_typedarray_span_t<> sig, | |
| 245 | js_typedarray_span_t<> m, | |
| 246 | js_typedarray_span_t<> sk | |
| 247 | ) { | |
| 248 | assert(sig.size_bytes() == crypto_sign_BYTES); | |
| 249 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 250 | ||
| 251 | return crypto_sign_detached(sig.data(), NULL, m.data(), m.size_bytes(), sk.data()); | |
| 252 | } | |
| 253 | ||
| 254 | static inline bool | |
| 255 | sn_crypto_sign_verify_detached( | |
| 256 | js_env_t *env, | |
| 257 | js_receiver_t, | |
| 258 | ||
| 259 | js_arraybuffer_span_t sig, | |
| 260 | uint32_t sig_offset, | |
| 261 | uint32_t sig_len, | |
| 262 | ||
| 263 | js_arraybuffer_span_t m, | |
| 264 | uint32_t m_offset, | |
| 265 | uint32_t m_len, | |
| 266 | ||
| 267 | js_arraybuffer_span_t pk, | |
| 268 | uint32_t pk_offset, | |
| 269 | uint32_t pk_len | |
| 270 | ) { | |
| 271 | assert_bounds(sig); | |
| 272 | assert_bounds(m); | |
| 273 | assert_bounds(pk); | |
| 274 | ||
| 275 | assert(sig_len >= crypto_sign_BYTES); | |
| 276 | assert(pk_len == crypto_sign_PUBLICKEYBYTES); | |
| 277 | ||
| 278 | return crypto_sign_verify_detached(&sig[sig_offset], &m[m_offset], m_len, &pk[pk_offset]) == 0; | |
| 279 | } | |
| 280 | ||
| 281 | static inline int | |
| 282 | sn_crypto_sign_ed25519_sk_to_pk( | |
| 283 | js_env_t *, | |
| 284 | js_receiver_t, | |
| 285 | js_typedarray_span_t<> pk, | |
| 286 | js_typedarray_span_t<> sk | |
| 287 | ) { | |
| 288 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 289 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 290 | ||
| 291 | return crypto_sign_ed25519_sk_to_pk(pk.data(), sk.data()); | |
| 292 | } | |
| 293 | ||
| 294 | static inline int | |
| 295 | sn_crypto_sign_ed25519_pk_to_curve25519( | |
| 296 | js_env_t *, | |
| 297 | js_receiver_t, | |
| 298 | js_typedarray_span_t<> x25519_pk, | |
| 299 | js_typedarray_span_t<> ed25519_pk | |
| 300 | ) { | |
| 301 | assert(x25519_pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 302 | assert(ed25519_pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 303 | ||
| 304 | return crypto_sign_ed25519_pk_to_curve25519(x25519_pk.data(), ed25519_pk.data()); | |
| 305 | } | |
| 306 | ||
| 307 | static inline int | |
| 308 | sn_crypto_sign_ed25519_sk_to_curve25519( | |
| 309 | js_env_t *, | |
| 310 | js_receiver_t, | |
| 311 | js_typedarray_span_t<> x25519_sk, | |
| 312 | js_typedarray_span_t<> ed25519_sk | |
| 313 | ) { | |
| 314 | assert(x25519_sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 315 | assert( | |
| 316 | ed25519_sk.size_bytes() == crypto_sign_SECRETKEYBYTES || | |
| 317 | ed25519_sk.size_bytes() == crypto_box_SECRETKEYBYTES | |
| 318 | ); | |
| 319 | ||
| 320 | return crypto_sign_ed25519_sk_to_curve25519(x25519_sk.data(), ed25519_sk.data()); | |
| 321 | } | |
| 322 | ||
| 323 | static inline int | |
| 324 | sn_crypto_generichash( | |
| 325 | js_env_t *env, | |
| 326 | js_receiver_t, | |
| 327 | ||
| 328 | js_arraybuffer_span_t out, | |
| 329 | uint32_t out_offset, | |
| 330 | uint32_t out_len, | |
| 331 | ||
| 332 | js_arraybuffer_span_t in, | |
| 333 | uint32_t in_offset, | |
| 334 | uint32_t in_len, | |
| 335 | ||
| 336 | js_arraybuffer_span_t key, | |
| 337 | uint32_t key_offset, | |
| 338 | uint32_t key_len | |
| 339 | ) { | |
| 340 | assert_bounds(out); | |
| 341 | assert( | |
| 342 | out_len >= crypto_generichash_BYTES_MIN && | |
| 343 | out_len <= crypto_generichash_BYTES_MAX | |
| 344 | ); | |
| 345 | ||
| 346 | assert_bounds(in); | |
| 347 | ||
| 348 | uint8_t *key_data = NULL; | |
| 349 | if (key_len) { | |
| 350 | assert_bounds(key); | |
| 351 | assert( | |
| 352 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 353 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 354 | ); | |
| 355 | key_data = &key[key_offset]; | |
| 356 | } | |
| 357 | ||
| 358 | return crypto_generichash(&out[out_offset], out_len, &in[in_offset], in_len, key_data, key_len); | |
| 359 | } | |
| 360 | ||
| 361 | static inline int | |
| 362 | sn_crypto_generichash_batch( | |
| 363 | js_env_t *env, | |
| 364 | js_receiver_t, | |
| 365 | js_typedarray_t<uint8_t> out, | |
| 366 | std::vector<js_typedarray_span_t<>> batch, | |
| 367 | bool use_key, | |
| 368 | js_typedarray_t<uint8_t> key | |
| 369 | ) { | |
| 370 | int err; | |
| 371 | ||
| 372 | uint8_t *out_data; | |
| 373 | size_t out_len; | |
| 374 | err = js_get_typedarray_info(env, out, out_data, out_len); | |
| 375 | assert(err == 0); | |
| 376 | assert( | |
| 377 | out_len >= crypto_generichash_BYTES_MIN && | |
| 378 | out_len <= crypto_generichash_BYTES_MAX | |
| 379 | ); | |
| 380 | ||
| 381 | uint8_t *key_data = NULL; | |
| 382 | size_t key_len = 0; | |
| 383 | if (use_key) { | |
| 384 | err = js_get_typedarray_info(env, key, key_data, key_len); | |
| 385 | assert(err == 0); | |
| 386 | assert( | |
| 387 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 388 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 389 | ); | |
| 390 | } | |
| 391 | ||
| 392 | crypto_generichash_state state; | |
| 393 | err = crypto_generichash_init(&state, key_data, key_len, out_len); | |
| 394 | if (err != 0) return err; | |
| 395 | ||
| 396 | for (auto &buf : batch) { | |
| 397 | err = crypto_generichash_update(&state, buf.data(), buf.size()); | |
| 398 | if (err != 0) return err; | |
| 399 | } | |
| 400 | ||
| 401 | return crypto_generichash_final(&state, out_data, out_len); | |
| 402 | } | |
| 403 | ||
| 404 | static inline void | |
| 405 | sn_crypto_generichash_keygen( | |
| 406 | js_env_t *env, | |
| 407 | js_receiver_t, | |
| 408 | ||
| 409 | js_arraybuffer_span_t key, | |
| 410 | uint32_t key_offset, | |
| 411 | uint32_t key_len | |
| 412 | ) { | |
| 413 | assert_bounds(key); | |
| 414 | assert(key_len == crypto_generichash_KEYBYTES); | |
| 415 | ||
| 416 | crypto_generichash_keygen(&key[key_offset]); | |
| 417 | } | |
| 418 | ||
| 419 | static inline int | |
| 420 | sn_crypto_generichash_init( | |
| 421 | js_env_t *env, | |
| 422 | js_receiver_t, | |
| 423 | ||
| 424 | js_arraybuffer_span_t state, | |
| 425 | uint32_t state_offset, | |
| 426 | uint32_t state_len, | |
| 427 | ||
| 428 | js_arraybuffer_span_t key, | |
| 429 | uint32_t key_offset, | |
| 430 | uint32_t key_len, | |
| 431 | ||
| 432 | uint32_t out_len | |
| 433 | ) { | |
| 434 | assert_bounds(state); | |
| 435 | ||
| 436 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 437 | ||
| 438 | uint8_t *key_data = NULL; | |
| 439 | if (key_len) { | |
| 440 | assert_bounds(key); | |
| 441 | assert( | |
| 442 | key_len >= crypto_generichash_KEYBYTES_MIN && | |
| 443 | key_len <= crypto_generichash_KEYBYTES_MAX | |
| 444 | ); | |
| 445 | key_data = &key[key_offset]; | |
| 446 | } | |
| 447 | ||
| 448 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 449 | ||
| 450 | return crypto_generichash_init(state_data, key_data, key_len, out_len); | |
| 451 | } | |
| 452 | ||
| 453 | static inline int | |
| 454 | sn_crypto_generichash_update( | |
| 455 | js_env_t *env, | |
| 456 | js_receiver_t, | |
| 457 | ||
| 458 | js_arraybuffer_span_t state, | |
| 459 | uint32_t state_offset, | |
| 460 | uint32_t state_len, | |
| 461 | ||
| 462 | js_arraybuffer_span_t in, | |
| 463 | uint32_t in_offset, | |
| 464 | uint32_t in_len | |
| 465 | ) { | |
| 466 | assert_bounds(state); | |
| 467 | assert_bounds(in); | |
| 468 | ||
| 469 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 470 | ||
| 471 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 472 | ||
| 473 | return crypto_generichash_update(state_data, &in[in_offset], in_len); | |
| 474 | } | |
| 475 | ||
| 476 | static inline int | |
| 477 | sn_crypto_generichash_final( | |
| 478 | js_env_t *env, | |
| 479 | js_receiver_t, | |
| 480 | ||
| 481 | js_arraybuffer_span_t state, | |
| 482 | uint32_t state_offset, | |
| 483 | uint32_t state_len, | |
| 484 | ||
| 485 | js_arraybuffer_span_t out, | |
| 486 | uint32_t out_offset, | |
| 487 | uint32_t out_len | |
| 488 | ) { | |
| 489 | assert_bounds(state); | |
| 490 | assert_bounds(out); | |
| 491 | ||
| 492 | assert(state_len == sizeof(crypto_generichash_state)); | |
| 493 | ||
| 494 | auto state_data = reinterpret_cast<crypto_generichash_state *>(&state[state_offset]); | |
| 495 | ||
| 496 | return crypto_generichash_final(state_data, &out[out_offset], out_len); | |
| 497 | } | |
| 498 | ||
| 499 | static inline int | |
| 500 | sn_crypto_box_keypair(js_env_t *, js_receiver_t, js_typedarray_span_t<> pk, js_typedarray_span_t<> sk) { | |
| 501 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 502 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 503 | ||
| 504 | return crypto_box_keypair(pk.data(), sk.data()); | |
| 505 | } | |
| 506 | ||
| 507 | static inline int | |
| 508 | sn_crypto_box_seed_keypair( | |
| 509 | js_env_t *, | |
| 510 | js_receiver_t, | |
| 511 | js_typedarray_span_t<> pk, | |
| 512 | js_typedarray_span_t<> sk, | |
| 513 | js_typedarray_span_t<> seed | |
| 514 | ) { | |
| 515 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 516 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 517 | assert(seed.size_bytes() == crypto_box_SEEDBYTES); | |
| 518 | ||
| 519 | return crypto_box_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 520 | } | |
| 521 | ||
| 522 | static inline int | |
| 523 | sn_crypto_box_easy( | |
| 524 | js_env_t *, | |
| 525 | js_receiver_t, | |
| 526 | js_typedarray_span_t<> c, | |
| 527 | js_typedarray_span_t<> m, | |
| 528 | js_typedarray_span_t<> n, | |
| 529 | js_typedarray_span_t<> pk, | |
| 530 | js_typedarray_span_t<> sk | |
| 531 | ) { | |
| 532 | assert(c.size_bytes() == m.size_bytes() + crypto_box_MACBYTES); | |
| 533 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 534 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 535 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 536 | ||
| 537 | return crypto_box_easy(c.data(), m.data(), m.size_bytes(), n.data(), pk.data(), sk.data()); | |
| 538 | } | |
| 539 | ||
| 540 | static inline bool | |
| 541 | sn_crypto_box_open_easy( | |
| 542 | js_env_t *, | |
| 543 | js_receiver_t, | |
| 544 | js_typedarray_span_t<> m, | |
| 545 | js_typedarray_span_t<> c, | |
| 546 | js_typedarray_span_t<> n, | |
| 547 | js_typedarray_span_t<> pk, | |
| 548 | js_typedarray_span_t<> sk | |
| 549 | ) { | |
| 550 | assert(c.size_bytes() >= crypto_box_MACBYTES); | |
| 551 | assert(m.size_bytes() == c.size_bytes() - crypto_box_MACBYTES); | |
| 552 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 553 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 554 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 555 | ||
| 556 | return crypto_box_open_easy(m.data(), c.data(), c.size_bytes(), n.data(), pk.data(), sk.data()) == 0; | |
| 557 | } | |
| 558 | ||
| 559 | static inline int | |
| 560 | sn_crypto_box_detached( | |
| 561 | js_env_t *, | |
| 562 | js_receiver_t, | |
| 563 | js_typedarray_span_t<> c, | |
| 564 | js_typedarray_span_t<> mac, | |
| 565 | js_typedarray_span_t<> m, | |
| 566 | js_typedarray_span_t<> n, | |
| 567 | js_typedarray_span_t<> pk, | |
| 568 | js_typedarray_span_t<> sk | |
| 569 | ) { | |
| 570 | assert(c.size_bytes() == m.size_bytes()); | |
| 571 | assert(mac.size_bytes() == crypto_box_MACBYTES); | |
| 572 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 573 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 574 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 575 | ||
| 576 | return crypto_box_detached(c.data(), mac.data(), m.data(), m.size_bytes(), n.data(), pk.data(), sk.data()); | |
| 577 | } | |
| 578 | ||
| 579 | static inline bool | |
| 580 | sn_crypto_box_open_detached( | |
| 581 | js_env_t *, | |
| 582 | js_receiver_t, | |
| 583 | js_typedarray_span_t<> m, | |
| 584 | js_typedarray_span_t<> c, | |
| 585 | js_typedarray_span_t<> mac, | |
| 586 | js_typedarray_span_t<> n, | |
| 587 | js_typedarray_span_t<> pk, | |
| 588 | js_typedarray_span_t<> sk | |
| 589 | ) { | |
| 590 | assert(m.size_bytes() == c.size_bytes()); | |
| 591 | assert(mac.size_bytes() == crypto_box_MACBYTES); | |
| 592 | assert(n.size_bytes() == crypto_box_NONCEBYTES); | |
| 593 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 594 | assert(sk.size_bytes() == crypto_box_SECRETKEYBYTES); | |
| 595 | ||
| 596 | return crypto_box_open_detached(m.data(), c.data(), mac.data(), c.size_bytes(), n.data(), pk.data(), sk.data()) == 0; | |
| 597 | } | |
| 598 | ||
| 599 | static inline int | |
| 600 | sn_crypto_box_seal( | |
| 601 | js_env_t *, | |
| 602 | js_receiver_t, | |
| 603 | js_typedarray_span_t<> c, | |
| 604 | js_typedarray_span_t<> m, | |
| 605 | js_typedarray_span_t<> pk | |
| 606 | ) { | |
| 607 | assert(c.size_bytes() == m.size_bytes() + crypto_box_SEALBYTES); | |
| 608 | assert(pk.size_bytes() == crypto_box_PUBLICKEYBYTES); | |
| 609 | ||
| 610 | return crypto_box_seal(c.data(), m.data(), m.size_bytes(), pk.data()); | |
| 611 | } | |
| 612 | ||
| 613 | static inline bool | |
| 614 | sn_crypto_box_seal_open( | |
| 615 | js_env_t *env, | |
| 616 | js_receiver_t, | |
| 617 | ||
| 618 | js_arraybuffer_span_t m, | |
| 619 | uint32_t m_offset, | |
| 620 | uint32_t m_len, | |
| 621 | ||
| 622 | js_arraybuffer_span_t c, | |
| 623 | uint32_t c_offset, | |
| 624 | uint32_t c_len, | |
| 625 | ||
| 626 | js_arraybuffer_span_t pk, | |
| 627 | uint32_t pk_offset, | |
| 628 | uint32_t pk_len, | |
| 629 | ||
| 630 | js_arraybuffer_span_t sk, | |
| 631 | uint32_t sk_offset, | |
| 632 | uint32_t sk_len | |
| 633 | ) { | |
| 634 | assert_bounds(m); | |
| 635 | assert_bounds(c); | |
| 636 | assert_bounds(pk); | |
| 637 | assert_bounds(sk); | |
| 638 | ||
| 639 | assert(m_len == c_len - crypto_box_SEALBYTES); | |
| 640 | assert(c_len >= crypto_box_SEALBYTES); | |
| 641 | assert(sk_len == crypto_box_SECRETKEYBYTES); | |
| 642 | assert(pk_len == crypto_box_PUBLICKEYBYTES); | |
| 643 | ||
| 644 | return crypto_box_seal_open(&m[m_offset], &c[c_offset], c_len, &pk[pk_offset], &sk[sk_offset]) == 0; | |
| 645 | } | |
| 646 | ||
| 647 | static inline int | |
| 648 | sn_crypto_secretbox_easy( | |
| 649 | js_env_t *, | |
| 650 | js_receiver_t, | |
| 651 | js_typedarray_span_t<> c, | |
| 652 | js_typedarray_span_t<> m, | |
| 653 | js_typedarray_span_t<> n, | |
| 654 | js_typedarray_span_t<> k | |
| 655 | ) { | |
| 656 | assert(c.size_bytes() == m.size_bytes() + crypto_secretbox_MACBYTES); | |
| 657 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 658 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 659 | ||
| 660 | return crypto_secretbox_easy(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 661 | } | |
| 662 | ||
| 663 | static inline bool | |
| 664 | sn_crypto_secretbox_open_easy( | |
| 665 | js_env_t *, | |
| 666 | js_receiver_t, | |
| 667 | js_typedarray_span_t<> m, | |
| 668 | js_typedarray_span_t<> c, | |
| 669 | js_typedarray_span_t<> n, | |
| 670 | js_typedarray_span_t<> k | |
| 671 | ) { | |
| 672 | assert(m.size_bytes() == c.size_bytes() - crypto_secretbox_MACBYTES); | |
| 673 | assert(c.size_bytes() >= crypto_secretbox_MACBYTES); | |
| 674 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 675 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 676 | ||
| 677 | return crypto_secretbox_open_easy(m.data(), c.data(), c.size_bytes(), n.data(), k.data()) == 0; | |
| 678 | } | |
| 679 | ||
| 680 | static inline int | |
| 681 | sn_crypto_secretbox_detached( | |
| 682 | js_env_t *, | |
| 683 | js_receiver_t, | |
| 684 | js_typedarray_span_t<> c, | |
| 685 | js_typedarray_span_t<> mac, | |
| 686 | js_typedarray_span_t<> m, | |
| 687 | js_typedarray_span_t<> n, | |
| 688 | js_typedarray_span_t<> k | |
| 689 | ) { | |
| 690 | assert(c.size_bytes() == m.size_bytes()); | |
| 691 | assert(mac.size_bytes() == crypto_secretbox_MACBYTES); | |
| 692 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 693 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 694 | ||
| 695 | return crypto_secretbox_detached(c.data(), mac.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 696 | } | |
| 697 | ||
| 698 | static inline bool | |
| 699 | sn_crypto_secretbox_open_detached( | |
| 700 | js_env_t *, | |
| 701 | js_receiver_t, | |
| 702 | js_typedarray_span_t<> m, | |
| 703 | js_typedarray_span_t<> c, | |
| 704 | js_typedarray_span_t<> mac, | |
| 705 | js_typedarray_span_t<> n, | |
| 706 | js_typedarray_span_t<> k | |
| 707 | ) { | |
| 708 | assert(m.size_bytes() == c.size_bytes()); | |
| 709 | assert(mac.size_bytes() == crypto_secretbox_MACBYTES); | |
| 710 | assert(n.size_bytes() == crypto_secretbox_NONCEBYTES); | |
| 711 | assert(k.size_bytes() == crypto_secretbox_KEYBYTES); | |
| 712 | ||
| 713 | return crypto_secretbox_open_detached(m.data(), c.data(), mac.data(), c.size_bytes(), n.data(), k.data()) == 0; | |
| 714 | } | |
| 715 | ||
| 716 | static inline int | |
| 717 | sn_crypto_stream( | |
| 718 | js_env_t *, | |
| 719 | js_receiver_t, | |
| 720 | js_typedarray_span_t<> c, | |
| 721 | js_typedarray_span_t<> n, | |
| 722 | js_typedarray_span_t<> k | |
| 723 | ) { | |
| 724 | assert(n.size_bytes() == crypto_stream_NONCEBYTES); | |
| 725 | assert(k.size_bytes() == crypto_stream_KEYBYTES); | |
| 726 | ||
| 727 | return crypto_stream(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 728 | } | |
| 729 | ||
| 730 | static inline int | |
| 731 | sn_crypto_stream_xor( | |
| 732 | js_env_t *env, | |
| 733 | js_receiver_t, | |
| 734 | ||
| 735 | js_arraybuffer_span_t c, | |
| 736 | uint32_t c_offset, | |
| 737 | uint32_t c_len, | |
| 738 | ||
| 739 | js_arraybuffer_span_t m, | |
| 740 | uint32_t m_offset, | |
| 741 | uint32_t m_len, | |
| 742 | ||
| 743 | js_arraybuffer_span_t n, | |
| 744 | uint32_t n_offset, | |
| 745 | uint32_t n_len, | |
| 746 | ||
| 747 | js_arraybuffer_span_t k, | |
| 748 | uint32_t k_offset, | |
| 749 | uint32_t k_len | |
| 750 | ) { | |
| 751 | assert_bounds(c); | |
| 752 | assert_bounds(m); | |
| 753 | assert_bounds(n); | |
| 754 | assert_bounds(k); | |
| 755 | ||
| 756 | assert(c_len == m_len); | |
| 757 | assert(n_len == crypto_stream_NONCEBYTES); | |
| 758 | assert(k_len == crypto_stream_KEYBYTES); | |
| 759 | ||
| 760 | return crypto_stream_xor(&c[c_offset], &m[m_offset], m_len, &n[n_offset], &k[k_offset]); | |
| 761 | } | |
| 762 | ||
| 763 | static inline int | |
| 764 | sn_crypto_stream_chacha20( | |
| 765 | js_env_t *, | |
| 766 | js_receiver_t, | |
| 767 | js_typedarray_span_t<> c, | |
| 768 | js_typedarray_span_t<> n, | |
| 769 | js_typedarray_span_t<> k | |
| 770 | ) { | |
| 771 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 772 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 773 | ||
| 774 | return crypto_stream_chacha20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 775 | } | |
| 776 | ||
| 777 | static inline int | |
| 778 | sn_crypto_stream_chacha20_xor( | |
| 779 | js_env_t *, | |
| 780 | js_receiver_t, | |
| 781 | js_typedarray_span_t<> c, | |
| 782 | js_typedarray_span_t<> m, | |
| 783 | js_typedarray_span_t<> n, | |
| 784 | js_typedarray_span_t<> k | |
| 785 | ) { | |
| 786 | assert(c.size_bytes() == m.size_bytes()); | |
| 787 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 788 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 789 | ||
| 790 | return crypto_stream_chacha20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 791 | } | |
| 792 | ||
| 793 | static inline int | |
| 794 | sn_crypto_stream_chacha20_xor_ic( | |
| 795 | js_env_t *, | |
| 796 | js_receiver_t, | |
| 797 | js_typedarray_span_t<> c, | |
| 798 | js_typedarray_span_t<> m, | |
| 799 | js_typedarray_span_t<> n, | |
| 800 | uint32_t ic, | |
| 801 | js_typedarray_span_t<> k | |
| 802 | ) { | |
| 803 | assert(c.size_bytes() == m.size_bytes()); | |
| 804 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 805 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 806 | ||
| 807 | return crypto_stream_chacha20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 808 | } | |
| 809 | ||
| 810 | static inline int | |
| 811 | sn_crypto_stream_chacha20_ietf( | |
| 812 | js_env_t *, | |
| 813 | js_receiver_t, | |
| 814 | js_typedarray_span_t<> c, | |
| 815 | js_typedarray_span_t<> n, | |
| 816 | js_typedarray_span_t<> k | |
| 817 | ) { | |
| 818 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 819 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 820 | ||
| 821 | return crypto_stream_chacha20_ietf(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 822 | } | |
| 823 | ||
| 824 | static inline int | |
| 825 | sn_crypto_stream_chacha20_ietf_xor( | |
| 826 | js_env_t *, | |
| 827 | js_receiver_t, | |
| 828 | js_typedarray_span_t<> c, | |
| 829 | js_typedarray_span_t<> m, | |
| 830 | js_typedarray_span_t<> n, | |
| 831 | js_typedarray_span_t<> k | |
| 832 | ) { | |
| 833 | assert(c.size_bytes() == m.size_bytes()); | |
| 834 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 835 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 836 | ||
| 837 | return crypto_stream_chacha20_ietf_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 838 | } | |
| 839 | ||
| 840 | static inline int | |
| 841 | sn_crypto_stream_chacha20_ietf_xor_ic( | |
| 842 | js_env_t *, | |
| 843 | js_receiver_t, | |
| 844 | js_typedarray_span_t<> c, | |
| 845 | js_typedarray_span_t<> m, | |
| 846 | js_typedarray_span_t<> n, | |
| 847 | uint32_t ic, | |
| 848 | js_typedarray_span_t<> k | |
| 849 | ) { | |
| 850 | assert(c.size_bytes() == m.size_bytes()); | |
| 851 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 852 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 853 | ||
| 854 | return crypto_stream_chacha20_ietf_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 855 | } | |
| 856 | ||
| 857 | static inline int | |
| 858 | sn_crypto_stream_xchacha20( | |
| 859 | js_env_t *, | |
| 860 | js_receiver_t, | |
| 861 | js_typedarray_span_t<> c, | |
| 862 | js_typedarray_span_t<> n, | |
| 863 | js_typedarray_span_t<> k | |
| 864 | ) { | |
| 865 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 866 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 867 | ||
| 868 | return crypto_stream_xchacha20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 869 | } | |
| 870 | ||
| 871 | static inline int | |
| 872 | sn_crypto_stream_xchacha20_xor( | |
| 873 | js_env_t *, | |
| 874 | js_receiver_t, | |
| 875 | js_typedarray_span_t<> c, | |
| 876 | js_typedarray_span_t<> m, | |
| 877 | js_typedarray_span_t<> n, | |
| 878 | js_typedarray_span_t<> k | |
| 879 | ) { | |
| 880 | assert(c.size_bytes() == m.size_bytes()); | |
| 881 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 882 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 883 | ||
| 884 | return crypto_stream_xchacha20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 885 | } | |
| 886 | ||
| 887 | static inline int | |
| 888 | sn_crypto_stream_xchacha20_xor_ic( | |
| 889 | js_env_t *, | |
| 890 | js_receiver_t, | |
| 891 | js_typedarray_span_t<> c, | |
| 892 | js_typedarray_span_t<> m, | |
| 893 | js_typedarray_span_t<> n, | |
| 894 | uint32_t ic, | |
| 895 | js_typedarray_span_t<> k | |
| 896 | ) { | |
| 897 | assert(c.size_bytes() == m.size_bytes()); | |
| 898 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 899 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 900 | ||
| 901 | return crypto_stream_xchacha20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 902 | } | |
| 903 | ||
| 904 | static inline int | |
| 905 | sn_crypto_stream_salsa20( | |
| 906 | js_env_t *, | |
| 907 | js_receiver_t, | |
| 908 | js_typedarray_span_t<> c, | |
| 909 | js_typedarray_span_t<> n, | |
| 910 | js_typedarray_span_t<> k | |
| 911 | ) { | |
| 912 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 913 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 914 | ||
| 915 | return crypto_stream_salsa20(c.data(), c.size_bytes(), n.data(), k.data()); | |
| 916 | } | |
| 917 | ||
| 918 | static inline int | |
| 919 | sn_crypto_stream_salsa20_xor( | |
| 920 | js_env_t *, | |
| 921 | js_receiver_t, | |
| 922 | js_typedarray_span_t<> c, | |
| 923 | js_typedarray_span_t<> m, | |
| 924 | js_typedarray_span_t<> n, | |
| 925 | js_typedarray_span_t<> k | |
| 926 | ) { | |
| 927 | assert(c.size_bytes() == m.size_bytes()); | |
| 928 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 929 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 930 | ||
| 931 | return crypto_stream_salsa20_xor(c.data(), m.data(), m.size_bytes(), n.data(), k.data()); | |
| 932 | } | |
| 933 | ||
| 934 | static inline int | |
| 935 | sn_crypto_stream_salsa20_xor_ic( | |
| 936 | js_env_t *, | |
| 937 | js_receiver_t, | |
| 938 | js_typedarray_span_t<> c, | |
| 939 | js_typedarray_span_t<> m, | |
| 940 | js_typedarray_span_t<> n, | |
| 941 | uint32_t ic, | |
| 942 | js_typedarray_span_t<> k | |
| 943 | ) { | |
| 944 | assert(c.size_bytes() == m.size_bytes()); | |
| 945 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 946 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 947 | ||
| 948 | return crypto_stream_salsa20_xor_ic(c.data(), m.data(), m.size_bytes(), n.data(), ic, k.data()); | |
| 949 | } | |
| 950 | ||
| 951 | static inline int | |
| 952 | sn_crypto_auth( | |
| 953 | js_env_t *, | |
| 954 | js_receiver_t, | |
| 955 | js_typedarray_span_t<> out, | |
| 956 | js_typedarray_span_t<> in, | |
| 957 | js_typedarray_span_t<> k | |
| 958 | ) { | |
| 959 | assert(out.size_bytes() == crypto_auth_BYTES); | |
| 960 | assert(k.size_bytes() == crypto_auth_KEYBYTES); | |
| 961 | ||
| 962 | return crypto_auth(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 963 | } | |
| 964 | ||
| 965 | static inline bool | |
| 966 | sn_crypto_auth_verify( | |
| 967 | js_env_t *, | |
| 968 | js_receiver_t, | |
| 969 | js_typedarray_span_t<> h, | |
| 970 | js_typedarray_span_t<> in, | |
| 971 | js_typedarray_span_t<> k | |
| 972 | ) { | |
| 973 | assert(h.size_bytes() == crypto_auth_BYTES); | |
| 974 | assert(k.size_bytes() == crypto_auth_KEYBYTES); | |
| 975 | ||
| 976 | return crypto_auth_verify(h.data(), in.data(), in.size_bytes(), k.data()) == 0; | |
| 977 | } | |
| 978 | ||
| 979 | static inline int | |
| 980 | sn_crypto_onetimeauth( | |
| 981 | js_env_t *, | |
| 982 | js_receiver_t, | |
| 983 | js_typedarray_span_t<> out, | |
| 984 | js_typedarray_span_t<> in, | |
| 985 | js_typedarray_span_t<> k | |
| 986 | ) { | |
| 987 | assert(out.size_bytes() == crypto_onetimeauth_BYTES); | |
| 988 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 989 | ||
| 990 | return crypto_onetimeauth(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 991 | } | |
| 992 | ||
| 993 | static inline int | |
| 994 | sn_crypto_onetimeauth_init( | |
| 995 | js_env_t *, | |
| 996 | js_receiver_t, | |
| 997 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 998 | js_typedarray_span_t<> k | |
| 999 | ) { | |
| 1000 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 1001 | ||
| 1002 | return crypto_onetimeauth_init(&*state, k.data()); | |
| 1003 | } | |
| 1004 | ||
| 1005 | static inline int | |
| 1006 | sn_crypto_onetimeauth_update( | |
| 1007 | js_env_t *, | |
| 1008 | js_receiver_t, | |
| 1009 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 1010 | js_typedarray_span_t<> in | |
| 1011 | ) { | |
| 1012 | return crypto_onetimeauth_update(&*state, in.data(), in.size_bytes()); | |
| 1013 | } | |
| 1014 | ||
| 1015 | static inline int | |
| 1016 | sn_crypto_onetimeauth_final( | |
| 1017 | js_env_t *, | |
| 1018 | js_receiver_t, | |
| 1019 | js_typedarray_span_of_t<crypto_onetimeauth_state, 1> state, | |
| 1020 | js_typedarray_span_t<> out | |
| 1021 | ) { | |
| 1022 | assert(out.size_bytes() == crypto_onetimeauth_BYTES); | |
| 1023 | ||
| 1024 | return crypto_onetimeauth_final(&*state, out.data()); | |
| 1025 | } | |
| 1026 | ||
| 1027 | static inline bool | |
| 1028 | sn_crypto_onetimeauth_verify( | |
| 1029 | js_env_t *, | |
| 1030 | js_receiver_t, | |
| 1031 | js_typedarray_span_t<> h, | |
| 1032 | js_typedarray_span_t<> in, | |
| 1033 | js_typedarray_span_t<> k | |
| 1034 | ) { | |
| 1035 | assert(h.size_bytes() == crypto_onetimeauth_BYTES); | |
| 1036 | assert(k.size_bytes() == crypto_onetimeauth_KEYBYTES); | |
| 1037 | ||
| 1038 | return crypto_onetimeauth_verify(h.data(), in.data(), in.size_bytes(), k.data()) == 0; | |
| 1039 | } | |
| 1040 | ||
| 1041 | // CHECK: memlimit can be >32bit | |
| 1042 | static inline int | |
| 1043 | sn_crypto_pwhash( | |
| 1044 | js_env_t *, | |
| 1045 | js_receiver_t, | |
| 1046 | js_typedarray_span_t<> out, | |
| 1047 | js_typedarray_span_of_t<char> passwd, | |
| 1048 | js_typedarray_span_t<> salt, | |
| 1049 | uint64_t opslimit, | |
| 1050 | uint64_t memlimit, | |
| 1051 | int32_t alg | |
| 1052 | ) { | |
| 1053 | assert(out.size_bytes() >= crypto_pwhash_BYTES_MIN); | |
| 1054 | assert(out.size_bytes() <= crypto_pwhash_BYTES_MAX); | |
| 1055 | assert(salt.size_bytes() == crypto_pwhash_SALTBYTES); | |
| 1056 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1057 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1058 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1059 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1060 | assert(alg == 1 || alg == 2); // Argon2i or Argon2id | |
| 1061 | ||
| 1062 | return crypto_pwhash( | |
| 1063 | out.data(), | |
| 1064 | out.size_bytes(), | |
| 1065 | passwd.data(), | |
| 1066 | passwd.size_bytes(), | |
| 1067 | salt.data(), | |
| 1068 | opslimit, | |
| 1069 | memlimit, | |
| 1070 | alg | |
| 1071 | ); | |
| 1072 | } | |
| 1073 | ||
| 1074 | static inline int | |
| 1075 | sn_crypto_pwhash_str( | |
| 1076 | js_env_t *, | |
| 1077 | js_receiver_t, | |
| 1078 | js_typedarray_span_of_t<char> out, | |
| 1079 | js_typedarray_span_of_t<char> passwd, | |
| 1080 | uint64_t opslimit, | |
| 1081 | uint64_t memlimit | |
| 1082 | ) { | |
| 1083 | assert(out.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1084 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1085 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1086 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1087 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1088 | ||
| 1089 | return crypto_pwhash_str( | |
| 1090 | out.data(), | |
| 1091 | passwd.data(), | |
| 1092 | passwd.size_bytes(), | |
| 1093 | opslimit, | |
| 1094 | memlimit | |
| 1095 | ); | |
| 1096 | } | |
| 1097 | ||
| 1098 | static inline bool | |
| 1099 | sn_crypto_pwhash_str_verify( | |
| 1100 | js_env_t *, | |
| 1101 | js_receiver_t, | |
| 1102 | js_typedarray_span_of_t<char> str, | |
| 1103 | js_typedarray_span_of_t<char> passwd | |
| 1104 | ) { | |
| 1105 | assert(str.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1106 | ||
| 1107 | return crypto_pwhash_str_verify(str.data(), passwd.data(), passwd.size_bytes()) == 0; | |
| 1108 | } | |
| 1109 | ||
| 1110 | // CHECK: returns 1, 0, -1 | |
| 1111 | static inline bool | |
| 1112 | sn_crypto_pwhash_str_needs_rehash( | |
| 1113 | js_env_t *, | |
| 1114 | js_receiver_t, | |
| 1115 | js_typedarray_span_of_t<char> str, | |
| 1116 | uint64_t opslimit, | |
| 1117 | uint64_t memlimit | |
| 1118 | ) { | |
| 1119 | assert(str.size_bytes() == crypto_pwhash_STRBYTES); | |
| 1120 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1121 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1122 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1123 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1124 | ||
| 1125 | return crypto_pwhash_str_needs_rehash(str.data(), opslimit, memlimit) != 0; | |
| 1126 | } | |
| 1127 | ||
| 1128 | // CHECK: memlimit can be >32bit | |
| 1129 | static inline int | |
| 1130 | sn_crypto_pwhash_scryptsalsa208sha256( | |
| 1131 | js_env_t *, | |
| 1132 | js_receiver_t, | |
| 1133 | js_typedarray_span_t<> out, | |
| 1134 | js_typedarray_span_of_t<char> passwd, | |
| 1135 | js_typedarray_span_t<> salt, | |
| 1136 | uint64_t opslimit, | |
| 1137 | uint64_t memlimit | |
| 1138 | ) { | |
| 1139 | assert(out.size_bytes() >= crypto_pwhash_scryptsalsa208sha256_BYTES_MIN); | |
| 1140 | assert(out.size_bytes() <= crypto_pwhash_scryptsalsa208sha256_BYTES_MAX); | |
| 1141 | assert(salt.size_bytes() == crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 1142 | assert(opslimit >= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 1143 | assert(opslimit <= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 1144 | assert(memlimit >= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 1145 | assert(memlimit <= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 1146 | ||
| 1147 | return crypto_pwhash_scryptsalsa208sha256( | |
| 1148 | out.data(), | |
| 1149 | out.size_bytes(), | |
| 1150 | passwd.data(), | |
| 1151 | passwd.size_bytes(), | |
| 1152 | salt.data(), | |
| 1153 | opslimit, | |
| 1154 | memlimit | |
| 1155 | ); | |
| 1156 | } | |
| 1157 | ||
| 1158 | static inline int | |
| 1159 | sn_crypto_pwhash_scryptsalsa208sha256_str( | |
| 1160 | js_env_t *, | |
| 1161 | js_receiver_t, | |
| 1162 | js_typedarray_span_of_t<char> out, | |
| 1163 | js_typedarray_span_of_t<char> passwd, | |
| 1164 | uint64_t opslimit, | |
| 1165 | uint64_t memlimit | |
| 1166 | ) { | |
| 1167 | assert(out.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1168 | assert(opslimit >= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 1169 | assert(opslimit <= crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 1170 | assert(memlimit >= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 1171 | assert(memlimit <= crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 1172 | ||
| 1173 | return crypto_pwhash_scryptsalsa208sha256_str( | |
| 1174 | out.data(), | |
| 1175 | passwd.data(), | |
| 1176 | passwd.size_bytes(), | |
| 1177 | opslimit, | |
| 1178 | memlimit | |
| 1179 | ); | |
| 1180 | } | |
| 1181 | ||
| 1182 | static inline bool | |
| 1183 | sn_crypto_pwhash_scryptsalsa208sha256_str_verify( | |
| 1184 | js_env_t *, | |
| 1185 | js_receiver_t, | |
| 1186 | js_typedarray_span_of_t<char> str, | |
| 1187 | js_typedarray_span_of_t<char> passwd | |
| 1188 | ) { | |
| 1189 | assert(str.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1190 | ||
| 1191 | return crypto_pwhash_scryptsalsa208sha256_str_verify(str.data(), passwd.data(), passwd.size_bytes()) == 0; | |
| 1192 | } | |
| 1193 | ||
| 1194 | static inline bool | |
| 1195 | sn_crypto_pwhash_scryptsalsa208sha256_str_needs_rehash( | |
| 1196 | js_env_t *, | |
| 1197 | js_receiver_t, | |
| 1198 | js_typedarray_span_of_t<char> str, | |
| 1199 | uint64_t opslimit, | |
| 1200 | uint64_t memlimit | |
| 1201 | ) { | |
| 1202 | assert(str.size_bytes() == crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 1203 | assert(opslimit >= crypto_pwhash_OPSLIMIT_MIN); | |
| 1204 | assert(opslimit <= crypto_pwhash_OPSLIMIT_MAX); | |
| 1205 | assert(memlimit >= crypto_pwhash_MEMLIMIT_MIN); | |
| 1206 | assert(memlimit <= crypto_pwhash_MEMLIMIT_MAX); | |
| 1207 | ||
| 1208 | return crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(str.data(), opslimit, memlimit) != 0; | |
| 1209 | } | |
| 1210 | ||
| 1211 | static inline int | |
| 1212 | sn_crypto_kx_keypair( | |
| 1213 | js_env_t *, | |
| 1214 | js_receiver_t, | |
| 1215 | js_typedarray_span_t<> pk, | |
| 1216 | js_typedarray_span_t<> sk | |
| 1217 | ) { | |
| 1218 | assert(pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1219 | assert(sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1220 | ||
| 1221 | return crypto_kx_keypair(pk.data(), sk.data()); | |
| 1222 | } | |
| 1223 | ||
| 1224 | static inline int | |
| 1225 | sn_crypto_kx_seed_keypair( | |
| 1226 | js_env_t *, | |
| 1227 | js_receiver_t, | |
| 1228 | js_typedarray_span_t<> pk, | |
| 1229 | js_typedarray_span_t<> sk, | |
| 1230 | js_typedarray_span_t<> seed | |
| 1231 | ) { | |
| 1232 | assert(pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1233 | assert(sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1234 | assert(seed.size_bytes() == crypto_kx_SEEDBYTES); | |
| 1235 | ||
| 1236 | return crypto_kx_seed_keypair(pk.data(), sk.data(), seed.data()); | |
| 1237 | } | |
| 1238 | ||
| 1239 | static inline int | |
| 1240 | sn_crypto_kx_client_session_keys( | |
| 1241 | js_env_t *, | |
| 1242 | js_receiver_t, | |
| 1243 | std::optional<js_typedarray_span_t<>> rx, | |
| 1244 | std::optional<js_typedarray_span_t<>> tx, | |
| 1245 | js_typedarray_span_t<> client_pk, | |
| 1246 | js_typedarray_span_t<> client_sk, | |
| 1247 | js_typedarray_span_t<> server_pk | |
| 1248 | ) { | |
| 1249 | assert(rx.has_value() || tx.has_value()); | |
| 1250 | ||
| 1251 | if (rx) assert(rx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1252 | if (tx) assert(tx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1253 | ||
| 1254 | assert(client_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1255 | assert(client_sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1256 | assert(server_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1257 | ||
| 1258 | return crypto_kx_client_session_keys( | |
| 1259 | rx ? rx->data() : nullptr, | |
| 1260 | tx ? tx->data() : nullptr, | |
| 1261 | client_pk.data(), | |
| 1262 | client_sk.data(), | |
| 1263 | server_pk.data() | |
| 1264 | ); | |
| 1265 | } | |
| 1266 | ||
| 1267 | static inline int | |
| 1268 | sn_crypto_kx_server_session_keys( | |
| 1269 | js_env_t *, | |
| 1270 | js_receiver_t, | |
| 1271 | std::optional<js_typedarray_span_t<>> rx, | |
| 1272 | std::optional<js_typedarray_span_t<>> tx, | |
| 1273 | js_typedarray_span_t<> server_pk, | |
| 1274 | js_typedarray_span_t<> server_sk, | |
| 1275 | js_typedarray_span_t<> client_pk | |
| 1276 | ) { | |
| 1277 | assert(rx.has_value() || tx.has_value()); | |
| 1278 | ||
| 1279 | if (rx) assert(rx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1280 | if (tx) assert(tx->size_bytes() == crypto_kx_SESSIONKEYBYTES); | |
| 1281 | ||
| 1282 | assert(server_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1283 | assert(server_sk.size_bytes() == crypto_kx_SECRETKEYBYTES); | |
| 1284 | assert(client_pk.size_bytes() == crypto_kx_PUBLICKEYBYTES); | |
| 1285 | ||
| 1286 | return crypto_kx_server_session_keys( | |
| 1287 | rx ? rx->data() : nullptr, | |
| 1288 | tx ? tx->data() : nullptr, | |
| 1289 | server_pk.data(), | |
| 1290 | server_sk.data(), | |
| 1291 | client_pk.data() | |
| 1292 | ); | |
| 1293 | } | |
| 1294 | ||
| 1295 | static inline int | |
| 1296 | sn_crypto_scalarmult_base( | |
| 1297 | js_env_t *, | |
| 1298 | js_receiver_t, | |
| 1299 | js_typedarray_span_t<> q, | |
| 1300 | js_typedarray_span_t<> n | |
| 1301 | ) { | |
| 1302 | assert(q.size_bytes() == crypto_scalarmult_BYTES); | |
| 1303 | assert(n.size_bytes() == crypto_scalarmult_SCALARBYTES); | |
| 1304 | ||
| 1305 | return crypto_scalarmult_base(q.data(), n.data()); | |
| 1306 | } | |
| 1307 | ||
| 1308 | static inline int | |
| 1309 | sn_crypto_scalarmult( | |
| 1310 | js_env_t *, | |
| 1311 | js_receiver_t, | |
| 1312 | js_typedarray_span_t<> q, | |
| 1313 | js_typedarray_span_t<> n, | |
| 1314 | js_typedarray_span_t<> p | |
| 1315 | ) { | |
| 1316 | assert(q.size_bytes() == crypto_scalarmult_BYTES); | |
| 1317 | assert(n.size_bytes() == crypto_scalarmult_SCALARBYTES); | |
| 1318 | assert(p.size_bytes() == crypto_scalarmult_BYTES); | |
| 1319 | ||
| 1320 | return crypto_scalarmult(q.data(), n.data(), p.data()); | |
| 1321 | } | |
| 1322 | ||
| 1323 | static inline int | |
| 1324 | sn_crypto_scalarmult_ed25519_base( | |
| 1325 | js_env_t *, | |
| 1326 | js_receiver_t, | |
| 1327 | js_typedarray_span_t<> q, | |
| 1328 | js_typedarray_span_t<> n | |
| 1329 | ) { | |
| 1330 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1331 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1332 | ||
| 1333 | return crypto_scalarmult_ed25519_base(q.data(), n.data()); | |
| 1334 | } | |
| 1335 | ||
| 1336 | static inline int | |
| 1337 | sn_crypto_scalarmult_ed25519( | |
| 1338 | js_env_t *, | |
| 1339 | js_receiver_t, | |
| 1340 | js_typedarray_span_t<> q, | |
| 1341 | js_typedarray_span_t<> n, | |
| 1342 | js_typedarray_span_t<> p | |
| 1343 | ) { | |
| 1344 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1345 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1346 | assert(p.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1347 | ||
| 1348 | return crypto_scalarmult_ed25519(q.data(), n.data(), p.data()); | |
| 1349 | } | |
| 1350 | ||
| 1351 | static inline bool | |
| 1352 | sn_crypto_core_ed25519_is_valid_point( | |
| 1353 | js_env_t *, | |
| 1354 | js_receiver_t, | |
| 1355 | js_typedarray_span_t<> p | |
| 1356 | ) { | |
| 1357 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1358 | ||
| 1359 | return crypto_core_ed25519_is_valid_point(p.data()) != 0; | |
| 1360 | } | |
| 1361 | ||
| 1362 | static inline int | |
| 1363 | sn_crypto_core_ed25519_from_uniform( | |
| 1364 | js_env_t *, | |
| 1365 | js_receiver_t, | |
| 1366 | js_typedarray_span_t<> p, | |
| 1367 | js_typedarray_span_t<> r | |
| 1368 | ) { | |
| 1369 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1370 | assert(r.size_bytes() == crypto_core_ed25519_UNIFORMBYTES); | |
| 1371 | ||
| 1372 | return crypto_core_ed25519_from_uniform(p.data(), r.data()); | |
| 1373 | } | |
| 1374 | ||
| 1375 | static inline int | |
| 1376 | sn_crypto_scalarmult_ed25519_base_noclamp( | |
| 1377 | js_env_t *, | |
| 1378 | js_receiver_t, | |
| 1379 | js_typedarray_span_t<> q, | |
| 1380 | js_typedarray_span_t<> n | |
| 1381 | ) { | |
| 1382 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1383 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1384 | ||
| 1385 | return crypto_scalarmult_ed25519_base_noclamp(q.data(), n.data()); | |
| 1386 | } | |
| 1387 | ||
| 1388 | static inline int | |
| 1389 | sn_crypto_scalarmult_ed25519_noclamp( | |
| 1390 | js_env_t *, | |
| 1391 | js_receiver_t, | |
| 1392 | js_typedarray_span_t<> q, | |
| 1393 | js_typedarray_span_t<> n, | |
| 1394 | js_typedarray_span_t<> p | |
| 1395 | ) { | |
| 1396 | assert(q.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1397 | assert(n.size_bytes() == crypto_scalarmult_ed25519_SCALARBYTES); | |
| 1398 | assert(p.size_bytes() == crypto_scalarmult_ed25519_BYTES); | |
| 1399 | ||
| 1400 | return crypto_scalarmult_ed25519_noclamp(q.data(), n.data(), p.data()); | |
| 1401 | } | |
| 1402 | ||
| 1403 | static inline int | |
| 1404 | sn_crypto_core_ed25519_add( | |
| 1405 | js_env_t *, | |
| 1406 | js_receiver_t, | |
| 1407 | js_typedarray_span_t<> r, | |
| 1408 | js_typedarray_span_t<> p, | |
| 1409 | js_typedarray_span_t<> q | |
| 1410 | ) { | |
| 1411 | assert(r.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1412 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1413 | assert(q.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1414 | ||
| 1415 | return crypto_core_ed25519_add(r.data(), p.data(), q.data()); | |
| 1416 | } | |
| 1417 | ||
| 1418 | static inline int | |
| 1419 | sn_crypto_core_ed25519_sub( | |
| 1420 | js_env_t *, | |
| 1421 | js_receiver_t, | |
| 1422 | js_typedarray_span_t<> r, | |
| 1423 | js_typedarray_span_t<> p, | |
| 1424 | js_typedarray_span_t<> q | |
| 1425 | ) { | |
| 1426 | assert(r.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1427 | assert(p.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1428 | assert(q.size_bytes() == crypto_core_ed25519_BYTES); | |
| 1429 | ||
| 1430 | return crypto_core_ed25519_sub(r.data(), p.data(), q.data()); | |
| 1431 | } | |
| 1432 | ||
| 1433 | static inline void | |
| 1434 | sn_crypto_core_ed25519_scalar_random( | |
| 1435 | js_env_t *, | |
| 1436 | js_receiver_t, | |
| 1437 | js_typedarray_span_t<> r | |
| 1438 | ) { | |
| 1439 | assert(r.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1440 | ||
| 1441 | crypto_core_ed25519_scalar_random(r.data()); | |
| 1442 | } | |
| 1443 | ||
| 1444 | static inline void | |
| 1445 | sn_crypto_core_ed25519_scalar_reduce( | |
| 1446 | js_env_t *, | |
| 1447 | js_receiver_t, | |
| 1448 | js_typedarray_span_t<> r, | |
| 1449 | js_typedarray_span_t<> s | |
| 1450 | ) { | |
| 1451 | assert(r.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1452 | assert(s.size_bytes() == crypto_core_ed25519_NONREDUCEDSCALARBYTES); | |
| 1453 | ||
| 1454 | crypto_core_ed25519_scalar_reduce(r.data(), s.data()); | |
| 1455 | } | |
| 1456 | ||
| 1457 | static inline void | |
| 1458 | sn_crypto_core_ed25519_scalar_invert( | |
| 1459 | js_env_t *, | |
| 1460 | js_receiver_t, | |
| 1461 | js_typedarray_span_t<> recip, | |
| 1462 | js_typedarray_span_t<> s | |
| 1463 | ) { | |
| 1464 | assert(recip.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1465 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1466 | ||
| 1467 | crypto_core_ed25519_scalar_invert(recip.data(), s.data()); | |
| 1468 | } | |
| 1469 | ||
| 1470 | static inline void | |
| 1471 | sn_crypto_core_ed25519_scalar_negate( | |
| 1472 | js_env_t *, | |
| 1473 | js_receiver_t, | |
| 1474 | js_typedarray_span_t<> neg, | |
| 1475 | js_typedarray_span_t<> s | |
| 1476 | ) { | |
| 1477 | assert(neg.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1478 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1479 | ||
| 1480 | crypto_core_ed25519_scalar_negate(neg.data(), s.data()); | |
| 1481 | } | |
| 1482 | ||
| 1483 | static inline void | |
| 1484 | sn_crypto_core_ed25519_scalar_complement( | |
| 1485 | js_env_t *, | |
| 1486 | js_receiver_t, | |
| 1487 | js_typedarray_span_t<> comp, | |
| 1488 | js_typedarray_span_t<> s | |
| 1489 | ) { | |
| 1490 | assert(comp.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1491 | assert(s.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1492 | ||
| 1493 | crypto_core_ed25519_scalar_complement(comp.data(), s.data()); | |
| 1494 | } | |
| 1495 | ||
| 1496 | static inline void | |
| 1497 | sn_crypto_core_ed25519_scalar_add( | |
| 1498 | js_env_t *, | |
| 1499 | js_receiver_t, | |
| 1500 | js_typedarray_span_t<> z, | |
| 1501 | js_typedarray_span_t<> x, | |
| 1502 | js_typedarray_span_t<> y | |
| 1503 | ) { | |
| 1504 | assert(z.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1505 | assert(x.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1506 | assert(y.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1507 | ||
| 1508 | crypto_core_ed25519_scalar_add(z.data(), x.data(), y.data()); | |
| 1509 | } | |
| 1510 | ||
| 1511 | static inline void | |
| 1512 | sn_crypto_core_ed25519_scalar_sub( | |
| 1513 | js_env_t *, | |
| 1514 | js_receiver_t, | |
| 1515 | js_typedarray_span_t<> z, | |
| 1516 | js_typedarray_span_t<> x, | |
| 1517 | js_typedarray_span_t<> y | |
| 1518 | ) { | |
| 1519 | assert(z.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1520 | assert(x.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1521 | assert(y.size_bytes() == crypto_core_ed25519_SCALARBYTES); | |
| 1522 | ||
| 1523 | crypto_core_ed25519_scalar_sub(z.data(), x.data(), y.data()); | |
| 1524 | } | |
| 1525 | ||
| 1526 | static inline int | |
| 1527 | sn_crypto_shorthash( | |
| 1528 | js_env_t *, | |
| 1529 | js_receiver_t, | |
| 1530 | js_typedarray_span_t<> out, | |
| 1531 | js_typedarray_span_t<> in, | |
| 1532 | js_typedarray_span_t<> k | |
| 1533 | ) { | |
| 1534 | assert(out.size_bytes() == crypto_shorthash_BYTES); | |
| 1535 | assert(k.size_bytes() == crypto_shorthash_KEYBYTES); | |
| 1536 | ||
| 1537 | return crypto_shorthash(out.data(), in.data(), in.size_bytes(), k.data()); | |
| 1538 | } | |
| 1539 | ||
| 1540 | static inline void | |
| 1541 | sn_crypto_kdf_keygen( | |
| 1542 | js_env_t *, | |
| 1543 | js_receiver_t, | |
| 1544 | js_typedarray_span_t<> key | |
| 1545 | ) { | |
| 1546 | assert(key.size_bytes() == crypto_kdf_KEYBYTES); | |
| 1547 | ||
| 1548 | crypto_kdf_keygen(key.data()); | |
| 1549 | } | |
| 1550 | ||
| 1551 | static inline int | |
| 1552 | sn_crypto_kdf_derive_from_key( | |
| 1553 | js_env_t *, | |
| 1554 | js_receiver_t, | |
| 1555 | js_typedarray_span_t<> subkey, | |
| 1556 | uint64_t subkey_id, | |
| 1557 | js_typedarray_span_of_t<char> ctx, | |
| 1558 | js_typedarray_span_t<> key | |
| 1559 | ) { | |
| 1560 | assert(subkey.size_bytes() >= crypto_kdf_BYTES_MIN); | |
| 1561 | assert(subkey.size_bytes() <= crypto_kdf_BYTES_MAX); | |
| 1562 | assert(ctx.size_bytes() == crypto_kdf_CONTEXTBYTES); | |
| 1563 | assert(key.size_bytes() == crypto_kdf_KEYBYTES); | |
| 1564 | ||
| 1565 | return crypto_kdf_derive_from_key( | |
| 1566 | subkey.data(), | |
| 1567 | subkey.size_bytes(), | |
| 1568 | subkey_id, | |
| 1569 | ctx.data(), | |
| 1570 | key.data() | |
| 1571 | ); | |
| 1572 | } | |
| 1573 | ||
| 1574 | static inline int | |
| 1575 | sn_crypto_hash( | |
| 1576 | js_env_t *, | |
| 1577 | js_receiver_t, | |
| 1578 | js_typedarray_span_t<> out, | |
| 1579 | js_typedarray_span_t<> in | |
| 1580 | ) { | |
| 1581 | assert(out.size_bytes() == crypto_hash_BYTES); | |
| 1582 | ||
| 1583 | return crypto_hash(out.data(), in.data(), in.size_bytes()); | |
| 1584 | } | |
| 1585 | ||
| 1586 | static inline int | |
| 1587 | sn_crypto_hash_sha256( | |
| 1588 | js_env_t *, | |
| 1589 | js_receiver_t, | |
| 1590 | js_typedarray_span_t<> out, | |
| 1591 | js_typedarray_span_t<> in | |
| 1592 | ) { | |
| 1593 | assert(out.size_bytes() == crypto_hash_sha256_BYTES); | |
| 1594 | ||
| 1595 | return crypto_hash_sha256(out.data(), in.data(), in.size_bytes()); | |
| 1596 | } | |
| 1597 | ||
| 1598 | static inline int | |
| 1599 | sn_crypto_hash_sha256_init( | |
| 1600 | js_env_t *, | |
| 1601 | js_receiver_t, | |
| 1602 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state | |
| 1603 | ) { | |
| 1604 | return crypto_hash_sha256_init(&*state); | |
| 1605 | } | |
| 1606 | ||
| 1607 | static inline int | |
| 1608 | sn_crypto_hash_sha256_update( | |
| 1609 | js_env_t *, | |
| 1610 | js_receiver_t, | |
| 1611 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state, | |
| 1612 | js_typedarray_span_t<> in | |
| 1613 | ) { | |
| 1614 | return crypto_hash_sha256_update(&*state, in.data(), in.size_bytes()); | |
| 1615 | } | |
| 1616 | ||
| 1617 | static inline int | |
| 1618 | sn_crypto_hash_sha256_final( | |
| 1619 | js_env_t *, | |
| 1620 | js_receiver_t, | |
| 1621 | js_typedarray_span_of_t<crypto_hash_sha256_state, 1> state, | |
| 1622 | js_typedarray_span_t<> out | |
| 1623 | ) { | |
| 1624 | assert(out.size_bytes() == crypto_hash_sha256_BYTES); | |
| 1625 | ||
| 1626 | return crypto_hash_sha256_final(&*state, out.data()); | |
| 1627 | } | |
| 1628 | ||
| 1629 | static inline int | |
| 1630 | sn_crypto_hash_sha512( | |
| 1631 | js_env_t *, | |
| 1632 | js_receiver_t, | |
| 1633 | js_typedarray_span_t<> out, | |
| 1634 | js_typedarray_span_t<> in | |
| 1635 | ) { | |
| 1636 | assert(out.size_bytes() == crypto_hash_sha512_BYTES); | |
| 1637 | ||
| 1638 | return crypto_hash_sha512(out.data(), in.data(), in.size_bytes()); | |
| 1639 | } | |
| 1640 | ||
| 1641 | static inline int | |
| 1642 | sn_crypto_hash_sha512_init( | |
| 1643 | js_env_t *, | |
| 1644 | js_receiver_t, | |
| 1645 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state | |
| 1646 | ) { | |
| 1647 | return crypto_hash_sha512_init(&*state); | |
| 1648 | } | |
| 1649 | ||
| 1650 | static inline int | |
| 1651 | sn_crypto_hash_sha512_update( | |
| 1652 | js_env_t *, | |
| 1653 | js_receiver_t, | |
| 1654 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state, | |
| 1655 | js_typedarray_span_t<> in | |
| 1656 | ) { | |
| 1657 | return crypto_hash_sha512_update(&*state, in.data(), in.size_bytes()); | |
| 1658 | } | |
| 1659 | ||
| 1660 | static inline int | |
| 1661 | sn_crypto_hash_sha512_final( | |
| 1662 | js_env_t *, | |
| 1663 | js_receiver_t, | |
| 1664 | js_typedarray_span_of_t<crypto_hash_sha512_state, 1> state, | |
| 1665 | js_typedarray_span_t<> out | |
| 1666 | ) { | |
| 1667 | assert(out.size_bytes() == crypto_hash_sha512_BYTES); | |
| 1668 | ||
| 1669 | return crypto_hash_sha512_final(&*state, out.data()); | |
| 1670 | } | |
| 1671 | ||
| 1672 | static inline void | |
| 1673 | sn_crypto_aead_xchacha20poly1305_ietf_keygen( | |
| 1674 | js_env_t *, | |
| 1675 | js_receiver_t, | |
| 1676 | js_typedarray_span_t<> k | |
| 1677 | ) { | |
| 1678 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1679 | ||
| 1680 | crypto_aead_xchacha20poly1305_ietf_keygen(k.data()); | |
| 1681 | } | |
| 1682 | ||
| 1683 | static inline uint64_t | |
| 1684 | sn_crypto_aead_xchacha20poly1305_ietf_encrypt( | |
| 1685 | js_env_t *, | |
| 1686 | js_receiver_t, | |
| 1687 | js_typedarray_span_t<> c, | |
| 1688 | js_typedarray_span_t<> m, | |
| 1689 | std::optional<js_typedarray_span_t<>> ad, | |
| 1690 | js_typedarray_span_t<> npub, | |
| 1691 | js_typedarray_span_t<> k | |
| 1692 | ) { | |
| 1693 | assert(c.size_bytes() == m.size_bytes() + crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1694 | assert(c.size_bytes() <= 0xffffffff); | |
| 1695 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1696 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1697 | ||
| 1698 | uint8_t *ad_data = nullptr; | |
| 1699 | size_t ad_size = 0; | |
| 1700 | if (ad) { | |
| 1701 | ad_data = ad->data(); | |
| 1702 | ad_size = ad->size_bytes(); | |
| 1703 | } | |
| 1704 | ||
| 1705 | unsigned long long clen = 0; | |
| 1706 | int status = crypto_aead_xchacha20poly1305_ietf_encrypt( | |
| 1707 | c.data(), | |
| 1708 | &clen, | |
| 1709 | m.data(), | |
| 1710 | m.size_bytes(), | |
| 1711 | ad_data, | |
| 1712 | ad_size, | |
| 1713 | nullptr, | |
| 1714 | npub.data(), | |
| 1715 | k.data() | |
| 1716 | ); | |
| 1717 | ||
| 1718 | if (status < 0) return status; | |
| 1719 | ||
| 1720 | return clen; | |
| 1721 | } | |
| 1722 | ||
| 1723 | static inline uint64_t | |
| 1724 | sn_crypto_aead_xchacha20poly1305_ietf_decrypt( | |
| 1725 | js_env_t *, | |
| 1726 | js_receiver_t, | |
| 1727 | js_typedarray_span_t<> m, | |
| 1728 | js_typedarray_span_t<> c, | |
| 1729 | std::optional<js_typedarray_span_t<>> ad, | |
| 1730 | js_typedarray_span_t<> npub, | |
| 1731 | js_typedarray_span_t<> k | |
| 1732 | ) { | |
| 1733 | assert(m.size_bytes() == c.size_bytes() - crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1734 | assert(m.size_bytes() <= 0xffffffff); | |
| 1735 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1736 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1737 | ||
| 1738 | uint8_t *ad_data = nullptr; | |
| 1739 | size_t ad_size = 0; | |
| 1740 | if (ad) { | |
| 1741 | ad_data = ad->data(); | |
| 1742 | ad_size = ad->size_bytes(); | |
| 1743 | } | |
| 1744 | ||
| 1745 | unsigned long long mlen = 0; | |
| 1746 | int status = crypto_aead_xchacha20poly1305_ietf_decrypt( | |
| 1747 | m.data(), | |
| 1748 | &mlen, | |
| 1749 | nullptr, | |
| 1750 | c.data(), | |
| 1751 | c.size_bytes(), | |
| 1752 | ad_data, | |
| 1753 | ad_size, | |
| 1754 | npub.data(), | |
| 1755 | k.data() | |
| 1756 | ); | |
| 1757 | ||
| 1758 | if (status < 0) return status; | |
| 1759 | ||
| 1760 | return mlen; | |
| 1761 | } | |
| 1762 | ||
| 1763 | static inline uint64_t | |
| 1764 | sn_crypto_aead_xchacha20poly1305_ietf_encrypt_detached( | |
| 1765 | js_env_t *, | |
| 1766 | js_receiver_t, | |
| 1767 | js_typedarray_span_t<> c, | |
| 1768 | js_typedarray_span_t<> mac, | |
| 1769 | js_typedarray_span_t<> m, | |
| 1770 | std::optional<js_typedarray_span_t<>> ad, | |
| 1771 | js_typedarray_span_t<> npub, | |
| 1772 | js_typedarray_span_t<> k | |
| 1773 | ) { | |
| 1774 | assert(c.size_bytes() == m.size_bytes()); | |
| 1775 | assert(mac.size_bytes() == crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1776 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1777 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1778 | ||
| 1779 | uint8_t *ad_data = nullptr; | |
| 1780 | size_t ad_size = 0; | |
| 1781 | if (ad) { | |
| 1782 | ad_data = ad->data(); | |
| 1783 | ad_size = ad->size_bytes(); | |
| 1784 | } | |
| 1785 | ||
| 1786 | unsigned long long maclen = 0; | |
| 1787 | int status = crypto_aead_xchacha20poly1305_ietf_encrypt_detached( | |
| 1788 | c.data(), | |
| 1789 | mac.data(), | |
| 1790 | &maclen, | |
| 1791 | m.data(), | |
| 1792 | m.size_bytes(), | |
| 1793 | ad_data, | |
| 1794 | ad_size, | |
| 1795 | nullptr, | |
| 1796 | npub.data(), | |
| 1797 | k.data() | |
| 1798 | ); | |
| 1799 | ||
| 1800 | if (status < 0) return status; | |
| 1801 | ||
| 1802 | return maclen; | |
| 1803 | } | |
| 1804 | ||
| 1805 | static inline int | |
| 1806 | sn_crypto_aead_xchacha20poly1305_ietf_decrypt_detached( | |
| 1807 | js_env_t *, | |
| 1808 | js_receiver_t, | |
| 1809 | js_typedarray_span_t<> m, | |
| 1810 | js_typedarray_span_t<> c, | |
| 1811 | js_typedarray_span_t<> mac, | |
| 1812 | std::optional<js_typedarray_span_t<>> ad, | |
| 1813 | js_typedarray_span_t<> npub, | |
| 1814 | js_typedarray_span_t<> k | |
| 1815 | ) { | |
| 1816 | assert(m.size_bytes() == c.size_bytes()); | |
| 1817 | assert(mac.size_bytes() == crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 1818 | assert(npub.size_bytes() == crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 1819 | assert(k.size_bytes() == crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 1820 | ||
| 1821 | uint8_t *ad_data = nullptr; | |
| 1822 | size_t ad_size = 0; | |
| 1823 | if (ad) { | |
| 1824 | ad_data = ad->data(); | |
| 1825 | ad_size = ad->size_bytes(); | |
| 1826 | } | |
| 1827 | ||
| 1828 | return crypto_aead_xchacha20poly1305_ietf_decrypt_detached( | |
| 1829 | m.data(), | |
| 1830 | nullptr, | |
| 1831 | c.data(), | |
| 1832 | c.size_bytes(), | |
| 1833 | mac.data(), | |
| 1834 | ad_data, | |
| 1835 | ad_size, | |
| 1836 | npub.data(), | |
| 1837 | k.data() | |
| 1838 | ); | |
| 1839 | } | |
| 1840 | ||
| 1841 | static inline void | |
| 1842 | sn_crypto_aead_chacha20poly1305_ietf_keygen( | |
| 1843 | js_env_t *, | |
| 1844 | js_receiver_t, | |
| 1845 | js_typedarray_span_t<> k | |
| 1846 | ) { | |
| 1847 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1848 | ||
| 1849 | crypto_aead_chacha20poly1305_ietf_keygen(k.data()); | |
| 1850 | } | |
| 1851 | ||
| 1852 | static inline uint64_t | |
| 1853 | sn_crypto_aead_chacha20poly1305_ietf_encrypt( | |
| 1854 | js_env_t *, | |
| 1855 | js_receiver_t, | |
| 1856 | js_typedarray_span_t<> c, | |
| 1857 | js_typedarray_span_t<> m, | |
| 1858 | std::optional<js_typedarray_span_t<>> ad, | |
| 1859 | js_typedarray_span_t<> npub, | |
| 1860 | js_typedarray_span_t<> k | |
| 1861 | ) { | |
| 1862 | assert(c.size_bytes() == m.size_bytes() + crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1863 | assert(c.size_bytes() <= 0xffffffff); | |
| 1864 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1865 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1866 | ||
| 1867 | uint8_t *ad_data = nullptr; | |
| 1868 | size_t ad_size = 0; | |
| 1869 | if (ad) { | |
| 1870 | ad_data = ad->data(); | |
| 1871 | ad_size = ad->size_bytes(); | |
| 1872 | } | |
| 1873 | ||
| 1874 | unsigned long long clen = 0; | |
| 1875 | int status = crypto_aead_chacha20poly1305_ietf_encrypt( | |
| 1876 | c.data(), | |
| 1877 | &clen, | |
| 1878 | m.data(), | |
| 1879 | m.size_bytes(), | |
| 1880 | ad_data, | |
| 1881 | ad_size, | |
| 1882 | nullptr, | |
| 1883 | npub.data(), | |
| 1884 | k.data() | |
| 1885 | ); | |
| 1886 | ||
| 1887 | if (status < 0) return status; | |
| 1888 | ||
| 1889 | return clen; | |
| 1890 | } | |
| 1891 | ||
| 1892 | static inline uint64_t | |
| 1893 | sn_crypto_aead_chacha20poly1305_ietf_decrypt( | |
| 1894 | js_env_t *, | |
| 1895 | js_receiver_t, | |
| 1896 | js_typedarray_span_t<> m, | |
| 1897 | js_typedarray_span_t<> c, | |
| 1898 | std::optional<js_typedarray_span_t<>> ad, | |
| 1899 | js_typedarray_span_t<> npub, | |
| 1900 | js_typedarray_span_t<> k | |
| 1901 | ) { | |
| 1902 | assert(m.size_bytes() == c.size_bytes() - crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1903 | assert(m.size_bytes() <= 0xffffffff); | |
| 1904 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1905 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1906 | ||
| 1907 | uint8_t *ad_data = nullptr; | |
| 1908 | size_t ad_size = 0; | |
| 1909 | if (ad) { | |
| 1910 | ad_data = ad->data(); | |
| 1911 | ad_size = ad->size_bytes(); | |
| 1912 | } | |
| 1913 | ||
| 1914 | unsigned long long mlen = 0; | |
| 1915 | int status = crypto_aead_chacha20poly1305_ietf_decrypt( | |
| 1916 | m.data(), | |
| 1917 | &mlen, | |
| 1918 | nullptr, | |
| 1919 | c.data(), | |
| 1920 | c.size_bytes(), | |
| 1921 | ad_data, | |
| 1922 | ad_size, | |
| 1923 | npub.data(), | |
| 1924 | k.data() | |
| 1925 | ); | |
| 1926 | ||
| 1927 | if (status < 0) return status; | |
| 1928 | ||
| 1929 | return mlen; | |
| 1930 | } | |
| 1931 | ||
| 1932 | static inline uint64_t | |
| 1933 | sn_crypto_aead_chacha20poly1305_ietf_encrypt_detached( | |
| 1934 | js_env_t *, | |
| 1935 | js_receiver_t, | |
| 1936 | js_typedarray_span_t<> c, | |
| 1937 | js_typedarray_span_t<> mac, | |
| 1938 | js_typedarray_span_t<> m, | |
| 1939 | std::optional<js_typedarray_span_t<>> ad, | |
| 1940 | js_typedarray_span_t<> npub, | |
| 1941 | js_typedarray_span_t<> k | |
| 1942 | ) { | |
| 1943 | assert(c.size_bytes() == m.size_bytes()); | |
| 1944 | assert(mac.size_bytes() == crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1945 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1946 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1947 | ||
| 1948 | uint8_t *ad_data = nullptr; | |
| 1949 | size_t ad_size = 0; | |
| 1950 | if (ad) { | |
| 1951 | ad_data = ad->data(); | |
| 1952 | ad_size = ad->size_bytes(); | |
| 1953 | } | |
| 1954 | ||
| 1955 | unsigned long long maclen = 0; | |
| 1956 | int status = crypto_aead_chacha20poly1305_ietf_encrypt_detached( | |
| 1957 | c.data(), | |
| 1958 | mac.data(), | |
| 1959 | &maclen, | |
| 1960 | m.data(), | |
| 1961 | m.size_bytes(), | |
| 1962 | ad_data, | |
| 1963 | ad_size, | |
| 1964 | nullptr, | |
| 1965 | npub.data(), | |
| 1966 | k.data() | |
| 1967 | ); | |
| 1968 | ||
| 1969 | if (status < 0) return status; | |
| 1970 | ||
| 1971 | return maclen; | |
| 1972 | } | |
| 1973 | ||
| 1974 | static inline int | |
| 1975 | sn_crypto_aead_chacha20poly1305_ietf_decrypt_detached( | |
| 1976 | js_env_t *, | |
| 1977 | js_receiver_t, | |
| 1978 | js_typedarray_span_t<> m, | |
| 1979 | js_typedarray_span_t<> c, | |
| 1980 | js_typedarray_span_t<> mac, | |
| 1981 | std::optional<js_typedarray_span_t<>> ad, | |
| 1982 | js_typedarray_span_t<> npub, | |
| 1983 | js_typedarray_span_t<> k | |
| 1984 | ) { | |
| 1985 | assert(m.size_bytes() == c.size_bytes()); | |
| 1986 | assert(mac.size_bytes() == crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 1987 | assert(npub.size_bytes() == crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 1988 | assert(k.size_bytes() == crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 1989 | ||
| 1990 | uint8_t *ad_data = nullptr; | |
| 1991 | size_t ad_size = 0; | |
| 1992 | if (ad) { | |
| 1993 | ad_data = ad->data(); | |
| 1994 | ad_size = ad->size_bytes(); | |
| 1995 | } | |
| 1996 | ||
| 1997 | return crypto_aead_chacha20poly1305_ietf_decrypt_detached( | |
| 1998 | m.data(), | |
| 1999 | nullptr, | |
| 2000 | c.data(), | |
| 2001 | c.size_bytes(), | |
| 2002 | mac.data(), | |
| 2003 | ad_data, | |
| 2004 | ad_size, | |
| 2005 | npub.data(), | |
| 2006 | k.data() | |
| 2007 | ); | |
| 2008 | } | |
| 2009 | ||
| 2010 | static inline void | |
| 2011 | sn_crypto_secretstream_xchacha20poly1305_keygen( | |
| 2012 | js_env_t *env, | |
| 2013 | js_receiver_t, | |
| 2014 | ||
| 2015 | js_arraybuffer_span_t k, | |
| 2016 | uint32_t k_offset, | |
| 2017 | uint32_t k_len | |
| 2018 | ) { | |
| 2019 | assert_bounds(k); | |
| 2020 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2021 | ||
| 2022 | crypto_secretstream_xchacha20poly1305_keygen(&k[k_offset]); | |
| 2023 | } | |
| 2024 | ||
| 2025 | static inline int | |
| 2026 | sn_crypto_secretstream_xchacha20poly1305_init_push( | |
| 2027 | js_env_t *env, | |
| 2028 | js_receiver_t, | |
| 2029 | ||
| 2030 | js_arraybuffer_span_t state, | |
| 2031 | uint32_t state_offset, | |
| 2032 | uint32_t state_len, | |
| 2033 | ||
| 2034 | js_arraybuffer_span_t header, | |
| 2035 | uint32_t header_offset, | |
| 2036 | uint32_t header_len, | |
| 2037 | ||
| 2038 | js_arraybuffer_span_t k, | |
| 2039 | uint32_t k_offset, | |
| 2040 | uint32_t k_len | |
| 2041 | ) { | |
| 2042 | assert_bounds(state); | |
| 2043 | assert_bounds(header); | |
| 2044 | assert_bounds(k); | |
| 2045 | ||
| 2046 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2047 | assert(header_len == crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 2048 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2049 | ||
| 2050 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2051 | ||
| 2052 | return crypto_secretstream_xchacha20poly1305_init_push(state_data, &header[header_offset], &k[k_offset]); | |
| 2053 | } | |
| 2054 | ||
| 2055 | static inline uint64_t | |
| 2056 | sn_crypto_secretstream_xchacha20poly1305_push( | |
| 2057 | js_env_t *env, | |
| 2058 | js_receiver_t, | |
| 2059 | ||
| 2060 | js_arraybuffer_span_t state, | |
| 2061 | uint32_t state_offset, | |
| 2062 | uint32_t state_len, | |
| 2063 | ||
| 2064 | js_arraybuffer_span_t c, | |
| 2065 | uint32_t c_offset, | |
| 2066 | uint32_t c_len, | |
| 2067 | ||
| 2068 | js_arraybuffer_span_t m, | |
| 2069 | uint32_t m_offset, | |
| 2070 | uint32_t m_len, | |
| 2071 | ||
| 2072 | js_arraybuffer_span_t ad, | |
| 2073 | uint32_t ad_offset, | |
| 2074 | uint32_t ad_len, | |
| 2075 | ||
| 2076 | uint32_t tag | |
| 2077 | ) { | |
| 2078 | assert_bounds(state); | |
| 2079 | assert_bounds(c); | |
| 2080 | assert_bounds(m); | |
| 2081 | ||
| 2082 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2083 | assert(c_len == m_len + crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2084 | assert(c_len <= 0xffffffff && "32bit integer"); | |
| 2085 | ||
| 2086 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2087 | ||
| 2088 | uint8_t *ad_data = NULL; | |
| 2089 | if (ad_len) { | |
| 2090 | assert_bounds(ad); | |
| 2091 | ad_data = &ad[ad_offset]; | |
| 2092 | } | |
| 2093 | ||
| 2094 | unsigned long long clen = 0; | |
| 2095 | int status = crypto_secretstream_xchacha20poly1305_push( | |
| 2096 | state_data, | |
| 2097 | &c[c_offset], | |
| 2098 | &clen, | |
| 2099 | &m[m_offset], | |
| 2100 | m_len, | |
| 2101 | ad_data, | |
| 2102 | ad_len, | |
| 2103 | tag | |
| 2104 | ); | |
| 2105 | ||
| 2106 | if (status < 0) return status; | |
| 2107 | ||
| 2108 | return clen; | |
| 2109 | } | |
| 2110 | ||
| 2111 | static inline int | |
| 2112 | sn_crypto_secretstream_xchacha20poly1305_init_pull( | |
| 2113 | js_env_t *, | |
| 2114 | js_receiver_t, | |
| 2115 | ||
| 2116 | js_arraybuffer_span_t state, | |
| 2117 | uint32_t state_offset, | |
| 2118 | uint32_t state_len, | |
| 2119 | ||
| 2120 | js_arraybuffer_span_t header, | |
| 2121 | uint32_t header_offset, | |
| 2122 | uint32_t header_len, | |
| 2123 | ||
| 2124 | js_arraybuffer_span_t k, | |
| 2125 | uint32_t k_offset, | |
| 2126 | uint32_t k_len | |
| 2127 | ) { | |
| 2128 | assert_bounds(state); | |
| 2129 | assert_bounds(header); | |
| 2130 | assert_bounds(k); | |
| 2131 | ||
| 2132 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2133 | assert(header_len == crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 2134 | assert(k_len == crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 2135 | ||
| 2136 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2137 | ||
| 2138 | return crypto_secretstream_xchacha20poly1305_init_pull(state_data, &header[header_offset], &k[k_offset]); | |
| 2139 | } | |
| 2140 | ||
| 2141 | static inline uint64_t | |
| 2142 | sn_crypto_secretstream_xchacha20poly1305_pull( | |
| 2143 | js_env_t *env, | |
| 2144 | js_receiver_t, | |
| 2145 | ||
| 2146 | js_arraybuffer_span_t state, | |
| 2147 | uint32_t state_offset, | |
| 2148 | uint32_t state_len, | |
| 2149 | ||
| 2150 | js_arraybuffer_span_t m, | |
| 2151 | uint32_t m_offset, | |
| 2152 | uint32_t m_len, | |
| 2153 | ||
| 2154 | js_arraybuffer_span_t tag, | |
| 2155 | uint32_t tag_offset, | |
| 2156 | uint32_t tag_len, | |
| 2157 | ||
| 2158 | js_arraybuffer_span_t c, | |
| 2159 | uint32_t c_offset, | |
| 2160 | uint32_t c_len, | |
| 2161 | ||
| 2162 | js_arraybuffer_span_t ad, | |
| 2163 | uint32_t ad_offset, | |
| 2164 | uint32_t ad_len | |
| 2165 | ) { | |
| 2166 | assert_bounds(state); | |
| 2167 | assert_bounds(m); | |
| 2168 | assert_bounds(tag); | |
| 2169 | assert_bounds(c); | |
| 2170 | ||
| 2171 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2172 | assert(c_len >= crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2173 | assert(tag_len == 1); | |
| 2174 | assert(m_len == c_len - crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 2175 | assert(m_len <= 0xffffffff); | |
| 2176 | ||
| 2177 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2178 | ||
| 2179 | uint8_t *ad_data = NULL; | |
| 2180 | if (ad_len) { | |
| 2181 | assert_bounds(ad); | |
| 2182 | ad_data = &ad[ad_offset]; | |
| 2183 | } | |
| 2184 | ||
| 2185 | unsigned long long mlen = 0; | |
| 2186 | int status = crypto_secretstream_xchacha20poly1305_pull( | |
| 2187 | state_data, | |
| 2188 | &m[m_offset], | |
| 2189 | &mlen, | |
| 2190 | &tag[tag_offset], | |
| 2191 | &c[c_offset], | |
| 2192 | c_len, | |
| 2193 | ad_data, | |
| 2194 | ad_len | |
| 2195 | ); | |
| 2196 | ||
| 2197 | if (status < 0) return status; | |
| 2198 | ||
| 2199 | return mlen; | |
| 2200 | } | |
| 2201 | ||
| 2202 | static inline void | |
| 2203 | sn_crypto_secretstream_xchacha20poly1305_rekey( | |
| 2204 | js_env_t *, | |
| 2205 | js_receiver_t, | |
| 2206 | ||
| 2207 | js_arraybuffer_span_t state, | |
| 2208 | uint32_t state_offset, | |
| 2209 | uint32_t state_len | |
| 2210 | ) { | |
| 2211 | assert_bounds(state); | |
| 2212 | ||
| 2213 | assert(state_len == sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 2214 | ||
| 2215 | auto state_data = reinterpret_cast<crypto_secretstream_xchacha20poly1305_state *>(&state[state_offset]); | |
| 2216 | ||
| 2217 | crypto_secretstream_xchacha20poly1305_rekey(state_data); | |
| 2218 | } | |
| 2219 | ||
| 2220 | struct sn_async_task { | |
| 2221 | uv_work_t task; | |
| 2222 | js_env_t *env; | |
| 2223 | js_persistent_t<js_function_t<void, int>> cb; | |
| 2224 | js_deferred_teardown_t *teardown; | |
| 2225 | int code = 0; | |
| 2226 | bool exiting = false; | |
| 2227 | ||
| 2228 | sn_async_task(js_env_t *env) : env(env) { | |
| 2229 | int err; | |
| 2230 | err = js_add_teardown_callback<on_teardown, sn_async_task>(env, this, teardown); | |
| 2231 | assert(err == 0); | |
| 2232 | } | |
| 2233 | ||
| 2234 | ~sn_async_task() { | |
| 2235 | int err; | |
| 2236 | err = js_finish_teardown_callback(teardown); | |
| 2237 | assert(err == 0); | |
| 2238 | } | |
| 2239 | ||
| 2240 | private: | |
| 2241 | static void | |
| 2242 | on_teardown(js_deferred_teardown_t *teardown, sn_async_task *req) { | |
| 2243 | req->exiting = true; | |
| 2244 | } | |
| 2245 | }; | |
| 2246 | ||
| 2247 | struct sn_async_pwhash_request : sn_async_task { | |
| 2248 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2249 | std::span<uint8_t> out; | |
| 2250 | ||
| 2251 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2252 | std::span<char> pwd; | |
| 2253 | ||
| 2254 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 2255 | std::span<uint8_t> salt; | |
| 2256 | ||
| 2257 | uint64_t opslimit; | |
| 2258 | size_t memlimit; | |
| 2259 | int alg; | |
| 2260 | ||
| 2261 | sn_async_pwhash_request(js_env_t *env) : sn_async_task(env) {} | |
| 2262 | }; | |
| 2263 | ||
| 2264 | static void | |
| 2265 | async_pwhash_execute(uv_work_t *uv_req) { | |
| 2266 | auto req = reinterpret_cast<sn_async_pwhash_request *>(uv_req); | |
| 2267 | ||
| 2268 | req->code = crypto_pwhash( | |
| 2269 | req->out.data(), | |
| 2270 | req->out.size(), | |
| 2271 | req->pwd.data(), | |
| 2272 | req->pwd.size(), | |
| 2273 | req->salt.data(), | |
| 2274 | req->opslimit, | |
| 2275 | req->memlimit, | |
| 2276 | req->alg | |
| 2277 | ); | |
| 2278 | } | |
| 2279 | ||
| 2280 | static void | |
| 2281 | async_pwhash_complete(uv_work_t *uv_req, int status) { | |
| 2282 | int err; | |
| 2283 | ||
| 2284 | auto req = reinterpret_cast<sn_async_pwhash_request *>(uv_req); | |
| 2285 | ||
| 2286 | if (req->exiting) return delete req; | |
| 2287 | ||
| 2288 | js_handle_scope_t *scope; | |
| 2289 | err = js_open_handle_scope(req->env, &scope); | |
| 2290 | assert(err == 0); | |
| 2291 | ||
| 2292 | js_function_t<void, int> callback; | |
| 2293 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2294 | assert(err == 0); | |
| 2295 | ||
| 2296 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2297 | assert(err != js_pending_exception); | |
| 2298 | ||
| 2299 | err = js_close_handle_scope(req->env, scope); | |
| 2300 | assert(err == 0); | |
| 2301 | ||
| 2302 | delete req; | |
| 2303 | } | |
| 2304 | ||
| 2305 | static inline void | |
| 2306 | sn_crypto_pwhash_async( | |
| 2307 | js_env_t *env, | |
| 2308 | js_receiver_t, | |
| 2309 | ||
| 2310 | js_arraybuffer_t out, | |
| 2311 | uint32_t out_offset, | |
| 2312 | uint32_t out_len, | |
| 2313 | ||
| 2314 | js_arraybuffer_t pwd, | |
| 2315 | uint32_t pwd_offset, | |
| 2316 | uint32_t pwd_len, | |
| 2317 | ||
| 2318 | js_arraybuffer_t salt, | |
| 2319 | uint32_t salt_offset, | |
| 2320 | uint32_t salt_len, | |
| 2321 | ||
| 2322 | uint64_t opslimit, | |
| 2323 | uint64_t memlimit, | |
| 2324 | uint32_t alg, | |
| 2325 | ||
| 2326 | js_function_t<void, int> callback | |
| 2327 | ) { | |
| 2328 | int err; | |
| 2329 | ||
| 2330 | auto *req = new sn_async_pwhash_request(env); | |
| 2331 | ||
| 2332 | std::span<uint8_t> out_view; | |
| 2333 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2334 | assert(err == 0); | |
| 2335 | assert(out_offset + out_len <= out_view.size()); | |
| 2336 | ||
| 2337 | req->out = {&out_view[out_offset], out_len}; | |
| 2338 | ||
| 2339 | std::span<char> pwd_view; | |
| 2340 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2341 | assert(err == 0); | |
| 2342 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2343 | ||
| 2344 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2345 | ||
| 2346 | std::span<uint8_t> salt_view; | |
| 2347 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 2348 | assert(err == 0); | |
| 2349 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 2350 | ||
| 2351 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 2352 | ||
| 2353 | req->opslimit = opslimit; | |
| 2354 | req->memlimit = memlimit; | |
| 2355 | req->alg = alg; | |
| 2356 | ||
| 2357 | err = js_create_reference(env, out, req->out_ref); | |
| 2358 | assert(err == 0); | |
| 2359 | ||
| 2360 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2361 | assert(err == 0); | |
| 2362 | ||
| 2363 | err = js_create_reference(env, salt, req->salt_ref); | |
| 2364 | assert(err == 0); | |
| 2365 | ||
| 2366 | err = js_create_reference(env, callback, req->cb); | |
| 2367 | assert(err == 0); | |
| 2368 | ||
| 2369 | uv_loop_t *loop; | |
| 2370 | err = js_get_env_loop(env, &loop); | |
| 2371 | assert(err == 0); | |
| 2372 | ||
| 2373 | err = uv_queue_work(loop, &req->task, async_pwhash_execute, async_pwhash_complete); | |
| 2374 | assert(err == 0); | |
| 2375 | } | |
| 2376 | ||
| 2377 | struct sn_async_pwhash_str_request : sn_async_task { | |
| 2378 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2379 | std::span<char> out; | |
| 2380 | ||
| 2381 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2382 | std::span<char> pwd; | |
| 2383 | ||
| 2384 | uint64_t opslimit; | |
| 2385 | size_t memlimit; | |
| 2386 | ||
| 2387 | sn_async_pwhash_str_request(js_env_t *env) : sn_async_task(env) {} | |
| 2388 | }; | |
| 2389 | ||
| 2390 | static void | |
| 2391 | async_pwhash_str_execute(uv_work_t *uv_req) { | |
| 2392 | auto *req = reinterpret_cast<sn_async_pwhash_str_request *>(uv_req); | |
| 2393 | ||
| 2394 | req->code = crypto_pwhash_str( | |
| 2395 | req->out.data(), | |
| 2396 | req->pwd.data(), | |
| 2397 | req->pwd.size(), | |
| 2398 | req->opslimit, | |
| 2399 | req->memlimit | |
| 2400 | ); | |
| 2401 | } | |
| 2402 | ||
| 2403 | static void | |
| 2404 | async_pwhash_str_complete(uv_work_t *uv_req, int status) { | |
| 2405 | int err; | |
| 2406 | ||
| 2407 | auto *req = reinterpret_cast<sn_async_pwhash_str_request *>(uv_req); | |
| 2408 | ||
| 2409 | if (req->exiting) return delete req; | |
| 2410 | ||
| 2411 | js_handle_scope_t *scope; | |
| 2412 | err = js_open_handle_scope(req->env, &scope); | |
| 2413 | assert(err == 0); | |
| 2414 | ||
| 2415 | js_function_t<void, int> callback; | |
| 2416 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2417 | assert(err == 0); | |
| 2418 | ||
| 2419 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2420 | assert(err != js_pending_exception); | |
| 2421 | ||
| 2422 | err = js_close_handle_scope(req->env, scope); | |
| 2423 | assert(err == 0); | |
| 2424 | ||
| 2425 | delete req; | |
| 2426 | } | |
| 2427 | ||
| 2428 | static inline void | |
| 2429 | sn_crypto_pwhash_str_async( | |
| 2430 | js_env_t *env, | |
| 2431 | js_receiver_t, | |
| 2432 | ||
| 2433 | js_arraybuffer_t out, | |
| 2434 | uint32_t out_offset, | |
| 2435 | uint32_t out_len, | |
| 2436 | ||
| 2437 | js_arraybuffer_t pwd, | |
| 2438 | uint32_t pwd_offset, | |
| 2439 | uint32_t pwd_len, | |
| 2440 | ||
| 2441 | uint64_t opslimit, | |
| 2442 | uint64_t memlimit, | |
| 2443 | js_function_t<void, int> callback | |
| 2444 | ) { | |
| 2445 | int err; | |
| 2446 | ||
| 2447 | auto *req = new sn_async_pwhash_str_request(env); | |
| 2448 | ||
| 2449 | std::span<char> out_view; | |
| 2450 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2451 | assert(err == 0); | |
| 2452 | assert(out_offset + out_len <= out_view.size()); | |
| 2453 | ||
| 2454 | req->out = {&out_view[out_offset], out_len}; | |
| 2455 | ||
| 2456 | std::span<char> pwd_view; | |
| 2457 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2458 | assert(err == 0); | |
| 2459 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2460 | ||
| 2461 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2462 | ||
| 2463 | req->opslimit = opslimit; | |
| 2464 | req->memlimit = memlimit; | |
| 2465 | ||
| 2466 | err = js_create_reference(env, out, req->out_ref); | |
| 2467 | assert(err == 0); | |
| 2468 | ||
| 2469 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2470 | assert(err == 0); | |
| 2471 | ||
| 2472 | err = js_create_reference(env, callback, req->cb); | |
| 2473 | assert(err == 0); | |
| 2474 | ||
| 2475 | uv_loop_t *loop; | |
| 2476 | err = js_get_env_loop(env, &loop); | |
| 2477 | assert(err == 0); | |
| 2478 | ||
| 2479 | err = uv_queue_work(loop, &req->task, async_pwhash_str_execute, async_pwhash_str_complete); | |
| 2480 | assert(err == 0); | |
| 2481 | } | |
| 2482 | ||
| 2483 | struct sn_async_pwhash_str_verify_request : sn_async_task { | |
| 2484 | js_persistent_t<js_arraybuffer_t> str_ref; | |
| 2485 | std::span<char> str; | |
| 2486 | ||
| 2487 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2488 | std::span<char> pwd; | |
| 2489 | ||
| 2490 | sn_async_pwhash_str_verify_request(js_env_t *env) : sn_async_task(env) {} | |
| 2491 | }; | |
| 2492 | ||
| 2493 | static void | |
| 2494 | async_pwhash_str_verify_execute(uv_work_t *uv_req) { | |
| 2495 | auto *req = reinterpret_cast<sn_async_pwhash_str_verify_request *>(uv_req); | |
| 2496 | ||
| 2497 | req->code = crypto_pwhash_str_verify(req->str.data(), req->pwd.data(), req->pwd.size()); | |
| 2498 | } | |
| 2499 | ||
| 2500 | static void | |
| 2501 | async_pwhash_str_verify_complete(uv_work_t *uv_req, int status) { | |
| 2502 | int err; | |
| 2503 | ||
| 2504 | auto *req = reinterpret_cast<sn_async_pwhash_str_verify_request *>(uv_req); | |
| 2505 | ||
| 2506 | if (req->exiting) return delete req; | |
| 2507 | ||
| 2508 | js_handle_scope_t *scope; | |
| 2509 | err = js_open_handle_scope(req->env, &scope); | |
| 2510 | assert(err == 0); | |
| 2511 | ||
| 2512 | js_function_t<void, int> callback; | |
| 2513 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2514 | assert(err == 0); | |
| 2515 | ||
| 2516 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2517 | assert(err != js_pending_exception); | |
| 2518 | ||
| 2519 | err = js_close_handle_scope(req->env, scope); | |
| 2520 | assert(err == 0); | |
| 2521 | ||
| 2522 | delete req; | |
| 2523 | } | |
| 2524 | ||
| 2525 | static inline void | |
| 2526 | sn_crypto_pwhash_str_verify_async( | |
| 2527 | js_env_t *env, | |
| 2528 | js_receiver_t, | |
| 2529 | ||
| 2530 | js_arraybuffer_t str, | |
| 2531 | uint32_t str_offset, | |
| 2532 | uint32_t str_len, | |
| 2533 | ||
| 2534 | js_arraybuffer_t pwd, | |
| 2535 | uint32_t pwd_offset, | |
| 2536 | uint32_t pwd_len, | |
| 2537 | ||
| 2538 | js_function_t<void, int> callback | |
| 2539 | ) { | |
| 2540 | int err; | |
| 2541 | ||
| 2542 | auto *req = new sn_async_pwhash_str_verify_request(env); | |
| 2543 | ||
| 2544 | std::span<char> str_view; | |
| 2545 | err = js_get_arraybuffer_info(env, str, str_view); | |
| 2546 | assert(err == 0); | |
| 2547 | assert(str_offset + str_len <= str_view.size()); | |
| 2548 | ||
| 2549 | req->str = {&str_view[str_offset], str_len}; | |
| 2550 | ||
| 2551 | std::span<char> pwd_view; | |
| 2552 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2553 | assert(err == 0); | |
| 2554 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2555 | ||
| 2556 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2557 | ||
| 2558 | err = js_create_reference(env, str, req->str_ref); | |
| 2559 | assert(err == 0); | |
| 2560 | ||
| 2561 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2562 | assert(err == 0); | |
| 2563 | ||
| 2564 | err = js_create_reference(env, callback, req->cb); | |
| 2565 | assert(err == 0); | |
| 2566 | ||
| 2567 | uv_loop_t *loop; | |
| 2568 | err = js_get_env_loop(env, &loop); | |
| 2569 | assert(err == 0); | |
| 2570 | ||
| 2571 | err = uv_queue_work(loop, &req->task, async_pwhash_str_verify_execute, async_pwhash_str_verify_complete); | |
| 2572 | assert(err == 0); | |
| 2573 | } | |
| 2574 | ||
| 2575 | struct sn_async_pwhash_scryptsalsa208sha256_request : sn_async_task { | |
| 2576 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2577 | std::span<uint8_t> out; | |
| 2578 | ||
| 2579 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2580 | std::span<char> pwd; | |
| 2581 | ||
| 2582 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 2583 | std::span<uint8_t> salt; | |
| 2584 | ||
| 2585 | uint64_t opslimit; | |
| 2586 | size_t memlimit; | |
| 2587 | ||
| 2588 | sn_async_pwhash_scryptsalsa208sha256_request(js_env_t *env) : sn_async_task(env) {} | |
| 2589 | }; | |
| 2590 | ||
| 2591 | static void | |
| 2592 | async_pwhash_scryptsalsa208sha256_execute(uv_work_t *uv_req) { | |
| 2593 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_request *>(uv_req); | |
| 2594 | ||
| 2595 | req->code = crypto_pwhash_scryptsalsa208sha256( | |
| 2596 | req->out.data(), | |
| 2597 | req->out.size(), | |
| 2598 | req->pwd.data(), | |
| 2599 | req->pwd.size(), | |
| 2600 | req->salt.data(), | |
| 2601 | req->opslimit, | |
| 2602 | req->memlimit | |
| 2603 | ); | |
| 2604 | } | |
| 2605 | ||
| 2606 | static void | |
| 2607 | async_pwhash_scryptsalsa208sha256_complete(uv_work_t *uv_req, int status) { | |
| 2608 | int err; | |
| 2609 | ||
| 2610 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_request *>(uv_req); | |
| 2611 | ||
| 2612 | if (req->exiting) return delete req; | |
| 2613 | ||
| 2614 | js_handle_scope_t *scope; | |
| 2615 | err = js_open_handle_scope(req->env, &scope); | |
| 2616 | assert(err == 0); | |
| 2617 | ||
| 2618 | js_function_t<void, int> callback; | |
| 2619 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2620 | assert(err == 0); | |
| 2621 | ||
| 2622 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2623 | assert(err != js_pending_exception); | |
| 2624 | ||
| 2625 | err = js_close_handle_scope(req->env, scope); | |
| 2626 | assert(err == 0); | |
| 2627 | ||
| 2628 | delete req; | |
| 2629 | } | |
| 2630 | ||
| 2631 | static inline void | |
| 2632 | sn_crypto_pwhash_scryptsalsa208sha256_async( | |
| 2633 | js_env_t *env, | |
| 2634 | js_receiver_t, | |
| 2635 | ||
| 2636 | js_arraybuffer_t out, | |
| 2637 | uint32_t out_offset, | |
| 2638 | uint32_t out_len, | |
| 2639 | ||
| 2640 | js_arraybuffer_t pwd, | |
| 2641 | uint32_t pwd_offset, | |
| 2642 | uint32_t pwd_len, | |
| 2643 | ||
| 2644 | js_arraybuffer_t salt, | |
| 2645 | uint32_t salt_offset, | |
| 2646 | uint32_t salt_len, | |
| 2647 | ||
| 2648 | uint64_t opslimit, | |
| 2649 | uint64_t memlimit, | |
| 2650 | ||
| 2651 | js_function_t<void, int> callback | |
| 2652 | ) { | |
| 2653 | int err; | |
| 2654 | ||
| 2655 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_request(env); | |
| 2656 | ||
| 2657 | std::span<uint8_t> out_view; | |
| 2658 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2659 | assert(err == 0); | |
| 2660 | assert(out_offset + out_len <= out_view.size()); | |
| 2661 | ||
| 2662 | req->out = {&out_view[out_offset], out_len}; | |
| 2663 | ||
| 2664 | std::span<char> pwd_view; | |
| 2665 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2666 | assert(err == 0); | |
| 2667 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2668 | ||
| 2669 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2670 | ||
| 2671 | std::span<uint8_t> salt_view; | |
| 2672 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 2673 | assert(err == 0); | |
| 2674 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 2675 | assert(salt_len == crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 2676 | ||
| 2677 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 2678 | ||
| 2679 | req->opslimit = opslimit; | |
| 2680 | req->memlimit = memlimit; | |
| 2681 | ||
| 2682 | err = js_create_reference(env, out, req->out_ref); | |
| 2683 | assert(err == 0); | |
| 2684 | ||
| 2685 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2686 | assert(err == 0); | |
| 2687 | ||
| 2688 | err = js_create_reference(env, salt, req->salt_ref); | |
| 2689 | assert(err == 0); | |
| 2690 | ||
| 2691 | err = js_create_reference(env, callback, req->cb); | |
| 2692 | assert(err == 0); | |
| 2693 | ||
| 2694 | uv_loop_t *loop; | |
| 2695 | err = js_get_env_loop(env, &loop); | |
| 2696 | assert(err == 0); | |
| 2697 | ||
| 2698 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_execute, async_pwhash_scryptsalsa208sha256_complete); | |
| 2699 | assert(err == 0); | |
| 2700 | } | |
| 2701 | ||
| 2702 | struct sn_async_pwhash_scryptsalsa208sha256_str_request : sn_async_task { | |
| 2703 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 2704 | std::span<char> out; | |
| 2705 | ||
| 2706 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2707 | std::span<char> pwd; | |
| 2708 | ||
| 2709 | uint64_t opslimit; | |
| 2710 | size_t memlimit; | |
| 2711 | ||
| 2712 | sn_async_pwhash_scryptsalsa208sha256_str_request(js_env_t *env) : sn_async_task(env) {} | |
| 2713 | }; | |
| 2714 | ||
| 2715 | static void | |
| 2716 | async_pwhash_scryptsalsa208sha256_str_execute(uv_work_t *uv_req) { | |
| 2717 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_request *>(uv_req); | |
| 2718 | ||
| 2719 | req->code = crypto_pwhash_scryptsalsa208sha256_str( | |
| 2720 | req->out.data(), | |
| 2721 | req->pwd.data(), | |
| 2722 | req->pwd.size(), | |
| 2723 | req->opslimit, | |
| 2724 | req->memlimit | |
| 2725 | ); | |
| 2726 | } | |
| 2727 | ||
| 2728 | static void | |
| 2729 | async_pwhash_scryptsalsa208sha256_str_complete(uv_work_t *uv_req, int status) { | |
| 2730 | int err; | |
| 2731 | ||
| 2732 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_request *>(uv_req); | |
| 2733 | ||
| 2734 | if (req->exiting) return delete req; | |
| 2735 | ||
| 2736 | js_handle_scope_t *scope; | |
| 2737 | err = js_open_handle_scope(req->env, &scope); | |
| 2738 | assert(err == 0); | |
| 2739 | ||
| 2740 | js_function_t<void, int> callback; | |
| 2741 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2742 | assert(err == 0); | |
| 2743 | ||
| 2744 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2745 | assert(err != js_pending_exception); | |
| 2746 | ||
| 2747 | err = js_close_handle_scope(req->env, scope); | |
| 2748 | assert(err == 0); | |
| 2749 | ||
| 2750 | delete req; | |
| 2751 | } | |
| 2752 | ||
| 2753 | static inline void | |
| 2754 | sn_crypto_pwhash_scryptsalsa208sha256_str_async( | |
| 2755 | js_env_t *env, | |
| 2756 | js_receiver_t, | |
| 2757 | ||
| 2758 | js_arraybuffer_t out, | |
| 2759 | uint32_t out_offset, | |
| 2760 | uint32_t out_len, | |
| 2761 | ||
| 2762 | js_arraybuffer_t pwd, | |
| 2763 | uint32_t pwd_offset, | |
| 2764 | uint32_t pwd_len, | |
| 2765 | ||
| 2766 | uint64_t opslimit, | |
| 2767 | uint64_t memlimit, | |
| 2768 | ||
| 2769 | js_function_t<void, int> callback | |
| 2770 | ) { | |
| 2771 | int err; | |
| 2772 | ||
| 2773 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_str_request(env); | |
| 2774 | ||
| 2775 | std::span<char> out_view; | |
| 2776 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 2777 | assert(err == 0); | |
| 2778 | assert(out_offset + out_len <= out_view.size()); | |
| 2779 | ||
| 2780 | req->out = {&out_view[out_offset], out_len}; | |
| 2781 | ||
| 2782 | std::span<char> pwd_view; | |
| 2783 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2784 | assert(err == 0); | |
| 2785 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2786 | ||
| 2787 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2788 | ||
| 2789 | req->opslimit = opslimit; | |
| 2790 | req->memlimit = memlimit; | |
| 2791 | ||
| 2792 | err = js_create_reference(env, out, req->out_ref); | |
| 2793 | assert(err == 0); | |
| 2794 | ||
| 2795 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2796 | assert(err == 0); | |
| 2797 | ||
| 2798 | err = js_create_reference(env, callback, req->cb); | |
| 2799 | assert(err == 0); | |
| 2800 | ||
| 2801 | uv_loop_t *loop; | |
| 2802 | err = js_get_env_loop(env, &loop); | |
| 2803 | assert(err == 0); | |
| 2804 | ||
| 2805 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_str_execute, async_pwhash_scryptsalsa208sha256_str_complete); | |
| 2806 | assert(err == 0); | |
| 2807 | } | |
| 2808 | ||
| 2809 | struct sn_async_pwhash_scryptsalsa208sha256_str_verify_request : sn_async_task { | |
| 2810 | js_persistent_t<js_arraybuffer_t> str_ref; | |
| 2811 | std::span<char> str; | |
| 2812 | ||
| 2813 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 2814 | std::span<char> pwd; | |
| 2815 | ||
| 2816 | sn_async_pwhash_scryptsalsa208sha256_str_verify_request(js_env_t *env) : sn_async_task(env) {} | |
| 2817 | }; | |
| 2818 | ||
| 2819 | static void | |
| 2820 | async_pwhash_scryptsalsa208sha256_str_verify_execute(uv_work_t *uv_req) { | |
| 2821 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_verify_request *>(uv_req); | |
| 2822 | ||
| 2823 | req->code = crypto_pwhash_scryptsalsa208sha256_str_verify(req->str.data(), req->pwd.data(), req->pwd.size()); | |
| 2824 | } | |
| 2825 | ||
| 2826 | static void | |
| 2827 | async_pwhash_scryptsalsa208sha256_str_verify_complete(uv_work_t *uv_req, int status) { | |
| 2828 | int err; | |
| 2829 | ||
| 2830 | auto *req = reinterpret_cast<sn_async_pwhash_scryptsalsa208sha256_str_verify_request *>(uv_req); | |
| 2831 | ||
| 2832 | if (req->exiting) return delete req; | |
| 2833 | ||
| 2834 | js_handle_scope_t *scope; | |
| 2835 | err = js_open_handle_scope(req->env, &scope); | |
| 2836 | assert(err == 0); | |
| 2837 | ||
| 2838 | js_function_t<void, int> callback; | |
| 2839 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 2840 | assert(err == 0); | |
| 2841 | ||
| 2842 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 2843 | assert(err != js_pending_exception); | |
| 2844 | ||
| 2845 | err = js_close_handle_scope(req->env, scope); | |
| 2846 | assert(err == 0); | |
| 2847 | ||
| 2848 | delete req; | |
| 2849 | } | |
| 2850 | ||
| 2851 | static inline void | |
| 2852 | sn_crypto_pwhash_scryptsalsa208sha256_str_verify_async( | |
| 2853 | js_env_t *env, | |
| 2854 | js_receiver_t, | |
| 2855 | ||
| 2856 | js_arraybuffer_t str, | |
| 2857 | uint32_t str_offset, | |
| 2858 | uint32_t str_len, | |
| 2859 | ||
| 2860 | js_arraybuffer_t pwd, | |
| 2861 | uint32_t pwd_offset, | |
| 2862 | uint32_t pwd_len, | |
| 2863 | ||
| 2864 | js_function_t<void, int> callback | |
| 2865 | ) { | |
| 2866 | int err; | |
| 2867 | ||
| 2868 | auto *req = new sn_async_pwhash_scryptsalsa208sha256_str_verify_request(env); | |
| 2869 | ||
| 2870 | std::span<char> str_view; | |
| 2871 | err = js_get_arraybuffer_info(env, str, str_view); | |
| 2872 | assert(err == 0); | |
| 2873 | assert(str_offset + str_len <= str_view.size()); | |
| 2874 | ||
| 2875 | req->str = {&str_view[str_offset], str_len}; | |
| 2876 | ||
| 2877 | std::span<char> pwd_view; | |
| 2878 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 2879 | assert(err == 0); | |
| 2880 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 2881 | ||
| 2882 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 2883 | ||
| 2884 | err = js_create_reference(env, str, req->str_ref); | |
| 2885 | assert(err == 0); | |
| 2886 | ||
| 2887 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 2888 | assert(err == 0); | |
| 2889 | ||
| 2890 | err = js_create_reference(env, callback, req->cb); | |
| 2891 | assert(err == 0); | |
| 2892 | ||
| 2893 | uv_loop_t *loop; | |
| 2894 | err = js_get_env_loop(env, &loop); | |
| 2895 | assert(err == 0); | |
| 2896 | ||
| 2897 | err = uv_queue_work(loop, &req->task, async_pwhash_scryptsalsa208sha256_str_verify_execute, async_pwhash_scryptsalsa208sha256_str_verify_complete); | |
| 2898 | assert(err == 0); | |
| 2899 | } | |
| 2900 | ||
| 2901 | struct sn_crypto_stream_xor_state { | |
| 2902 | unsigned char n[crypto_stream_NONCEBYTES]; | |
| 2903 | unsigned char k[crypto_stream_KEYBYTES]; | |
| 2904 | unsigned char next_block[64]; | |
| 2905 | int remainder; | |
| 2906 | uint64_t block_counter; | |
| 2907 | }; | |
| 2908 | ||
| 2909 | static inline void | |
| 2910 | sn_crypto_stream_xor_wrap_init( | |
| 2911 | js_env_t *, | |
| 2912 | js_receiver_t, | |
| 2913 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state, | |
| 2914 | js_typedarray_span_t<> n, | |
| 2915 | js_typedarray_span_t<> k | |
| 2916 | ) { | |
| 2917 | assert(n.size_bytes() == crypto_stream_NONCEBYTES); | |
| 2918 | assert(k.size_bytes() == crypto_stream_KEYBYTES); | |
| 2919 | ||
| 2920 | state->remainder = 0; | |
| 2921 | state->block_counter = 0; | |
| 2922 | memcpy(state->n, n.data(), crypto_stream_NONCEBYTES); | |
| 2923 | memcpy(state->k, k.data(), crypto_stream_KEYBYTES); | |
| 2924 | } | |
| 2925 | ||
| 2926 | static inline void | |
| 2927 | sn_crypto_stream_xor_wrap_update( | |
| 2928 | js_env_t *, | |
| 2929 | js_receiver_t, | |
| 2930 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state, | |
| 2931 | js_typedarray_span_t<> c, | |
| 2932 | js_typedarray_span_t<> m | |
| 2933 | ) { | |
| 2934 | assert(c.size_bytes() == m.size_bytes()); | |
| 2935 | ||
| 2936 | auto next_block = state->next_block; | |
| 2937 | ||
| 2938 | size_t m_size = m.size_bytes(); | |
| 2939 | auto *c_ptr = c.data(); | |
| 2940 | auto *m_ptr = m.data(); | |
| 2941 | ||
| 2942 | if (state->remainder) { | |
| 2943 | uint64_t offset = 0; | |
| 2944 | int rem = state->remainder; | |
| 2945 | ||
| 2946 | while (rem < 64 && offset < m_size) { | |
| 2947 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 2948 | ++offset; | |
| 2949 | ++rem; | |
| 2950 | } | |
| 2951 | ||
| 2952 | c_ptr += offset; | |
| 2953 | m_ptr += offset; | |
| 2954 | m_size -= offset; | |
| 2955 | state->remainder = (rem == 64) ? 0 : rem; | |
| 2956 | ||
| 2957 | if (m_size == 0) return; | |
| 2958 | } | |
| 2959 | ||
| 2960 | state->remainder = m_size & 63; | |
| 2961 | size_t main_len = m_size - state->remainder; | |
| 2962 | ||
| 2963 | crypto_stream_xsalsa20_xor_ic(c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k); | |
| 2964 | state->block_counter += main_len / 64; | |
| 2965 | ||
| 2966 | if (state->remainder) { | |
| 2967 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 2968 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 2969 | ||
| 2970 | crypto_stream_xsalsa20_xor_ic( | |
| 2971 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 2972 | ); | |
| 2973 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 2974 | ||
| 2975 | state->block_counter++; | |
| 2976 | } | |
| 2977 | } | |
| 2978 | ||
| 2979 | static inline void | |
| 2980 | sn_crypto_stream_xor_wrap_final( | |
| 2981 | js_env_t *, | |
| 2982 | js_receiver_t, | |
| 2983 | js_typedarray_span_of_t<sn_crypto_stream_xor_state, 1> state | |
| 2984 | ) { | |
| 2985 | sodium_memzero(state->n, sizeof(state->n)); | |
| 2986 | sodium_memzero(state->k, sizeof(state->k)); | |
| 2987 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 2988 | state->remainder = 0; | |
| 2989 | } | |
| 2990 | ||
| 2991 | struct sn_crypto_stream_chacha20_xor_state { | |
| 2992 | unsigned char n[crypto_stream_chacha20_NONCEBYTES]; | |
| 2993 | unsigned char k[crypto_stream_chacha20_KEYBYTES]; | |
| 2994 | unsigned char next_block[64]; | |
| 2995 | int remainder; | |
| 2996 | uint64_t block_counter; | |
| 2997 | }; | |
| 2998 | ||
| 2999 | static inline void | |
| 3000 | sn_crypto_stream_chacha20_xor_wrap_init( | |
| 3001 | js_env_t *, | |
| 3002 | js_receiver_t, | |
| 3003 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state, | |
| 3004 | js_typedarray_span_t<> n, | |
| 3005 | js_typedarray_span_t<> k | |
| 3006 | ) { | |
| 3007 | assert(n.size_bytes() == crypto_stream_chacha20_NONCEBYTES); | |
| 3008 | assert(k.size_bytes() == crypto_stream_chacha20_KEYBYTES); | |
| 3009 | ||
| 3010 | state->remainder = 0; | |
| 3011 | state->block_counter = 0; | |
| 3012 | memcpy(state->n, n.data(), crypto_stream_chacha20_NONCEBYTES); | |
| 3013 | memcpy(state->k, k.data(), crypto_stream_chacha20_KEYBYTES); | |
| 3014 | } | |
| 3015 | ||
| 3016 | static inline void | |
| 3017 | sn_crypto_stream_chacha20_xor_wrap_update( | |
| 3018 | js_env_t *, | |
| 3019 | js_receiver_t, | |
| 3020 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state, | |
| 3021 | js_typedarray_span_t<> c, | |
| 3022 | js_typedarray_span_t<> m | |
| 3023 | ) { | |
| 3024 | assert(c.size_bytes() == m.size_bytes()); | |
| 3025 | ||
| 3026 | auto *next_block = state->next_block; | |
| 3027 | ||
| 3028 | size_t m_size = m.size_bytes(); | |
| 3029 | auto *c_ptr = c.data(); | |
| 3030 | auto *m_ptr = m.data(); | |
| 3031 | ||
| 3032 | if (state->remainder) { | |
| 3033 | uint64_t offset = 0; | |
| 3034 | int rem = state->remainder; | |
| 3035 | ||
| 3036 | while (rem < 64 && offset < m_size) { | |
| 3037 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3038 | offset++; | |
| 3039 | rem++; | |
| 3040 | } | |
| 3041 | ||
| 3042 | c_ptr += offset; | |
| 3043 | m_ptr += offset; | |
| 3044 | m_size -= offset; | |
| 3045 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3046 | ||
| 3047 | if (m_size == 0) return; | |
| 3048 | } | |
| 3049 | ||
| 3050 | state->remainder = m_size & 63; | |
| 3051 | size_t main_len = m_size - state->remainder; | |
| 3052 | ||
| 3053 | crypto_stream_chacha20_xor_ic( | |
| 3054 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3055 | ); | |
| 3056 | ||
| 3057 | state->block_counter += main_len / 64; | |
| 3058 | ||
| 3059 | if (state->remainder) { | |
| 3060 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3061 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3062 | ||
| 3063 | crypto_stream_chacha20_xor_ic( | |
| 3064 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3065 | ); | |
| 3066 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3067 | ||
| 3068 | state->block_counter++; | |
| 3069 | } | |
| 3070 | } | |
| 3071 | ||
| 3072 | static inline void | |
| 3073 | sn_crypto_stream_chacha20_xor_wrap_final( | |
| 3074 | js_env_t *, | |
| 3075 | js_receiver_t, | |
| 3076 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_xor_state, 1> state | |
| 3077 | ) { | |
| 3078 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3079 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3080 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3081 | state->remainder = 0; | |
| 3082 | } | |
| 3083 | ||
| 3084 | struct sn_crypto_stream_chacha20_ietf_xor_state { | |
| 3085 | unsigned char n[crypto_stream_chacha20_ietf_NONCEBYTES]; | |
| 3086 | unsigned char k[crypto_stream_chacha20_ietf_KEYBYTES]; | |
| 3087 | unsigned char next_block[64]; | |
| 3088 | int remainder; | |
| 3089 | uint64_t block_counter; | |
| 3090 | }; | |
| 3091 | ||
| 3092 | static inline void | |
| 3093 | sn_crypto_stream_chacha20_ietf_xor_wrap_init( | |
| 3094 | js_env_t *, | |
| 3095 | js_receiver_t, | |
| 3096 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state, | |
| 3097 | js_typedarray_span_t<> n, | |
| 3098 | js_typedarray_span_t<> k | |
| 3099 | ) { | |
| 3100 | assert(n.size_bytes() == crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 3101 | assert(k.size_bytes() == crypto_stream_chacha20_ietf_KEYBYTES); | |
| 3102 | ||
| 3103 | state->remainder = 0; | |
| 3104 | state->block_counter = 0; | |
| 3105 | memcpy(state->n, n.data(), crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 3106 | memcpy(state->k, k.data(), crypto_stream_chacha20_ietf_KEYBYTES); | |
| 3107 | } | |
| 3108 | ||
| 3109 | static inline void | |
| 3110 | sn_crypto_stream_chacha20_ietf_xor_wrap_update( | |
| 3111 | js_env_t *, | |
| 3112 | js_receiver_t, | |
| 3113 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state, | |
| 3114 | js_typedarray_span_t<> c, | |
| 3115 | js_typedarray_span_t<> m | |
| 3116 | ) { | |
| 3117 | assert(c.size_bytes() == m.size_bytes()); | |
| 3118 | ||
| 3119 | auto *next_block = state->next_block; | |
| 3120 | ||
| 3121 | size_t m_size = m.size_bytes(); | |
| 3122 | auto *c_ptr = c.data(); | |
| 3123 | auto *m_ptr = m.data(); | |
| 3124 | ||
| 3125 | if (state->remainder) { | |
| 3126 | uint64_t offset = 0; | |
| 3127 | int rem = state->remainder; | |
| 3128 | ||
| 3129 | while (rem < 64 && offset < m_size) { | |
| 3130 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3131 | offset++; | |
| 3132 | rem++; | |
| 3133 | } | |
| 3134 | ||
| 3135 | c_ptr += offset; | |
| 3136 | m_ptr += offset; | |
| 3137 | m_size -= offset; | |
| 3138 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3139 | ||
| 3140 | if (m_size == 0) return; | |
| 3141 | } | |
| 3142 | ||
| 3143 | state->remainder = m_size & 63; | |
| 3144 | size_t main_len = m_size - state->remainder; | |
| 3145 | ||
| 3146 | crypto_stream_chacha20_ietf_xor_ic( | |
| 3147 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3148 | ); | |
| 3149 | ||
| 3150 | state->block_counter += main_len / 64; | |
| 3151 | ||
| 3152 | if (state->remainder) { | |
| 3153 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3154 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3155 | ||
| 3156 | crypto_stream_chacha20_ietf_xor_ic( | |
| 3157 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3158 | ); | |
| 3159 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3160 | ||
| 3161 | state->block_counter++; | |
| 3162 | } | |
| 3163 | } | |
| 3164 | ||
| 3165 | static inline void | |
| 3166 | sn_crypto_stream_chacha20_ietf_xor_wrap_final( | |
| 3167 | js_env_t *, | |
| 3168 | js_receiver_t, | |
| 3169 | js_typedarray_span_of_t<sn_crypto_stream_chacha20_ietf_xor_state, 1> state | |
| 3170 | ) { | |
| 3171 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3172 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3173 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3174 | state->remainder = 0; | |
| 3175 | } | |
| 3176 | ||
| 3177 | struct sn_crypto_stream_xchacha20_xor_state { | |
| 3178 | unsigned char n[crypto_stream_xchacha20_NONCEBYTES]; | |
| 3179 | unsigned char k[crypto_stream_xchacha20_KEYBYTES]; | |
| 3180 | unsigned char next_block[64]; | |
| 3181 | int remainder; | |
| 3182 | uint64_t block_counter; | |
| 3183 | }; | |
| 3184 | ||
| 3185 | static inline void | |
| 3186 | sn_crypto_stream_xchacha20_xor_wrap_init( | |
| 3187 | js_env_t *, | |
| 3188 | js_receiver_t, | |
| 3189 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state, | |
| 3190 | js_typedarray_span_t<> n, | |
| 3191 | js_typedarray_span_t<> k | |
| 3192 | ) { | |
| 3193 | assert(n.size_bytes() == crypto_stream_xchacha20_NONCEBYTES); | |
| 3194 | assert(k.size_bytes() == crypto_stream_xchacha20_KEYBYTES); | |
| 3195 | ||
| 3196 | state->remainder = 0; | |
| 3197 | state->block_counter = 0; | |
| 3198 | memcpy(state->n, n.data(), crypto_stream_xchacha20_NONCEBYTES); | |
| 3199 | memcpy(state->k, k.data(), crypto_stream_xchacha20_KEYBYTES); | |
| 3200 | } | |
| 3201 | ||
| 3202 | static inline void | |
| 3203 | sn_crypto_stream_xchacha20_xor_wrap_update( | |
| 3204 | js_env_t *, | |
| 3205 | js_receiver_t, | |
| 3206 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state, | |
| 3207 | js_typedarray_span_t<> c, | |
| 3208 | js_typedarray_span_t<> m | |
| 3209 | ) { | |
| 3210 | assert(c.size_bytes() == m.size_bytes()); | |
| 3211 | ||
| 3212 | auto *next_block = state->next_block; | |
| 3213 | ||
| 3214 | size_t m_size = m.size_bytes(); | |
| 3215 | auto *c_ptr = c.data(); | |
| 3216 | auto *m_ptr = m.data(); | |
| 3217 | ||
| 3218 | if (state->remainder) { | |
| 3219 | uint64_t offset = 0; | |
| 3220 | int rem = state->remainder; | |
| 3221 | ||
| 3222 | while (rem < 64 && offset < m_size) { | |
| 3223 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3224 | ++offset; | |
| 3225 | ++rem; | |
| 3226 | } | |
| 3227 | ||
| 3228 | c_ptr += offset; | |
| 3229 | m_ptr += offset; | |
| 3230 | m_size -= offset; | |
| 3231 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3232 | ||
| 3233 | if (m_size == 0) return; | |
| 3234 | } | |
| 3235 | ||
| 3236 | state->remainder = m_size & 63; | |
| 3237 | size_t main_len = m_size - state->remainder; | |
| 3238 | ||
| 3239 | crypto_stream_xchacha20_xor_ic( | |
| 3240 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3241 | ); | |
| 3242 | ||
| 3243 | state->block_counter += main_len / 64; | |
| 3244 | ||
| 3245 | if (state->remainder) { | |
| 3246 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3247 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3248 | ||
| 3249 | crypto_stream_xchacha20_xor_ic( | |
| 3250 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3251 | ); | |
| 3252 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3253 | ||
| 3254 | state->block_counter++; | |
| 3255 | } | |
| 3256 | } | |
| 3257 | ||
| 3258 | static inline void | |
| 3259 | sn_crypto_stream_xchacha20_xor_wrap_final( | |
| 3260 | js_env_t *, | |
| 3261 | js_receiver_t, | |
| 3262 | js_typedarray_span_of_t<sn_crypto_stream_xchacha20_xor_state, 1> state | |
| 3263 | ) { | |
| 3264 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3265 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3266 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3267 | state->remainder = 0; | |
| 3268 | } | |
| 3269 | ||
| 3270 | struct sn_crypto_stream_salsa20_xor_state { | |
| 3271 | unsigned char n[crypto_stream_salsa20_NONCEBYTES]; | |
| 3272 | unsigned char k[crypto_stream_salsa20_KEYBYTES]; | |
| 3273 | unsigned char next_block[64]; | |
| 3274 | int remainder; | |
| 3275 | uint64_t block_counter; | |
| 3276 | }; | |
| 3277 | ||
| 3278 | static inline void | |
| 3279 | sn_crypto_stream_salsa20_xor_wrap_init( | |
| 3280 | js_env_t *, | |
| 3281 | js_receiver_t, | |
| 3282 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state, | |
| 3283 | js_typedarray_span_t<> n, | |
| 3284 | js_typedarray_span_t<> k | |
| 3285 | ) { | |
| 3286 | assert(n.size_bytes() == crypto_stream_salsa20_NONCEBYTES); | |
| 3287 | assert(k.size_bytes() == crypto_stream_salsa20_KEYBYTES); | |
| 3288 | ||
| 3289 | state->remainder = 0; | |
| 3290 | state->block_counter = 0; | |
| 3291 | memcpy(state->n, n.data(), crypto_stream_salsa20_NONCEBYTES); | |
| 3292 | memcpy(state->k, k.data(), crypto_stream_salsa20_KEYBYTES); | |
| 3293 | } | |
| 3294 | ||
| 3295 | static inline void | |
| 3296 | sn_crypto_stream_salsa20_xor_wrap_update( | |
| 3297 | js_env_t *, | |
| 3298 | js_receiver_t, | |
| 3299 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state, | |
| 3300 | js_typedarray_span_t<> c, | |
| 3301 | js_typedarray_span_t<> m | |
| 3302 | ) { | |
| 3303 | assert(c.size_bytes() == m.size_bytes()); | |
| 3304 | ||
| 3305 | auto *next_block = state->next_block; | |
| 3306 | ||
| 3307 | size_t m_size = m.size_bytes(); | |
| 3308 | auto *c_ptr = c.data(); | |
| 3309 | auto *m_ptr = m.data(); | |
| 3310 | ||
| 3311 | if (state->remainder) { | |
| 3312 | uint64_t offset = 0; | |
| 3313 | int rem = state->remainder; | |
| 3314 | ||
| 3315 | while (rem < 64 && offset < m_size) { | |
| 3316 | c_ptr[offset] = next_block[rem] ^ m_ptr[offset]; | |
| 3317 | ++offset; | |
| 3318 | ++rem; | |
| 3319 | } | |
| 3320 | ||
| 3321 | c_ptr += offset; | |
| 3322 | m_ptr += offset; | |
| 3323 | m_size -= offset; | |
| 3324 | state->remainder = (rem == 64) ? 0 : rem; | |
| 3325 | ||
| 3326 | if (m_size == 0) return; | |
| 3327 | } | |
| 3328 | ||
| 3329 | state->remainder = m_size & 63; | |
| 3330 | size_t main_len = m_size - state->remainder; | |
| 3331 | ||
| 3332 | crypto_stream_salsa20_xor_ic( | |
| 3333 | c_ptr, m_ptr, main_len, state->n, state->block_counter, state->k | |
| 3334 | ); | |
| 3335 | ||
| 3336 | state->block_counter += main_len / 64; | |
| 3337 | ||
| 3338 | if (state->remainder) { | |
| 3339 | sodium_memzero(next_block + state->remainder, 64 - state->remainder); | |
| 3340 | memcpy(next_block, m_ptr + main_len, state->remainder); | |
| 3341 | ||
| 3342 | crypto_stream_salsa20_xor_ic( | |
| 3343 | next_block, next_block, 64, state->n, state->block_counter, state->k | |
| 3344 | ); | |
| 3345 | memcpy(c_ptr + main_len, next_block, state->remainder); | |
| 3346 | ||
| 3347 | state->block_counter++; | |
| 3348 | } | |
| 3349 | } | |
| 3350 | ||
| 3351 | static inline void | |
| 3352 | sn_crypto_stream_salsa20_xor_wrap_final( | |
| 3353 | js_env_t *, | |
| 3354 | js_receiver_t, | |
| 3355 | js_typedarray_span_of_t<sn_crypto_stream_salsa20_xor_state, 1> state | |
| 3356 | ) { | |
| 3357 | sodium_memzero(state->n, sizeof(state->n)); | |
| 3358 | sodium_memzero(state->k, sizeof(state->k)); | |
| 3359 | sodium_memzero(state->next_block, sizeof(state->next_block)); | |
| 3360 | state->remainder = 0; | |
| 3361 | } | |
| 3362 | ||
| 3363 | // Experimental API | |
| 3364 | ||
| 3365 | static inline void | |
| 3366 | sn_extension_tweak_ed25519_base( | |
| 3367 | js_env_t *, | |
| 3368 | js_receiver_t, | |
| 3369 | js_typedarray_span_t<> n, | |
| 3370 | js_typedarray_span_t<> p, | |
| 3371 | js_typedarray_span_t<> ns | |
| 3372 | ) { | |
| 3373 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3374 | assert(p.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3375 | ||
| 3376 | sn__extension_tweak_ed25519_base(p.data(), n.data(), ns.data(), ns.size_bytes()); | |
| 3377 | } | |
| 3378 | ||
| 3379 | static inline int | |
| 3380 | sn_extension_tweak_ed25519_sign_detached( | |
| 3381 | js_env_t *, | |
| 3382 | js_receiver_t, | |
| 3383 | js_typedarray_span_t<> sig, | |
| 3384 | js_typedarray_span_t<> m, | |
| 3385 | js_typedarray_span_t<> scalar, | |
| 3386 | std::optional<js_typedarray_span_t<>> pk | |
| 3387 | ) { | |
| 3388 | assert(sig.size_bytes() == crypto_sign_BYTES); | |
| 3389 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3390 | ||
| 3391 | uint8_t *pk_data = nullptr; | |
| 3392 | if (pk) { | |
| 3393 | assert(pk->size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3394 | pk_data = pk->data(); | |
| 3395 | } | |
| 3396 | ||
| 3397 | return sn__extension_tweak_ed25519_sign_detached( | |
| 3398 | sig.data(), | |
| 3399 | nullptr, | |
| 3400 | m.data(), | |
| 3401 | m.size_bytes(), | |
| 3402 | scalar.data(), | |
| 3403 | pk_data | |
| 3404 | ); | |
| 3405 | } | |
| 3406 | ||
| 3407 | static inline void | |
| 3408 | sn_extension_tweak_ed25519_sk_to_scalar( | |
| 3409 | js_env_t *, | |
| 3410 | js_receiver_t, | |
| 3411 | js_typedarray_span_t<> n, | |
| 3412 | js_typedarray_span_t<> sk | |
| 3413 | ) { | |
| 3414 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3415 | assert(sk.size_bytes() == crypto_sign_SECRETKEYBYTES); | |
| 3416 | ||
| 3417 | sn__extension_tweak_ed25519_sk_to_scalar(n.data(), sk.data()); | |
| 3418 | } | |
| 3419 | ||
| 3420 | static inline void | |
| 3421 | sn_extension_tweak_ed25519_scalar( | |
| 3422 | js_env_t *, | |
| 3423 | js_receiver_t, | |
| 3424 | js_typedarray_span_t<> scalar_out, | |
| 3425 | js_typedarray_span_t<> scalar, | |
| 3426 | js_typedarray_span_t<> ns | |
| 3427 | ) { | |
| 3428 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3429 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3430 | ||
| 3431 | sn__extension_tweak_ed25519_scalar(scalar_out.data(), scalar.data(), ns.data(), ns.size_bytes()); | |
| 3432 | } | |
| 3433 | ||
| 3434 | static inline int | |
| 3435 | sn_extension_tweak_ed25519_pk( | |
| 3436 | js_env_t *, | |
| 3437 | js_receiver_t, | |
| 3438 | js_typedarray_span_t<> tpk, | |
| 3439 | js_typedarray_span_t<> pk, | |
| 3440 | js_typedarray_span_t<> ns | |
| 3441 | ) { | |
| 3442 | assert(tpk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3443 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3444 | ||
| 3445 | return sn__extension_tweak_ed25519_pk(tpk.data(), pk.data(), ns.data(), ns.size_bytes()); | |
| 3446 | } | |
| 3447 | ||
| 3448 | static inline void | |
| 3449 | sn_extension_tweak_ed25519_keypair( | |
| 3450 | js_env_t *, | |
| 3451 | js_receiver_t, | |
| 3452 | js_typedarray_span_t<> pk, | |
| 3453 | js_typedarray_span_t<> scalar_out, | |
| 3454 | js_typedarray_span_t<> scalar_in, | |
| 3455 | js_typedarray_span_t<> ns | |
| 3456 | ) { | |
| 3457 | assert(pk.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3458 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3459 | assert(scalar_in.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3460 | ||
| 3461 | sn__extension_tweak_ed25519_keypair( | |
| 3462 | pk.data(), | |
| 3463 | scalar_out.data(), | |
| 3464 | scalar_in.data(), | |
| 3465 | ns.data(), | |
| 3466 | ns.size_bytes() | |
| 3467 | ); | |
| 3468 | } | |
| 3469 | ||
| 3470 | static inline void | |
| 3471 | sn_extension_tweak_ed25519_scalar_add( | |
| 3472 | js_env_t *, | |
| 3473 | js_receiver_t, | |
| 3474 | js_typedarray_span_t<> scalar_out, | |
| 3475 | js_typedarray_span_t<> scalar, | |
| 3476 | js_typedarray_span_t<> n | |
| 3477 | ) { | |
| 3478 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3479 | assert(scalar.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3480 | assert(n.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3481 | ||
| 3482 | sn__extension_tweak_ed25519_scalar_add(scalar_out.data(), scalar.data(), n.data()); | |
| 3483 | } | |
| 3484 | ||
| 3485 | static inline int | |
| 3486 | sn_extension_tweak_ed25519_pk_add( | |
| 3487 | js_env_t *, | |
| 3488 | js_receiver_t, | |
| 3489 | js_typedarray_span_t<> tpk, | |
| 3490 | js_typedarray_span_t<> pk, | |
| 3491 | js_typedarray_span_t<> p | |
| 3492 | ) { | |
| 3493 | assert(tpk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3494 | assert(pk.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3495 | assert(p.size_bytes() == crypto_sign_PUBLICKEYBYTES); | |
| 3496 | ||
| 3497 | return sn__extension_tweak_ed25519_pk_add(tpk.data(), pk.data(), p.data()); | |
| 3498 | } | |
| 3499 | ||
| 3500 | static inline int | |
| 3501 | sn_extension_tweak_ed25519_keypair_add( | |
| 3502 | js_env_t *, | |
| 3503 | js_receiver_t, | |
| 3504 | js_typedarray_span_t<> pk, | |
| 3505 | js_typedarray_span_t<> scalar_out, | |
| 3506 | js_typedarray_span_t<> scalar_in, | |
| 3507 | js_typedarray_span_t<> tweak | |
| 3508 | ) { | |
| 3509 | assert(pk.size_bytes() == sn__extension_tweak_ed25519_BYTES); | |
| 3510 | assert(scalar_out.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3511 | assert(scalar_in.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3512 | assert(tweak.size_bytes() == sn__extension_tweak_ed25519_SCALARBYTES); | |
| 3513 | ||
| 3514 | return sn__extension_tweak_ed25519_keypair_add( | |
| 3515 | pk.data(), | |
| 3516 | scalar_out.data(), | |
| 3517 | scalar_in.data(), | |
| 3518 | tweak.data() | |
| 3519 | ); | |
| 3520 | } | |
| 3521 | ||
| 3522 | static inline int | |
| 3523 | sn_extension_pbkdf2_sha512( | |
| 3524 | js_env_t *, | |
| 3525 | js_receiver_t, | |
| 3526 | js_typedarray_span_t<> out, | |
| 3527 | js_typedarray_span_t<> passwd, | |
| 3528 | js_typedarray_span_t<> salt, | |
| 3529 | uint64_t iter, | |
| 3530 | uint64_t outlen | |
| 3531 | ) { | |
| 3532 | ||
| 3533 | assert(iter >= sn__extension_pbkdf2_sha512_ITERATIONS_MIN); | |
| 3534 | assert(outlen <= sn__extension_pbkdf2_sha512_BYTES_MAX); | |
| 3535 | assert(out.size_bytes() >= static_cast<size_t>(outlen)); | |
| 3536 | ||
| 3537 | return sn__extension_pbkdf2_sha512( | |
| 3538 | passwd.data(), | |
| 3539 | passwd.size_bytes(), | |
| 3540 | salt.data(), | |
| 3541 | salt.size_bytes(), | |
| 3542 | iter, | |
| 3543 | out.data(), | |
| 3544 | outlen | |
| 3545 | ); | |
| 3546 | } | |
| 3547 | ||
| 3548 | struct sn_async_pbkdf2_sha512_request : sn_async_task { | |
| 3549 | js_persistent_t<js_arraybuffer_t> out_ref; | |
| 3550 | std::span<uint8_t> out; | |
| 3551 | ||
| 3552 | size_t outlen; | |
| 3553 | ||
| 3554 | js_persistent_t<js_arraybuffer_t> pwd_ref; | |
| 3555 | std::span<uint8_t> pwd; | |
| 3556 | ||
| 3557 | js_persistent_t<js_arraybuffer_t> salt_ref; | |
| 3558 | std::span<uint8_t> salt; | |
| 3559 | ||
| 3560 | uint64_t iter; | |
| 3561 | ||
| 3562 | sn_async_pbkdf2_sha512_request(js_env_t *env) : sn_async_task(env) {} | |
| 3563 | }; | |
| 3564 | ||
| 3565 | static void | |
| 3566 | async_pbkdf2_sha512_execute(uv_work_t *uv_req) { | |
| 3567 | auto *req = reinterpret_cast<sn_async_pbkdf2_sha512_request *>(uv_req); | |
| 3568 | ||
| 3569 | req->code = sn__extension_pbkdf2_sha512( | |
| 3570 | req->pwd.data(), | |
| 3571 | req->pwd.size(), | |
| 3572 | req->salt.data(), | |
| 3573 | req->salt.size(), | |
| 3574 | req->iter, | |
| 3575 | req->out.data(), | |
| 3576 | req->outlen | |
| 3577 | ); | |
| 3578 | } | |
| 3579 | ||
| 3580 | static void | |
| 3581 | async_pbkdf2_sha512_complete(uv_work_t *uv_req, int status) { | |
| 3582 | int err; | |
| 3583 | ||
| 3584 | auto *req = reinterpret_cast<sn_async_pbkdf2_sha512_request *>(uv_req); | |
| 3585 | ||
| 3586 | if (req->exiting) return delete req; | |
| 3587 | ||
| 3588 | js_handle_scope_t *scope; | |
| 3589 | err = js_open_handle_scope(req->env, &scope); | |
| 3590 | assert(err == 0); | |
| 3591 | ||
| 3592 | js_function_t<void, int> callback; | |
| 3593 | err = js_get_reference_value(req->env, req->cb, callback); | |
| 3594 | assert(err == 0); | |
| 3595 | ||
| 3596 | err = js_call_function_with_checkpoint(req->env, callback, req->code); | |
| 3597 | assert(err != js_pending_exception); | |
| 3598 | ||
| 3599 | err = js_close_handle_scope(req->env, scope); | |
| 3600 | assert(err == 0); | |
| 3601 | ||
| 3602 | delete req; | |
| 3603 | } | |
| 3604 | ||
| 3605 | static inline void | |
| 3606 | sn_extension_pbkdf2_sha512_async( | |
| 3607 | js_env_t *env, | |
| 3608 | js_receiver_t, | |
| 3609 | ||
| 3610 | js_arraybuffer_t out, | |
| 3611 | uint32_t out_offset, | |
| 3612 | uint32_t out_len, | |
| 3613 | ||
| 3614 | js_arraybuffer_t pwd, | |
| 3615 | uint32_t pwd_offset, | |
| 3616 | uint32_t pwd_len, | |
| 3617 | ||
| 3618 | js_arraybuffer_t salt, | |
| 3619 | uint32_t salt_offset, | |
| 3620 | uint32_t salt_len, | |
| 3621 | ||
| 3622 | uint64_t iter, | |
| 3623 | uint64_t outlen, | |
| 3624 | ||
| 3625 | js_function_t<void, int> callback | |
| 3626 | ) { | |
| 3627 | int err; | |
| 3628 | ||
| 3629 | auto *req = new sn_async_pbkdf2_sha512_request(env); | |
| 3630 | ||
| 3631 | std::span<uint8_t> out_view; | |
| 3632 | err = js_get_arraybuffer_info(env, out, out_view); | |
| 3633 | assert(err == 0); | |
| 3634 | assert(out_offset + out_len <= out_view.size()); | |
| 3635 | ||
| 3636 | req->out = {&out_view[out_offset], out_len}; | |
| 3637 | ||
| 3638 | std::span<uint8_t> pwd_view; | |
| 3639 | err = js_get_arraybuffer_info(env, pwd, pwd_view); | |
| 3640 | assert(err == 0); | |
| 3641 | assert(pwd_offset + pwd_len <= pwd_view.size()); | |
| 3642 | ||
| 3643 | req->pwd = {&pwd_view[pwd_offset], pwd_len}; | |
| 3644 | ||
| 3645 | std::span<uint8_t> salt_view; | |
| 3646 | err = js_get_arraybuffer_info(env, salt, salt_view); | |
| 3647 | assert(err == 0); | |
| 3648 | assert(salt_offset + salt_len <= salt_view.size()); | |
| 3649 | ||
| 3650 | req->salt = {&salt_view[salt_offset], salt_len}; | |
| 3651 | ||
| 3652 | req->iter = iter; | |
| 3653 | req->outlen = outlen; | |
| 3654 | ||
| 3655 | err = js_create_reference(env, out, req->out_ref); | |
| 3656 | assert(err == 0); | |
| 3657 | ||
| 3658 | err = js_create_reference(env, pwd, req->pwd_ref); | |
| 3659 | assert(err == 0); | |
| 3660 | ||
| 3661 | err = js_create_reference(env, salt, req->salt_ref); | |
| 3662 | assert(err == 0); | |
| 3663 | ||
| 3664 | err = js_create_reference(env, callback, req->cb); | |
| 3665 | assert(err == 0); | |
| 3666 | ||
| 3667 | uv_loop_t *loop; | |
| 3668 | err = js_get_env_loop(env, &loop); | |
| 3669 | assert(err == 0); | |
| 3670 | ||
| 3671 | err = uv_queue_work(loop, &req->task, async_pbkdf2_sha512_execute, async_pbkdf2_sha512_complete); | |
| 3672 | assert(err == 0); | |
| 3673 | } | |
| 3674 | ||
| 3675 | static js_value_t * | |
| 3676 | sodium_native_exports(js_env_t *env, js_value_t *exports) { | |
| 3677 | int err; | |
| 3678 | ||
| 3679 | err = sodium_init(); | |
| 3680 | assert(err >= 0); | |
| 3681 | ||
| 3682 | #define V_FUNCTION(name, fn) \ | |
| 3683 | err = js_set_property<fn>(env, exports, name); \ | |
| 3684 | assert(err == 0); | |
| 3685 | ||
| 3686 | #define V_FUNCTION_NOSCOPE(name, fn) \ | |
| 3687 | err = js_set_property<fn, js_function_options_t{.scoped = false}>(env, exports, name); \ | |
| 3688 | assert(err == 0); | |
| 3689 | ||
| 3690 | #define V_UINT32(name, constant) \ | |
| 3691 | err = js_set_property(env, exports, name, static_cast<uint32_t>(constant)); \ | |
| 3692 | assert(err == 0); | |
| 3693 | ||
| 3694 | #define V_UINT64(name, constant) \ | |
| 3695 | err = js_set_property(env, exports, name, static_cast<int64_t>(std::min<uint64_t>(constant, js_max_safe_integer))); \ | |
| 3696 | assert(err == 0); | |
| 3697 | ||
| 3698 | #define V_STRING(name, str) \ | |
| 3699 | err = js_set_property(env, exports, name, str); \ | |
| 3700 | assert(err == 0); | |
| 3701 | ||
| 3702 | // memory | |
| 3703 | ||
| 3704 | V_FUNCTION("sodium_memzero", sn_sodium_memzero); | |
| 3705 | V_FUNCTION("sodium_mlock", sn_sodium_mlock); | |
| 3706 | V_FUNCTION("sodium_munlock", sn_sodium_munlock); | |
| 3707 | V_FUNCTION("sodium_malloc", sn_sodium_malloc); | |
| 3708 | V_FUNCTION("sodium_free", sn_sodium_free); | |
| 3709 | V_FUNCTION("sodium_mprotect_noaccess", sn_sodium_mprotect_noaccess); | |
| 3710 | V_FUNCTION("sodium_mprotect_readonly", sn_sodium_mprotect_readonly); | |
| 3711 | V_FUNCTION("sodium_mprotect_readwrite", sn_sodium_mprotect_readwrite); | |
| 3712 | ||
| 3713 | // randombytes | |
| 3714 | ||
| 3715 | V_FUNCTION_NOSCOPE("randombytes_buf", sn_randombytes_buf); | |
| 3716 | V_FUNCTION_NOSCOPE("randombytes_buf_deterministic", sn_randombytes_buf_deterministic); | |
| 3717 | V_FUNCTION_NOSCOPE("randombytes_random", sn_randombytes_random); | |
| 3718 | V_FUNCTION_NOSCOPE("randombytes_uniform", sn_randombytes_uniform); | |
| 3719 | ||
| 3720 | V_UINT32("randombytes_SEEDBYTES", randombytes_SEEDBYTES); | |
| 3721 | ||
| 3722 | // sodium helpers | |
| 3723 | ||
| 3724 | V_FUNCTION("sodium_memcmp", sn_sodium_memcmp); | |
| 3725 | V_FUNCTION("sodium_increment", sn_sodium_increment); | |
| 3726 | V_FUNCTION("sodium_add", sn_sodium_add); | |
| 3727 | V_FUNCTION("sodium_sub", sn_sodium_sub); | |
| 3728 | V_FUNCTION("sodium_compare", sn_sodium_compare); | |
| 3729 | V_FUNCTION("sodium_is_zero", sn_sodium_is_zero); | |
| 3730 | V_FUNCTION("sodium_pad", sn_sodium_pad); | |
| 3731 | V_FUNCTION("sodium_unpad", sn_sodium_unpad); | |
| 3732 | ||
| 3733 | // crypto_aead | |
| 3734 | ||
| 3735 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_keygen", sn_crypto_aead_xchacha20poly1305_ietf_keygen); | |
| 3736 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_encrypt", sn_crypto_aead_xchacha20poly1305_ietf_encrypt); | |
| 3737 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_decrypt", sn_crypto_aead_xchacha20poly1305_ietf_decrypt); | |
| 3738 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_encrypt_detached", sn_crypto_aead_xchacha20poly1305_ietf_encrypt_detached); | |
| 3739 | V_FUNCTION("crypto_aead_xchacha20poly1305_ietf_decrypt_detached", sn_crypto_aead_xchacha20poly1305_ietf_decrypt_detached); | |
| 3740 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_ABYTES", crypto_aead_xchacha20poly1305_ietf_ABYTES); | |
| 3741 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_KEYBYTES", crypto_aead_xchacha20poly1305_ietf_KEYBYTES); | |
| 3742 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_NPUBBYTES", crypto_aead_xchacha20poly1305_ietf_NPUBBYTES); | |
| 3743 | V_UINT32("crypto_aead_xchacha20poly1305_ietf_NSECBYTES", crypto_aead_xchacha20poly1305_ietf_NSECBYTES); | |
| 3744 | V_UINT64("crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX", crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX); | |
| 3745 | ||
| 3746 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_keygen", sn_crypto_aead_chacha20poly1305_ietf_keygen); | |
| 3747 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_encrypt", sn_crypto_aead_chacha20poly1305_ietf_encrypt); | |
| 3748 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_decrypt", sn_crypto_aead_chacha20poly1305_ietf_decrypt); | |
| 3749 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_encrypt_detached", sn_crypto_aead_chacha20poly1305_ietf_encrypt_detached); | |
| 3750 | V_FUNCTION("crypto_aead_chacha20poly1305_ietf_decrypt_detached", sn_crypto_aead_chacha20poly1305_ietf_decrypt_detached); | |
| 3751 | V_UINT32("crypto_aead_chacha20poly1305_ietf_ABYTES", crypto_aead_chacha20poly1305_ietf_ABYTES); | |
| 3752 | V_UINT32("crypto_aead_chacha20poly1305_ietf_KEYBYTES", crypto_aead_chacha20poly1305_ietf_KEYBYTES); | |
| 3753 | V_UINT32("crypto_aead_chacha20poly1305_ietf_NPUBBYTES", crypto_aead_chacha20poly1305_ietf_NPUBBYTES); | |
| 3754 | V_UINT32("crypto_aead_chacha20poly1305_ietf_NSECBYTES", crypto_aead_chacha20poly1305_ietf_NSECBYTES); | |
| 3755 | V_UINT64("crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX", crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX); | |
| 3756 | ||
| 3757 | // crypto_auth | |
| 3758 | ||
| 3759 | V_FUNCTION("crypto_auth", sn_crypto_auth); | |
| 3760 | V_FUNCTION("crypto_auth_verify", sn_crypto_auth_verify); | |
| 3761 | V_UINT32("crypto_auth_BYTES", crypto_auth_BYTES); | |
| 3762 | V_UINT32("crypto_auth_KEYBYTES", crypto_auth_KEYBYTES); | |
| 3763 | V_STRING("crypto_auth_PRIMITIVE", crypto_auth_PRIMITIVE); | |
| 3764 | ||
| 3765 | // crypto_box | |
| 3766 | ||
| 3767 | V_FUNCTION("crypto_box_keypair", sn_crypto_box_keypair); | |
| 3768 | V_FUNCTION("crypto_box_seed_keypair", sn_crypto_box_seed_keypair); | |
| 3769 | V_FUNCTION("crypto_box_easy", sn_crypto_box_easy); | |
| 3770 | V_FUNCTION("crypto_box_open_easy", sn_crypto_box_open_easy); | |
| 3771 | V_FUNCTION("crypto_box_detached", sn_crypto_box_detached); | |
| 3772 | V_FUNCTION("crypto_box_open_detached", sn_crypto_box_open_detached); | |
| 3773 | V_FUNCTION("crypto_box_seal", sn_crypto_box_seal); | |
| 3774 | V_FUNCTION_NOSCOPE("crypto_box_seal_open", sn_crypto_box_seal_open); | |
| 3775 | ||
| 3776 | V_UINT32("crypto_box_SEEDBYTES", crypto_box_SEEDBYTES); | |
| 3777 | V_UINT32("crypto_box_PUBLICKEYBYTES", crypto_box_PUBLICKEYBYTES); | |
| 3778 | V_UINT32("crypto_box_SECRETKEYBYTES", crypto_box_SECRETKEYBYTES); | |
| 3779 | V_UINT32("crypto_box_NONCEBYTES", crypto_box_NONCEBYTES); | |
| 3780 | V_UINT32("crypto_box_MACBYTES", crypto_box_MACBYTES); | |
| 3781 | V_UINT32("crypto_box_SEALBYTES", crypto_box_SEALBYTES); | |
| 3782 | V_STRING("crypto_box_PRIMITIVE", crypto_box_PRIMITIVE); | |
| 3783 | ||
| 3784 | // crypto_core | |
| 3785 | ||
| 3786 | V_FUNCTION("crypto_core_ed25519_is_valid_point", sn_crypto_core_ed25519_is_valid_point); | |
| 3787 | V_FUNCTION("crypto_core_ed25519_from_uniform", sn_crypto_core_ed25519_from_uniform); | |
| 3788 | V_FUNCTION("crypto_core_ed25519_add", sn_crypto_core_ed25519_add); | |
| 3789 | V_FUNCTION("crypto_core_ed25519_sub", sn_crypto_core_ed25519_sub); | |
| 3790 | V_FUNCTION("crypto_core_ed25519_scalar_random", sn_crypto_core_ed25519_scalar_random); | |
| 3791 | V_FUNCTION("crypto_core_ed25519_scalar_reduce", sn_crypto_core_ed25519_scalar_reduce); | |
| 3792 | V_FUNCTION("crypto_core_ed25519_scalar_invert", sn_crypto_core_ed25519_scalar_invert); | |
| 3793 | V_FUNCTION("crypto_core_ed25519_scalar_negate", sn_crypto_core_ed25519_scalar_negate); | |
| 3794 | V_FUNCTION("crypto_core_ed25519_scalar_complement", sn_crypto_core_ed25519_scalar_complement); | |
| 3795 | V_FUNCTION("crypto_core_ed25519_scalar_add", sn_crypto_core_ed25519_scalar_add); | |
| 3796 | V_FUNCTION("crypto_core_ed25519_scalar_sub", sn_crypto_core_ed25519_scalar_sub); | |
| 3797 | V_UINT32("crypto_core_ed25519_BYTES", crypto_core_ed25519_BYTES); | |
| 3798 | V_UINT32("crypto_core_ed25519_UNIFORMBYTES", crypto_core_ed25519_UNIFORMBYTES); | |
| 3799 | V_UINT32("crypto_core_ed25519_SCALARBYTES", crypto_core_ed25519_SCALARBYTES); | |
| 3800 | V_UINT32("crypto_core_ed25519_NONREDUCEDSCALARBYTES", crypto_core_ed25519_NONREDUCEDSCALARBYTES); | |
| 3801 | ||
| 3802 | // crypto_kdf | |
| 3803 | ||
| 3804 | V_FUNCTION("crypto_kdf_keygen", sn_crypto_kdf_keygen); | |
| 3805 | V_FUNCTION("crypto_kdf_derive_from_key", sn_crypto_kdf_derive_from_key); | |
| 3806 | V_UINT32("crypto_kdf_BYTES_MIN", crypto_kdf_BYTES_MIN); | |
| 3807 | V_UINT32("crypto_kdf_BYTES_MAX", crypto_kdf_BYTES_MAX); | |
| 3808 | V_UINT32("crypto_kdf_CONTEXTBYTES", crypto_kdf_CONTEXTBYTES); | |
| 3809 | V_UINT32("crypto_kdf_KEYBYTES", crypto_kdf_KEYBYTES); | |
| 3810 | V_STRING("crypto_kdf_PRIMITIVE", crypto_kdf_PRIMITIVE); | |
| 3811 | ||
| 3812 | // crypto_kx | |
| 3813 | ||
| 3814 | V_FUNCTION("crypto_kx_keypair", sn_crypto_kx_keypair); | |
| 3815 | V_FUNCTION("crypto_kx_seed_keypair", sn_crypto_kx_seed_keypair); | |
| 3816 | V_FUNCTION("crypto_kx_client_session_keys", sn_crypto_kx_client_session_keys); | |
| 3817 | V_FUNCTION("crypto_kx_server_session_keys", sn_crypto_kx_server_session_keys); | |
| 3818 | V_UINT32("crypto_kx_PUBLICKEYBYTES", crypto_kx_PUBLICKEYBYTES); | |
| 3819 | V_UINT32("crypto_kx_SECRETKEYBYTES", crypto_kx_SECRETKEYBYTES); | |
| 3820 | V_UINT32("crypto_kx_SEEDBYTES", crypto_kx_SEEDBYTES); | |
| 3821 | V_UINT32("crypto_kx_SESSIONKEYBYTES", crypto_kx_SESSIONKEYBYTES); | |
| 3822 | V_STRING("crypto_kx_PRIMITIVE", crypto_kx_PRIMITIVE); | |
| 3823 | ||
| 3824 | // crypto_generichash | |
| 3825 | ||
| 3826 | V_FUNCTION_NOSCOPE("crypto_generichash", sn_crypto_generichash); | |
| 3827 | V_FUNCTION("crypto_generichash_batch", sn_crypto_generichash_batch); | |
| 3828 | V_FUNCTION_NOSCOPE("crypto_generichash_keygen", sn_crypto_generichash_keygen); | |
| 3829 | V_FUNCTION_NOSCOPE("crypto_generichash_init", sn_crypto_generichash_init); | |
| 3830 | V_FUNCTION_NOSCOPE("crypto_generichash_update", sn_crypto_generichash_update); | |
| 3831 | V_FUNCTION_NOSCOPE("crypto_generichash_final", sn_crypto_generichash_final); | |
| 3832 | ||
| 3833 | V_UINT32("crypto_generichash_STATEBYTES", sizeof(crypto_generichash_state)); | |
| 3834 | V_STRING("crypto_generichash_PRIMITIVE", crypto_generichash_PRIMITIVE); | |
| 3835 | V_UINT32("crypto_generichash_BYTES_MIN", crypto_generichash_BYTES_MIN); | |
| 3836 | V_UINT32("crypto_generichash_BYTES_MAX", crypto_generichash_BYTES_MAX); | |
| 3837 | V_UINT32("crypto_generichash_BYTES", crypto_generichash_BYTES); | |
| 3838 | V_UINT32("crypto_generichash_KEYBYTES_MIN", crypto_generichash_KEYBYTES_MIN); | |
| 3839 | V_UINT32("crypto_generichash_KEYBYTES_MAX", crypto_generichash_KEYBYTES_MAX); | |
| 3840 | V_UINT32("crypto_generichash_KEYBYTES", crypto_generichash_KEYBYTES); | |
| 3841 | ||
| 3842 | // crypto_hash | |
| 3843 | ||
| 3844 | V_FUNCTION("crypto_hash", sn_crypto_hash); | |
| 3845 | V_UINT32("crypto_hash_BYTES", crypto_hash_BYTES); | |
| 3846 | V_STRING("crypto_hash_PRIMITIVE", crypto_hash_PRIMITIVE); | |
| 3847 | ||
| 3848 | V_FUNCTION("crypto_hash_sha256", sn_crypto_hash_sha256); | |
| 3849 | V_FUNCTION("crypto_hash_sha256_init", sn_crypto_hash_sha256_init); | |
| 3850 | V_FUNCTION("crypto_hash_sha256_update", sn_crypto_hash_sha256_update); | |
| 3851 | V_FUNCTION("crypto_hash_sha256_final", sn_crypto_hash_sha256_final); | |
| 3852 | V_UINT32("crypto_hash_sha256_STATEBYTES", sizeof(crypto_hash_sha256_state)); | |
| 3853 | V_UINT32("crypto_hash_sha256_BYTES", crypto_hash_sha256_BYTES); | |
| 3854 | ||
| 3855 | V_FUNCTION("crypto_hash_sha512", sn_crypto_hash_sha512); | |
| 3856 | V_FUNCTION("crypto_hash_sha512_init", sn_crypto_hash_sha512_init); | |
| 3857 | V_FUNCTION("crypto_hash_sha512_update", sn_crypto_hash_sha512_update); | |
| 3858 | V_FUNCTION("crypto_hash_sha512_final", sn_crypto_hash_sha512_final); | |
| 3859 | V_UINT32("crypto_hash_sha512_STATEBYTES", sizeof(crypto_hash_sha512_state)); | |
| 3860 | V_UINT32("crypto_hash_sha512_BYTES", crypto_hash_sha512_BYTES); | |
| 3861 | ||
| 3862 | // crypto_onetimeauth | |
| 3863 | ||
| 3864 | V_FUNCTION("crypto_onetimeauth", sn_crypto_onetimeauth); | |
| 3865 | V_FUNCTION("crypto_onetimeauth_verify", sn_crypto_onetimeauth_verify); | |
| 3866 | V_FUNCTION("crypto_onetimeauth_init", sn_crypto_onetimeauth_init); | |
| 3867 | V_FUNCTION("crypto_onetimeauth_update", sn_crypto_onetimeauth_update); | |
| 3868 | V_FUNCTION("crypto_onetimeauth_final", sn_crypto_onetimeauth_final); | |
| 3869 | V_UINT32("crypto_onetimeauth_STATEBYTES", sizeof(crypto_onetimeauth_state)); | |
| 3870 | V_UINT32("crypto_onetimeauth_BYTES", crypto_onetimeauth_BYTES); | |
| 3871 | V_UINT32("crypto_onetimeauth_KEYBYTES", crypto_onetimeauth_KEYBYTES); | |
| 3872 | V_STRING("crypto_onetimeauth_PRIMITIVE", crypto_onetimeauth_PRIMITIVE); | |
| 3873 | ||
| 3874 | // crypto_pwhash | |
| 3875 | ||
| 3876 | V_FUNCTION("crypto_pwhash", sn_crypto_pwhash); | |
| 3877 | V_FUNCTION("crypto_pwhash_str", sn_crypto_pwhash_str); | |
| 3878 | V_FUNCTION("crypto_pwhash_str_verify", sn_crypto_pwhash_str_verify); | |
| 3879 | V_FUNCTION("crypto_pwhash_str_needs_rehash", sn_crypto_pwhash_str_needs_rehash); | |
| 3880 | V_FUNCTION("crypto_pwhash_async", sn_crypto_pwhash_async); | |
| 3881 | V_FUNCTION("crypto_pwhash_str_async", sn_crypto_pwhash_str_async); | |
| 3882 | V_FUNCTION("crypto_pwhash_str_verify_async", sn_crypto_pwhash_str_verify_async); | |
| 3883 | V_UINT32("crypto_pwhash_ALG_ARGON2I13", crypto_pwhash_ALG_ARGON2I13); | |
| 3884 | V_UINT32("crypto_pwhash_ALG_ARGON2ID13", crypto_pwhash_ALG_ARGON2ID13); | |
| 3885 | V_UINT32("crypto_pwhash_ALG_DEFAULT", crypto_pwhash_ALG_DEFAULT); | |
| 3886 | V_UINT32("crypto_pwhash_BYTES_MIN", crypto_pwhash_BYTES_MIN); | |
| 3887 | V_UINT32("crypto_pwhash_BYTES_MAX", crypto_pwhash_BYTES_MAX); | |
| 3888 | V_UINT32("crypto_pwhash_PASSWD_MIN", crypto_pwhash_PASSWD_MIN); | |
| 3889 | V_UINT32("crypto_pwhash_PASSWD_MAX", crypto_pwhash_PASSWD_MAX); | |
| 3890 | V_UINT32("crypto_pwhash_SALTBYTES", crypto_pwhash_SALTBYTES); | |
| 3891 | V_UINT32("crypto_pwhash_STRBYTES", crypto_pwhash_STRBYTES); | |
| 3892 | V_STRING("crypto_pwhash_STRPREFIX", crypto_pwhash_STRPREFIX); | |
| 3893 | V_UINT32("crypto_pwhash_OPSLIMIT_MIN", crypto_pwhash_OPSLIMIT_MIN); | |
| 3894 | V_UINT32("crypto_pwhash_OPSLIMIT_MAX", crypto_pwhash_OPSLIMIT_MAX); | |
| 3895 | V_UINT64("crypto_pwhash_MEMLIMIT_MIN", crypto_pwhash_MEMLIMIT_MIN); | |
| 3896 | V_UINT64("crypto_pwhash_MEMLIMIT_MAX", crypto_pwhash_MEMLIMIT_MAX); | |
| 3897 | V_UINT32("crypto_pwhash_OPSLIMIT_INTERACTIVE", crypto_pwhash_OPSLIMIT_INTERACTIVE); | |
| 3898 | V_UINT64("crypto_pwhash_MEMLIMIT_INTERACTIVE", crypto_pwhash_MEMLIMIT_INTERACTIVE); | |
| 3899 | V_UINT32("crypto_pwhash_OPSLIMIT_MODERATE", crypto_pwhash_OPSLIMIT_MODERATE); | |
| 3900 | V_UINT64("crypto_pwhash_MEMLIMIT_MODERATE", crypto_pwhash_MEMLIMIT_MODERATE); | |
| 3901 | V_UINT32("crypto_pwhash_OPSLIMIT_SENSITIVE", crypto_pwhash_OPSLIMIT_SENSITIVE); | |
| 3902 | V_UINT64("crypto_pwhash_MEMLIMIT_SENSITIVE", crypto_pwhash_MEMLIMIT_SENSITIVE); | |
| 3903 | V_STRING("crypto_pwhash_PRIMITIVE", crypto_pwhash_PRIMITIVE); | |
| 3904 | ||
| 3905 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256", sn_crypto_pwhash_scryptsalsa208sha256); | |
| 3906 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str", sn_crypto_pwhash_scryptsalsa208sha256_str); | |
| 3907 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_verify", sn_crypto_pwhash_scryptsalsa208sha256_str_verify); | |
| 3908 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_needs_rehash", sn_crypto_pwhash_scryptsalsa208sha256_str_needs_rehash); | |
| 3909 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_async", sn_crypto_pwhash_scryptsalsa208sha256_async); | |
| 3910 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_async", sn_crypto_pwhash_scryptsalsa208sha256_str_async) | |
| 3911 | V_FUNCTION("crypto_pwhash_scryptsalsa208sha256_str_verify_async", sn_crypto_pwhash_scryptsalsa208sha256_str_verify_async); | |
| 3912 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_BYTES_MIN", crypto_pwhash_scryptsalsa208sha256_BYTES_MIN); | |
| 3913 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_BYTES_MAX", crypto_pwhash_scryptsalsa208sha256_BYTES_MAX); | |
| 3914 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN", crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN); | |
| 3915 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX", crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX); | |
| 3916 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_SALTBYTES", crypto_pwhash_scryptsalsa208sha256_SALTBYTES); | |
| 3917 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_STRBYTES", crypto_pwhash_scryptsalsa208sha256_STRBYTES); | |
| 3918 | V_STRING("crypto_pwhash_scryptsalsa208sha256_STRPREFIX", crypto_pwhash_scryptsalsa208sha256_STRPREFIX); | |
| 3919 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN); | |
| 3920 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX); | |
| 3921 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN); | |
| 3922 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX); | |
| 3923 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE); | |
| 3924 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE); | |
| 3925 | V_UINT32("crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE", crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE); | |
| 3926 | V_UINT64("crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE", crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE); | |
| 3927 | ||
| 3928 | // crypto_scalarmult | |
| 3929 | ||
| 3930 | V_FUNCTION("crypto_scalarmult_base", sn_crypto_scalarmult_base); | |
| 3931 | V_FUNCTION("crypto_scalarmult", sn_crypto_scalarmult); | |
| 3932 | V_STRING("crypto_scalarmult_PRIMITIVE", crypto_scalarmult_PRIMITIVE); | |
| 3933 | V_UINT32("crypto_scalarmult_BYTES", crypto_scalarmult_BYTES); | |
| 3934 | V_UINT32("crypto_scalarmult_SCALARBYTES", crypto_scalarmult_SCALARBYTES); | |
| 3935 | ||
| 3936 | V_FUNCTION("crypto_scalarmult_ed25519_base", sn_crypto_scalarmult_ed25519_base); | |
| 3937 | V_FUNCTION("crypto_scalarmult_ed25519", sn_crypto_scalarmult_ed25519); | |
| 3938 | V_FUNCTION("crypto_scalarmult_ed25519_base_noclamp", sn_crypto_scalarmult_ed25519_base_noclamp); | |
| 3939 | V_FUNCTION("crypto_scalarmult_ed25519_noclamp", sn_crypto_scalarmult_ed25519_noclamp); | |
| 3940 | V_UINT32("crypto_scalarmult_ed25519_BYTES", crypto_scalarmult_ed25519_BYTES); | |
| 3941 | V_UINT32("crypto_scalarmult_ed25519_SCALARBYTES", crypto_scalarmult_ed25519_SCALARBYTES); | |
| 3942 | ||
| 3943 | // crypto_secretbox | |
| 3944 | ||
| 3945 | V_FUNCTION("crypto_secretbox_easy", sn_crypto_secretbox_easy); | |
| 3946 | V_FUNCTION("crypto_secretbox_open_easy", sn_crypto_secretbox_open_easy); | |
| 3947 | V_FUNCTION("crypto_secretbox_detached", sn_crypto_secretbox_detached); | |
| 3948 | V_FUNCTION("crypto_secretbox_open_detached", sn_crypto_secretbox_open_detached); | |
| 3949 | V_UINT32("crypto_secretbox_KEYBYTES", crypto_secretbox_KEYBYTES); | |
| 3950 | V_UINT32("crypto_secretbox_NONCEBYTES", crypto_secretbox_NONCEBYTES); | |
| 3951 | V_UINT32("crypto_secretbox_MACBYTES", crypto_secretbox_MACBYTES); | |
| 3952 | V_STRING("crypto_secretbox_PRIMITIVE", crypto_secretbox_PRIMITIVE); | |
| 3953 | ||
| 3954 | // crypto_secretstream | |
| 3955 | ||
| 3956 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_keygen", sn_crypto_secretstream_xchacha20poly1305_keygen); | |
| 3957 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_init_push", sn_crypto_secretstream_xchacha20poly1305_init_push); | |
| 3958 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_init_pull", sn_crypto_secretstream_xchacha20poly1305_init_pull); | |
| 3959 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_push", sn_crypto_secretstream_xchacha20poly1305_push); | |
| 3960 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_pull", sn_crypto_secretstream_xchacha20poly1305_pull); | |
| 3961 | V_FUNCTION_NOSCOPE("crypto_secretstream_xchacha20poly1305_rekey", sn_crypto_secretstream_xchacha20poly1305_rekey); | |
| 3962 | ||
| 3963 | V_UINT32("crypto_secretstream_xchacha20poly1305_STATEBYTES", sizeof(crypto_secretstream_xchacha20poly1305_state)); | |
| 3964 | V_UINT32("crypto_secretstream_xchacha20poly1305_ABYTES", crypto_secretstream_xchacha20poly1305_ABYTES); | |
| 3965 | V_UINT32("crypto_secretstream_xchacha20poly1305_HEADERBYTES", crypto_secretstream_xchacha20poly1305_HEADERBYTES); | |
| 3966 | V_UINT32("crypto_secretstream_xchacha20poly1305_KEYBYTES", crypto_secretstream_xchacha20poly1305_KEYBYTES); | |
| 3967 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAGBYTES", 1); | |
| 3968 | V_UINT64("crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX", crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX); | |
| 3969 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_MESSAGE", crypto_secretstream_xchacha20poly1305_TAG_MESSAGE); | |
| 3970 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_PUSH", crypto_secretstream_xchacha20poly1305_TAG_PUSH); | |
| 3971 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_REKEY", crypto_secretstream_xchacha20poly1305_TAG_REKEY); | |
| 3972 | V_UINT32("crypto_secretstream_xchacha20poly1305_TAG_FINAL", crypto_secretstream_xchacha20poly1305_TAG_FINAL); | |
| 3973 | ||
| 3974 | // crypto_shorthash | |
| 3975 | ||
| 3976 | V_FUNCTION("crypto_shorthash", sn_crypto_shorthash); | |
| 3977 | V_UINT32("crypto_shorthash_BYTES", crypto_shorthash_BYTES); | |
| 3978 | V_UINT32("crypto_shorthash_KEYBYTES", crypto_shorthash_KEYBYTES); | |
| 3979 | V_STRING("crypto_shorthash_PRIMITIVE", crypto_shorthash_PRIMITIVE); | |
| 3980 | ||
| 3981 | // crypto_sign | |
| 3982 | ||
| 3983 | V_FUNCTION("crypto_sign_keypair", sn_crypto_sign_keypair); | |
| 3984 | V_FUNCTION("crypto_sign_seed_keypair", sn_crypto_sign_seed_keypair); | |
| 3985 | V_FUNCTION("crypto_sign", sn_crypto_sign); | |
| 3986 | V_FUNCTION("crypto_sign_open", sn_crypto_sign_open); | |
| 3987 | V_FUNCTION("crypto_sign_detached", sn_crypto_sign_detached); | |
| 3988 | V_FUNCTION_NOSCOPE("crypto_sign_verify_detached", sn_crypto_sign_verify_detached); | |
| 3989 | V_FUNCTION("crypto_sign_ed25519_sk_to_pk", sn_crypto_sign_ed25519_sk_to_pk); | |
| 3990 | V_FUNCTION("crypto_sign_ed25519_pk_to_curve25519", sn_crypto_sign_ed25519_pk_to_curve25519); | |
| 3991 | V_FUNCTION("crypto_sign_ed25519_sk_to_curve25519", sn_crypto_sign_ed25519_sk_to_curve25519); | |
| 3992 | ||
| 3993 | V_UINT32("crypto_sign_SEEDBYTES", crypto_sign_SEEDBYTES); | |
| 3994 | V_UINT32("crypto_sign_PUBLICKEYBYTES", crypto_sign_PUBLICKEYBYTES); | |
| 3995 | V_UINT32("crypto_sign_SECRETKEYBYTES", crypto_sign_SECRETKEYBYTES); | |
| 3996 | V_UINT32("crypto_sign_BYTES", crypto_sign_BYTES); | |
| 3997 | ||
| 3998 | // crypto_stream | |
| 3999 | ||
| 4000 | V_FUNCTION("crypto_stream", sn_crypto_stream); | |
| 4001 | V_UINT32("crypto_stream_KEYBYTES", crypto_stream_KEYBYTES); | |
| 4002 | V_UINT32("crypto_stream_NONCEBYTES", crypto_stream_NONCEBYTES); | |
| 4003 | V_STRING("crypto_stream_PRIMITIVE", crypto_stream_PRIMITIVE); | |
| 4004 | ||
| 4005 | V_FUNCTION_NOSCOPE("crypto_stream_xor", sn_crypto_stream_xor); | |
| 4006 | V_FUNCTION("crypto_stream_xor_init", sn_crypto_stream_xor_wrap_init); | |
| 4007 | V_FUNCTION("crypto_stream_xor_update", sn_crypto_stream_xor_wrap_update); | |
| 4008 | V_FUNCTION("crypto_stream_xor_final", sn_crypto_stream_xor_wrap_final); | |
| 4009 | V_UINT32("crypto_stream_xor_STATEBYTES", sizeof(sn_crypto_stream_xor_state)); | |
| 4010 | ||
| 4011 | V_FUNCTION("crypto_stream_chacha20", sn_crypto_stream_chacha20); | |
| 4012 | V_UINT32("crypto_stream_chacha20_KEYBYTES", crypto_stream_chacha20_KEYBYTES); | |
| 4013 | V_UINT32("crypto_stream_chacha20_NONCEBYTES", crypto_stream_chacha20_NONCEBYTES); | |
| 4014 | V_UINT64("crypto_stream_chacha20_MESSAGEBYTES_MAX", crypto_stream_chacha20_MESSAGEBYTES_MAX); | |
| 4015 | ||
| 4016 | V_FUNCTION("crypto_stream_chacha20_xor", sn_crypto_stream_chacha20_xor); | |
| 4017 | V_FUNCTION("crypto_stream_chacha20_xor_ic", sn_crypto_stream_chacha20_xor_ic); | |
| 4018 | V_FUNCTION("crypto_stream_chacha20_xor_init", sn_crypto_stream_chacha20_xor_wrap_init); | |
| 4019 | V_FUNCTION("crypto_stream_chacha20_xor_update", sn_crypto_stream_chacha20_xor_wrap_update); | |
| 4020 | V_FUNCTION("crypto_stream_chacha20_xor_final", sn_crypto_stream_chacha20_xor_wrap_final); | |
| 4021 | V_UINT32("crypto_stream_chacha20_xor_STATEBYTES", sizeof(sn_crypto_stream_chacha20_xor_state)); | |
| 4022 | ||
| 4023 | V_FUNCTION("crypto_stream_chacha20_ietf", sn_crypto_stream_chacha20_ietf); | |
| 4024 | V_UINT32("crypto_stream_chacha20_ietf_KEYBYTES", crypto_stream_chacha20_ietf_KEYBYTES); | |
| 4025 | V_UINT32("crypto_stream_chacha20_ietf_NONCEBYTES", crypto_stream_chacha20_ietf_NONCEBYTES); | |
| 4026 | V_UINT64("crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX", crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX); | |
| 4027 | V_UINT32("crypto_stream_chacha20_ietf_xor_STATEBYTES", sizeof(sn_crypto_stream_chacha20_ietf_xor_state)); | |
| 4028 | ||
| 4029 | V_FUNCTION("crypto_stream_chacha20_ietf_xor", sn_crypto_stream_chacha20_ietf_xor); | |
| 4030 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_ic", sn_crypto_stream_chacha20_ietf_xor_ic); | |
| 4031 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_init", sn_crypto_stream_chacha20_ietf_xor_wrap_init); | |
| 4032 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_update", sn_crypto_stream_chacha20_ietf_xor_wrap_update); | |
| 4033 | V_FUNCTION("crypto_stream_chacha20_ietf_xor_final", sn_crypto_stream_chacha20_ietf_xor_wrap_final); | |
| 4034 | ||
| 4035 | V_FUNCTION("crypto_stream_xchacha20", sn_crypto_stream_xchacha20); | |
| 4036 | V_UINT32("crypto_stream_xchacha20_KEYBYTES", crypto_stream_xchacha20_KEYBYTES); | |
| 4037 | V_UINT32("crypto_stream_xchacha20_NONCEBYTES", crypto_stream_xchacha20_NONCEBYTES); | |
| 4038 | V_UINT64("crypto_stream_xchacha20_MESSAGEBYTES_MAX", crypto_stream_xchacha20_MESSAGEBYTES_MAX); | |
| 4039 | ||
| 4040 | V_FUNCTION("crypto_stream_xchacha20_xor", sn_crypto_stream_xchacha20_xor); | |
| 4041 | V_FUNCTION("crypto_stream_xchacha20_xor_ic", sn_crypto_stream_xchacha20_xor_ic); | |
| 4042 | V_FUNCTION("crypto_stream_xchacha20_xor_init", sn_crypto_stream_xchacha20_xor_wrap_init); | |
| 4043 | V_FUNCTION("crypto_stream_xchacha20_xor_update", sn_crypto_stream_xchacha20_xor_wrap_update); | |
| 4044 | V_FUNCTION("crypto_stream_xchacha20_xor_final", sn_crypto_stream_xchacha20_xor_wrap_final); | |
| 4045 | V_FUNCTION("crypto_stream_xchacha20", sn_crypto_stream_xchacha20); | |
| 4046 | V_UINT32("crypto_stream_xchacha20_xor_STATEBYTES", sizeof(sn_crypto_stream_xchacha20_xor_state)); | |
| 4047 | ||
| 4048 | V_FUNCTION("crypto_stream_salsa20", sn_crypto_stream_salsa20); | |
| 4049 | V_UINT32("crypto_stream_salsa20_KEYBYTES", crypto_stream_salsa20_KEYBYTES); | |
| 4050 | V_UINT32("crypto_stream_salsa20_NONCEBYTES", crypto_stream_salsa20_NONCEBYTES); | |
| 4051 | V_UINT64("crypto_stream_salsa20_MESSAGEBYTES_MAX", crypto_stream_salsa20_MESSAGEBYTES_MAX); | |
| 4052 | ||
| 4053 | V_FUNCTION("crypto_stream_salsa20_xor", sn_crypto_stream_salsa20_xor); | |
| 4054 | V_FUNCTION("crypto_stream_salsa20_xor_ic", sn_crypto_stream_salsa20_xor_ic); | |
| 4055 | V_FUNCTION("crypto_stream_salsa20_xor_init", sn_crypto_stream_salsa20_xor_wrap_init); | |
| 4056 | V_FUNCTION("crypto_stream_salsa20_xor_update", sn_crypto_stream_salsa20_xor_wrap_update); | |
| 4057 | V_FUNCTION("crypto_stream_salsa20_xor_final", sn_crypto_stream_salsa20_xor_wrap_final); | |
| 4058 | V_UINT32("crypto_stream_salsa20_xor_STATEBYTES", sizeof(sn_crypto_stream_salsa20_xor_state)); | |
| 4059 | ||
| 4060 | // extensions | |
| 4061 | ||
| 4062 | // tweak | |
| 4063 | ||
| 4064 | V_FUNCTION("extension_tweak_ed25519_base", sn_extension_tweak_ed25519_base); | |
| 4065 | V_FUNCTION("extension_tweak_ed25519_sign_detached", sn_extension_tweak_ed25519_sign_detached); | |
| 4066 | V_FUNCTION("extension_tweak_ed25519_sk_to_scalar", sn_extension_tweak_ed25519_sk_to_scalar); | |
| 4067 | V_FUNCTION("extension_tweak_ed25519_scalar", sn_extension_tweak_ed25519_scalar); | |
| 4068 | V_FUNCTION("extension_tweak_ed25519_pk", sn_extension_tweak_ed25519_pk); | |
| 4069 | V_FUNCTION("extension_tweak_ed25519_keypair", sn_extension_tweak_ed25519_keypair); | |
| 4070 | V_FUNCTION("extension_tweak_ed25519_scalar_add", sn_extension_tweak_ed25519_scalar_add); | |
| 4071 | V_FUNCTION("extension_tweak_ed25519_pk_add", sn_extension_tweak_ed25519_pk_add); | |
| 4072 | V_FUNCTION("extension_tweak_ed25519_keypair_add", sn_extension_tweak_ed25519_keypair_add); | |
| 4073 | V_UINT32("extension_tweak_ed25519_BYTES", sn__extension_tweak_ed25519_BYTES); | |
| 4074 | V_UINT32("extension_tweak_ed25519_SCALARBYTES", sn__extension_tweak_ed25519_SCALARBYTES); | |
| 4075 | ||
| 4076 | // pbkdf2 | |
| 4077 | ||
| 4078 | V_FUNCTION("extension_pbkdf2_sha512", sn_extension_pbkdf2_sha512); | |
| 4079 | V_FUNCTION("extension_pbkdf2_sha512_async", sn_extension_pbkdf2_sha512_async); | |
| 4080 | V_UINT32("extension_pbkdf2_sha512_SALTBYTES", sn__extension_pbkdf2_sha512_SALTBYTES); | |
| 4081 | V_UINT32("extension_pbkdf2_sha512_HASHBYTES", sn__extension_pbkdf2_sha512_HASHBYTES); | |
| 4082 | V_UINT32("extension_pbkdf2_sha512_ITERATIONS_MIN", sn__extension_pbkdf2_sha512_ITERATIONS_MIN); | |
| 4083 | V_UINT64("extension_pbkdf2_sha512_BYTES_MAX", sn__extension_pbkdf2_sha512_BYTES_MAX); | |
| 4084 | ||
| 4085 | #undef V_FUNCTION | |
| 4086 | #undef V_FUNCTION_NOSCOPE | |
| 4087 | #undef V_UINT32 | |
| 4088 | #undef V_UINT64 | |
| 4089 | #undef V_STRING | |
| 4090 | ||
| 4091 | return exports; | |
| 4092 | } | |
| 4093 | ||
| 4094 | BARE_MODULE(sodium_native, sodium_native_exports) |
| … | … | @@ -0,0 +1,2 @@ |
| 1 | require.addon = require('require-addon') | |
| 2 | module.exports = require.addon('.', __filename) |
| … | … | @@ -0,0 +1,90 @@ |
| 1 | /*- | |
| 2 | * Copyright 2005,2007,2009 Colin Percival | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 24 | * SUCH DAMAGE. | |
| 25 | */ | |
| 26 | ||
| 27 | /* | |
| 28 | * Adapted from libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf-sha256.c | |
| 29 | */ | |
| 30 | ||
| 31 | #include <string.h> | |
| 32 | #include <sodium.h> | |
| 33 | ||
| 34 | #include "pbkdf2.h" | |
| 35 | ||
| 36 | /** | |
| 37 | * pbkdf2_sha512(passwd, passwdlen, salt, saltlen, c, buf, dkLen): | |
| 38 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and | |
| 39 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). | |
| 40 | */ | |
| 41 | int | |
| 42 | sn__extension_pbkdf2_sha512(const unsigned char *passwd, size_t passwdlen, | |
| 43 | const unsigned char *salt, size_t saltlen, uint64_t c, | |
| 44 | unsigned char *buf, size_t dkLen) | |
| 45 | { | |
| 46 | crypto_auth_hmacsha512_state PShctx, hctx; | |
| 47 | size_t i; | |
| 48 | unsigned char ivec[4]; | |
| 49 | unsigned char U[64]; | |
| 50 | unsigned char T[64]; | |
| 51 | uint64_t j; | |
| 52 | unsigned int k; | |
| 53 | size_t clen; | |
| 54 | ||
| 55 | if (dkLen > sn__extension_pbkdf2_sha512_BYTES_MAX) { | |
| 56 | return -1; | |
| 57 | } | |
| 58 | ||
| 59 | crypto_auth_hmacsha512_init(&PShctx, passwd, passwdlen); | |
| 60 | crypto_auth_hmacsha512_update(&PShctx, salt, saltlen); | |
| 61 | ||
| 62 | for (i = 0; i * crypto_auth_hmacsha512_BYTES < dkLen; i++) { | |
| 63 | SN_PBKDF2_STORE32_BE(ivec, (uint32_t)(i + 1)); | |
| 64 | memcpy(&hctx, &PShctx, sizeof(crypto_auth_hmacsha512_state)); | |
| 65 | crypto_auth_hmacsha512_update(&hctx, ivec, 4); | |
| 66 | crypto_auth_hmacsha512_final(&hctx, U); | |
| 67 | ||
| 68 | memcpy(T, U, crypto_auth_hmacsha512_BYTES); | |
| 69 | /* LCOV_EXCL_START */ | |
| 70 | for (j = 2; j <= c; j++) { | |
| 71 | crypto_auth_hmacsha512_init(&hctx, passwd, passwdlen); | |
| 72 | crypto_auth_hmacsha512_update(&hctx, U, crypto_auth_hmacsha512_BYTES); | |
| 73 | crypto_auth_hmacsha512_final(&hctx, U); | |
| 74 | ||
| 75 | for (k = 0; k < crypto_auth_hmacsha512_BYTES; k++) { | |
| 76 | T[k] ^= U[k]; | |
| 77 | } | |
| 78 | } | |
| 79 | /* LCOV_EXCL_STOP */ | |
| 80 | ||
| 81 | clen = dkLen - i * crypto_auth_hmacsha512_BYTES; | |
| 82 | if (clen > crypto_auth_hmacsha512_BYTES) { | |
| 83 | clen = crypto_auth_hmacsha512_BYTES; | |
| 84 | } | |
| 85 | memcpy(&buf[i * crypto_auth_hmacsha512_BYTES], T, clen); | |
| 86 | } | |
| 87 | sodium_memzero((void *) &PShctx, sizeof PShctx); | |
| 88 | ||
| 89 | return 0; | |
| 90 | } |
| … | … | @@ -0,0 +1,60 @@ |
| 1 | /*- | |
| 2 | * Copyright 2005,2007,2009 Colin Percival | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 24 | * SUCH DAMAGE. | |
| 25 | * | |
| 26 | */ | |
| 27 | ||
| 28 | /* | |
| 29 | * Adapted from libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf-sha256.c | |
| 30 | */ | |
| 31 | #ifdef __cplusplus | |
| 32 | extern "C" { | |
| 33 | #endif | |
| 34 | ||
| 35 | #include <sodium.h> | |
| 36 | ||
| 37 | #define SN_PBKDF2_STORE32_BE(buf, n32) \ | |
| 38 | buf[0] = n32 >> 24 & 0xff; \ | |
| 39 | buf[1] = n32 >> 16 & 0xff; \ | |
| 40 | buf[2] = n32 >> 8 & 0xff; \ | |
| 41 | buf[3] = n32 >> 0 & 0xff; | |
| 42 | ||
| 43 | #define sn__extension_pbkdf2_sha512_SALTBYTES 16U | |
| 44 | ||
| 45 | #define sn__extension_pbkdf2_sha512_HASHBYTES crypto_hash_sha512_BYTES | |
| 46 | ||
| 47 | #define sn__extension_pbkdf2_sha512_ITERATIONS_MIN 1U | |
| 48 | ||
| 49 | #define sn__extension_pbkdf2_sha512_BYTES_MAX 0x3fffffffc0ULL | |
| 50 | ||
| 51 | /** | |
| 52 | * extension_pbkdf2_sha512(passwd, passwdlen, salt, saltlen, c, buf, dkLen): | |
| 53 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and | |
| 54 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). | |
| 55 | */ | |
| 56 | int sn__extension_pbkdf2_sha512(const unsigned char *, size_t, const unsigned char *, size_t, | |
| 57 | uint64_t, unsigned char *, size_t); | |
| 58 | #ifdef __cplusplus | |
| 59 | }; | |
| 60 | #endif |
| … | … | @@ -0,0 +1,206 @@ |
| 1 | #include "tweak.h" | |
| 2 | ||
| 3 | /* | |
| 4 | *EXPERIMENTAL API* | |
| 5 | ||
| 6 | This module is an experimental implementation of a key tweaking protocol | |
| 7 | over ed25519 keys. The signature algorithm has been reimplemented from | |
| 8 | libsodium, but the nonce generation algorithm is *non-standard*. | |
| 9 | ||
| 10 | Use at your own risk | |
| 11 | */ | |
| 12 | ||
| 13 | static void _extension_tweak_nonce (unsigned char *nonce, const unsigned char *n, | |
| 14 | const unsigned char *m, unsigned long long mlen) | |
| 15 | { | |
| 16 | // dom2(x, y) with x = 0 (not prehashed) and y = "crypto_tweak_ed25519" | |
| 17 | static const unsigned char TWEAK_PREFIX[32 + 2 + 20] = { | |
| 18 | 'S', 'i', 'g', 'E', 'd', '2', '5', '5', '1', '9', ' ', | |
| 19 | 'n', 'o', ' ', 'E', 'd', '2', '5', '5', '1', '9', ' ', | |
| 20 | 'c', 'o', 'l', 'l', 'i', 's', 'i', 'o', 'n', 's', 0, | |
| 21 | 20, 'c', 'r', 'y', 'p', 't', 'o', '_', 't', 'w', 'e', | |
| 22 | 'a', 'k', '_', 'e', 'd', '2', '5', '5', '1', '9' | |
| 23 | }; | |
| 24 | ||
| 25 | crypto_hash_sha512_state hs; | |
| 26 | ||
| 27 | crypto_hash_sha512_init(&hs); | |
| 28 | crypto_hash_sha512_update(&hs, TWEAK_PREFIX, sizeof TWEAK_PREFIX); | |
| 29 | crypto_hash_sha512_update(&hs, n, 32); | |
| 30 | crypto_hash_sha512_update(&hs, m, mlen); | |
| 31 | crypto_hash_sha512_final(&hs, nonce); | |
| 32 | } | |
| 33 | ||
| 34 | static inline void | |
| 35 | _crypto_sign_ed25519_clamp(unsigned char k[32]) | |
| 36 | { | |
| 37 | k[0] &= 248; | |
| 38 | k[31] &= 127; | |
| 39 | k[31] |= 64; | |
| 40 | } | |
| 41 | ||
| 42 | static void _extension_tweak_ed25519(unsigned char *q, unsigned char *n, | |
| 43 | const unsigned char *ns, unsigned long long nslen) | |
| 44 | { | |
| 45 | sodium_memzero(q, sizeof q); | |
| 46 | ||
| 47 | crypto_hash(n, ns, nslen); | |
| 48 | n[31] &= 127; // clear highest bit | |
| 49 | ||
| 50 | crypto_scalarmult_ed25519_base_noclamp(q, n); | |
| 51 | ||
| 52 | // hash tweak until we get a valid tweaked q | |
| 53 | while (crypto_core_ed25519_is_valid_point(q) != 1) { | |
| 54 | crypto_hash(n, n, 32); | |
| 55 | n[31] &= 127; // clear highest bit | |
| 56 | ||
| 57 | crypto_scalarmult_ed25519_base_noclamp(q, n); | |
| 58 | } | |
| 59 | } | |
| 60 | ||
| 61 | void sn__extension_tweak_ed25519_base(unsigned char *pk, unsigned char *scalar, | |
| 62 | const unsigned char *ns, unsigned long long nslen) | |
| 63 | { | |
| 64 | unsigned char n64[64]; | |
| 65 | ||
| 66 | _extension_tweak_ed25519(pk, n64, ns, nslen); | |
| 67 | ||
| 68 | SN_TWEAK_COPY_32(scalar, n64) | |
| 69 | } | |
| 70 | ||
| 71 | int sn__extension_tweak_ed25519_sign_detached(unsigned char *sig, unsigned long long *siglen_p, | |
| 72 | const unsigned char *m, unsigned long long mlen, | |
| 73 | const unsigned char *n, unsigned char *pk) | |
| 74 | { | |
| 75 | crypto_hash_sha512_state hs; | |
| 76 | ||
| 77 | unsigned char nonce[64]; | |
| 78 | unsigned char R[32]; | |
| 79 | unsigned char hram[64]; | |
| 80 | unsigned char _pk[32]; | |
| 81 | ||
| 82 | // check if pk was passed | |
| 83 | if (pk == NULL) { | |
| 84 | pk = _pk; | |
| 85 | ||
| 86 | // derive pk from scalar | |
| 87 | if (crypto_scalarmult_ed25519_base_noclamp(pk, n) != 0) { | |
| 88 | return -1; | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | _extension_tweak_nonce(nonce, n, m, mlen); | |
| 93 | crypto_core_ed25519_scalar_reduce(nonce, nonce); | |
| 94 | ||
| 95 | // R = G ^ nonce : curve point from nonce | |
| 96 | if (crypto_scalarmult_ed25519_base_noclamp(R, nonce) != 0) { | |
| 97 | return -1; | |
| 98 | } | |
| 99 | ||
| 100 | // generate challenge as h(ram) = hash(R, pk, message) | |
| 101 | crypto_hash_sha512_init(&hs); | |
| 102 | crypto_hash_sha512_update(&hs, R, 32); | |
| 103 | crypto_hash_sha512_update(&hs, pk, 32); | |
| 104 | crypto_hash_sha512_update(&hs, m, mlen); | |
| 105 | ||
| 106 | crypto_hash_sha512_final(&hs, hram); | |
| 107 | ||
| 108 | crypto_core_ed25519_scalar_reduce(hram, hram); | |
| 109 | ||
| 110 | // sig = nonce + n * h(ram) | |
| 111 | crypto_core_ed25519_scalar_mul(sig, hram, n); | |
| 112 | crypto_core_ed25519_scalar_add(sig + 32, nonce, sig); | |
| 113 | ||
| 114 | SN_TWEAK_COPY_32(sig, R) | |
| 115 | ||
| 116 | if (siglen_p != NULL) { | |
| 117 | *siglen_p = 64U; | |
| 118 | } | |
| 119 | ||
| 120 | return 0; | |
| 121 | } | |
| 122 | ||
| 123 | // tweak a secret key | |
| 124 | void sn__extension_tweak_ed25519_sk_to_scalar(unsigned char *n, const unsigned char *sk) | |
| 125 | { | |
| 126 | unsigned char n64[64]; | |
| 127 | ||
| 128 | // get sk scalar from seed, cf. crypto_sign_keypair_seed | |
| 129 | crypto_hash(n64, sk, 32); | |
| 130 | _crypto_sign_ed25519_clamp(n64); | |
| 131 | ||
| 132 | SN_TWEAK_COPY_32(n, n64) | |
| 133 | } | |
| 134 | ||
| 135 | // tweak a secret key | |
| 136 | void sn__extension_tweak_ed25519_scalar(unsigned char *scalar_out, | |
| 137 | const unsigned char *scalar, | |
| 138 | const unsigned char *ns, | |
| 139 | unsigned long long nslen) | |
| 140 | { | |
| 141 | unsigned char n[64]; | |
| 142 | unsigned char q[32]; | |
| 143 | ||
| 144 | _extension_tweak_ed25519(q, n, ns, nslen); | |
| 145 | crypto_core_ed25519_scalar_add(scalar_out, scalar, n); | |
| 146 | } | |
| 147 | ||
| 148 | // tweak a public key | |
| 149 | int sn__extension_tweak_ed25519_pk(unsigned char *tpk, | |
| 150 | const unsigned char *pk, | |
| 151 | const unsigned char *ns, | |
| 152 | unsigned long long nslen) | |
| 153 | { | |
| 154 | unsigned char n[64]; | |
| 155 | unsigned char q[32]; | |
| 156 | ||
| 157 | _extension_tweak_ed25519(q, n, ns, nslen); | |
| 158 | return crypto_core_ed25519_add(tpk, q, pk); | |
| 159 | } | |
| 160 | ||
| 161 | ||
| 162 | void sn__extension_tweak_ed25519_keypair(unsigned char *pk, unsigned char *scalar_out, | |
| 163 | unsigned char *scalar, const unsigned char *ns, | |
| 164 | unsigned long long nslen) | |
| 165 | { | |
| 166 | unsigned char n64[64]; | |
| 167 | ||
| 168 | crypto_hash(n64, ns, nslen); | |
| 169 | n64[31] &= 127; // clear highest bit | |
| 170 | ||
| 171 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, n64); | |
| 172 | crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 173 | ||
| 174 | // hash tweak until we get a valid tweaked point | |
| 175 | while (crypto_core_ed25519_is_valid_point(pk) != 1) { | |
| 176 | crypto_hash(n64, n64, 32); | |
| 177 | n64[31] &= 127; // clear highest bit | |
| 178 | ||
| 179 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, n64); | |
| 180 | crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 181 | } | |
| 182 | } | |
| 183 | ||
| 184 | // add tweak to scalar | |
| 185 | void sn__extension_tweak_ed25519_scalar_add(unsigned char *scalar_out, | |
| 186 | const unsigned char *scalar, | |
| 187 | const unsigned char *n) | |
| 188 | { | |
| 189 | crypto_core_ed25519_scalar_add(scalar_out, scalar, n); | |
| 190 | } | |
| 191 | ||
| 192 | // add tweak point to public key | |
| 193 | int sn__extension_tweak_ed25519_pk_add(unsigned char *tpk, | |
| 194 | const unsigned char *pk, | |
| 195 | const unsigned char *q) | |
| 196 | { | |
| 197 | return crypto_core_ed25519_add(tpk, pk, q); | |
| 198 | } | |
| 199 | ||
| 200 | ||
| 201 | int sn__extension_tweak_ed25519_keypair_add(unsigned char *pk, unsigned char *scalar_out, | |
| 202 | unsigned char *scalar, const unsigned char *tweak) | |
| 203 | { | |
| 204 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, tweak); | |
| 205 | return crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); | |
| 206 | } |
| … | … | @@ -0,0 +1,62 @@ |
| 1 | #ifdef __cplusplus | |
| 2 | extern "C" { | |
| 3 | #endif | |
| 4 | ||
| 5 | #include <sodium.h> | |
| 6 | ||
| 7 | // copy 32 bytes using int64_t pointers | |
| 8 | #define SN_TWEAK_COPY_32(a, b) \ | |
| 9 | { \ | |
| 10 | long long *dst = (long long *) a; \ | |
| 11 | long long *src = (long long *) b; \ | |
| 12 | dst[0] = src[0]; \ | |
| 13 | dst[1] = src[1]; \ | |
| 14 | dst[2] = src[2]; \ | |
| 15 | dst[3] = src[3]; \ | |
| 16 | } | |
| 17 | ||
| 18 | #define sn__extension_tweak_ed25519_BYTES crypto_sign_ed25519_PUBLICKEYBYTES | |
| 19 | ||
| 20 | #define sn__extension_tweak_ed25519_SCALARBYTES crypto_scalarmult_ed25519_SCALARBYTES | |
| 21 | ||
| 22 | int sn__extension_tweak_ed25519_sign_detached(unsigned char *sig, unsigned long long *siglen_p, | |
| 23 | const unsigned char *m, unsigned long long mlen, | |
| 24 | const unsigned char *n, unsigned char *pk); | |
| 25 | ||
| 26 | void sn__extension_tweak_ed25519_base(unsigned char *pk, unsigned char *scalar, | |
| 27 | const unsigned char *ns, unsigned long long nslen); | |
| 28 | ||
| 29 | void sn__extension_tweak_ed25519_sk_to_scalar(unsigned char *scalar, const unsigned char *sk); | |
| 30 | ||
| 31 | // tweak a secret key | |
| 32 | void sn__extension_tweak_ed25519_scalar(unsigned char *scalar_out, | |
| 33 | const unsigned char *scalar, | |
| 34 | const unsigned char *ns, | |
| 35 | unsigned long long nslen); | |
| 36 | ||
| 37 | // tweak a public key | |
| 38 | int sn__extension_tweak_ed25519_pk(unsigned char *tpk, | |
| 39 | const unsigned char *pk, | |
| 40 | const unsigned char *ns, | |
| 41 | unsigned long long nslen); | |
| 42 | ||
| 43 | void sn__extension_tweak_ed25519_keypair(unsigned char *pk, unsigned char *scalar_out, | |
| 44 | unsigned char *scalar, const unsigned char *ns, | |
| 45 | unsigned long long nslen); | |
| 46 | ||
| 47 | // add tweak scalar to private key | |
| 48 | void sn__extension_tweak_ed25519_scalar_add(unsigned char *scalar_out, | |
| 49 | const unsigned char *scalar, | |
| 50 | const unsigned char *n); | |
| 51 | ||
| 52 | // add tweak point to public key | |
| 53 | int sn__extension_tweak_ed25519_pk_add(unsigned char *tpk, | |
| 54 | const unsigned char *pk, | |
| 55 | const unsigned char *q); | |
| 56 | ||
| 57 | int sn__extension_tweak_ed25519_keypair_add(unsigned char *pk, unsigned char *scalar_out, | |
| 58 | unsigned char *scalar, const unsigned char *tweak); | |
| 59 | ||
| 60 | #ifdef __cplusplus | |
| 61 | }; | |
| 62 | #endif |
| … | … | @@ -0,0 +1,3231 @@ |
| 1 | const assert = require('assert') | |
| 2 | const binding = require('./binding') | |
| 3 | const { isNode } = require('which-runtime') | |
| 4 | ||
| 5 | const OPTIONAL = Buffer.from(new ArrayBuffer(0)) | |
| 6 | ||
| 7 | module.exports = exports = { ...binding } | |
| 8 | ||
| 9 | // memory | |
| 10 | ||
| 11 | exports.sodium_memzero = function (buf) { | |
| 12 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 13 | ||
| 14 | binding.sodium_memzero(buf) | |
| 15 | } | |
| 16 | ||
| 17 | exports.sodium_mlock = function (buf) { | |
| 18 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 19 | ||
| 20 | const res = binding.sodium_mlock(buf) | |
| 21 | ||
| 22 | if (res !== 0) throw new Error('memory lock failed') | |
| 23 | } | |
| 24 | ||
| 25 | exports.sodium_munlock = function (buf) { | |
| 26 | assert(ArrayBuffer.isView(buf), 'buf must be a typed array') | |
| 27 | ||
| 28 | const res = binding.sodium_munlock(buf) | |
| 29 | ||
| 30 | if (res !== 0) throw new Error('memory unlock failed') | |
| 31 | } | |
| 32 | ||
| 33 | exports.sodium_malloc = function (size) { | |
| 34 | assert(size >= 0, 'invalid size') | |
| 35 | ||
| 36 | const buf = Buffer.from(binding.sodium_malloc(size)) | |
| 37 | buf.secure = true | |
| 38 | return buf | |
| 39 | } | |
| 40 | ||
| 41 | exports.sodium_free = function (buf) { | |
| 42 | if (!buf || !buf.secure) return | |
| 43 | ||
| 44 | binding.sodium_free(buf.buffer) | |
| 45 | } | |
| 46 | ||
| 47 | exports.sodium_mprotect_noaccess = function (buf) { | |
| 48 | const res = binding.sodium_mprotect_noaccess(buf.buffer) | |
| 49 | ||
| 50 | if (res !== 0) throw new Error('failed to lock buffer') | |
| 51 | } | |
| 52 | ||
| 53 | exports.sodium_mprotect_readonly = function (buf) { | |
| 54 | const res = binding.sodium_mprotect_readonly(buf.buffer) | |
| 55 | ||
| 56 | if (res !== 0) throw new Error('failed to unlock buffer') | |
| 57 | } | |
| 58 | ||
| 59 | exports.sodium_mprotect_readwrite = function (buf) { | |
| 60 | const res = binding.sodium_mprotect_readwrite(buf.buffer) | |
| 61 | ||
| 62 | if (res !== 0) throw new Error('failed to unlock buffer') | |
| 63 | } | |
| 64 | ||
| 65 | // crypto_randombytes | |
| 66 | ||
| 67 | exports.randombytes_buf = function (buffer) { | |
| 68 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 69 | ||
| 70 | binding.randombytes_buf(buffer.buffer, buffer.byteOffset, buffer.byteLength) | |
| 71 | } | |
| 72 | ||
| 73 | exports.randombytes_buf_deterministic = function (buffer, seed) { | |
| 74 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 75 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 76 | assert( | |
| 77 | seed.byteLength === binding.randombytes_SEEDBYTES, | |
| 78 | "seed must be 'randombytes_SEEDBYTES' bytes" | |
| 79 | ) | |
| 80 | ||
| 81 | binding.randombytes_buf_deterministic( | |
| 82 | buffer.buffer, | |
| 83 | buffer.byteOffset, | |
| 84 | buffer.byteLength, | |
| 85 | ||
| 86 | seed.buffer, | |
| 87 | seed.byteOffset, | |
| 88 | seed.byteLength | |
| 89 | ) | |
| 90 | } | |
| 91 | ||
| 92 | // sodium_helpers | |
| 93 | ||
| 94 | exports.sodium_memcmp = function (a, b) { | |
| 95 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 96 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 97 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 98 | ||
| 99 | return binding.sodium_memcmp(a, b) | |
| 100 | } | |
| 101 | ||
| 102 | exports.sodium_add = function (a, b) { | |
| 103 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 104 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 105 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 106 | ||
| 107 | binding.sodium_add(a, b) | |
| 108 | } | |
| 109 | ||
| 110 | exports.sodium_sub = function (a, b) { | |
| 111 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 112 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 113 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 114 | ||
| 115 | binding.sodium_sub(a, b) | |
| 116 | } | |
| 117 | ||
| 118 | exports.sodium_compare = function (a, b) { | |
| 119 | assert(ArrayBuffer.isView(a), 'a must be a typed array') | |
| 120 | assert(ArrayBuffer.isView(b), 'b must be a typed array') | |
| 121 | assert(a.byteLength === b.byteLength, 'buffers must be of same length') | |
| 122 | ||
| 123 | return binding.sodium_compare(a, b) | |
| 124 | } | |
| 125 | ||
| 126 | exports.sodium_is_zero = function (buffer, length) { | |
| 127 | if (length === undefined) length = buffer.byteLength | |
| 128 | ||
| 129 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 130 | assert(length >= 0 && length <= buffer.byteLength, 'invalid length') | |
| 131 | ||
| 132 | return binding.sodium_is_zero(buffer, length) | |
| 133 | } | |
| 134 | ||
| 135 | exports.sodium_pad = function (buffer, unpaddedBuflen, blockSize) { | |
| 136 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 137 | assert(unpaddedBuflen <= buffer.byteLength, 'unpadded length cannot exceed buffer length') | |
| 138 | assert(blockSize <= buffer.byteLength, 'block size cannot exceed buffer length') | |
| 139 | assert(blockSize >= 1, 'block size must be at least 1 byte') | |
| 140 | assert( | |
| 141 | buffer.byteLength >= unpaddedBuflen + (blockSize - (unpaddedBuflen % blockSize)), | |
| 142 | 'buf not long enough' | |
| 143 | ) | |
| 144 | ||
| 145 | return binding.sodium_pad(buffer, unpaddedBuflen, blockSize) | |
| 146 | } | |
| 147 | ||
| 148 | exports.sodium_unpad = function (buffer, paddedBuflen, blockSize) { | |
| 149 | assert(ArrayBuffer.isView(buffer), 'buffer must be a typed array') | |
| 150 | assert(paddedBuflen <= buffer.byteLength, 'unpadded length cannot exceed buffer length') | |
| 151 | assert(blockSize <= buffer.byteLength, 'block size cannot exceed buffer length') | |
| 152 | assert(blockSize >= 1, 'block size must be at least 1 byte') | |
| 153 | ||
| 154 | return binding.sodium_unpad(buffer, paddedBuflen, blockSize) | |
| 155 | } | |
| 156 | ||
| 157 | // crypto_sign | |
| 158 | ||
| 159 | exports.crypto_sign_keypair = function (pk, sk) { | |
| 160 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 161 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 162 | assert( | |
| 163 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 164 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 165 | ) | |
| 166 | assert( | |
| 167 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 168 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 169 | ) | |
| 170 | ||
| 171 | const res = binding.crypto_sign_keypair(pk, sk) | |
| 172 | ||
| 173 | if (res !== 0) throw new Error('status: ' + res) | |
| 174 | } | |
| 175 | ||
| 176 | exports.crypto_sign_seed_keypair = function (pk, sk, seed) { | |
| 177 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 178 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 179 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 180 | assert( | |
| 181 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 182 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 183 | ) | |
| 184 | assert( | |
| 185 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 186 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 187 | ) | |
| 188 | assert( | |
| 189 | seed.byteLength === binding.crypto_sign_SEEDBYTES, | |
| 190 | "seed must be 'crypto_sign_SEEDBYTES' bytes" | |
| 191 | ) | |
| 192 | ||
| 193 | const res = binding.crypto_sign_seed_keypair(pk, sk, seed) | |
| 194 | ||
| 195 | if (res !== 0) throw new Error('status: ' + res) | |
| 196 | } | |
| 197 | ||
| 198 | exports.crypto_sign = function (sm, m, sk) { | |
| 199 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 200 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 201 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 202 | assert( | |
| 203 | sm.byteLength === binding.crypto_sign_BYTES + m.byteLength, | |
| 204 | "sm must be 'm.byteLength + crypto_sign_BYTES' bytes" | |
| 205 | ) | |
| 206 | assert( | |
| 207 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 208 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 209 | ) | |
| 210 | ||
| 211 | const res = binding.crypto_sign(sm, m, sk) | |
| 212 | ||
| 213 | if (res !== 0) throw new Error('status: ' + res) | |
| 214 | } | |
| 215 | ||
| 216 | exports.crypto_sign_open = function (m, sm, pk) { | |
| 217 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 218 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 219 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 220 | assert( | |
| 221 | sm.byteLength >= binding.crypto_sign_BYTES, | |
| 222 | "sm must be at least 'crypto_sign_BYTES' bytes" | |
| 223 | ) | |
| 224 | assert( | |
| 225 | m.byteLength === sm.byteLength - binding.crypto_sign_BYTES, | |
| 226 | "m must be 'sm.byteLength - crypto_sign_BYTES' bytes" | |
| 227 | ) | |
| 228 | assert( | |
| 229 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 230 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 231 | ) | |
| 232 | ||
| 233 | const res = binding.crypto_sign_open(m, sm, pk) | |
| 234 | ||
| 235 | if (res !== 0) throw new Error('status: ' + res) | |
| 236 | } | |
| 237 | ||
| 238 | exports.crypto_sign_open = function (m, sm, pk) { | |
| 239 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 240 | assert(ArrayBuffer.isView(sm), 'sm must be a typed array') | |
| 241 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 242 | assert( | |
| 243 | sm.byteLength >= binding.crypto_sign_BYTES, | |
| 244 | "sm must be at least 'crypto_sign_BYTES' bytes" | |
| 245 | ) | |
| 246 | assert( | |
| 247 | m.byteLength === sm.byteLength - binding.crypto_sign_BYTES, | |
| 248 | "m must be 'sm.byteLength - crypto_sign_BYTES' bytes" | |
| 249 | ) | |
| 250 | assert( | |
| 251 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 252 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 253 | ) | |
| 254 | ||
| 255 | return binding.crypto_sign_open(m, sm, pk) | |
| 256 | } | |
| 257 | ||
| 258 | exports.crypto_sign_detached = function (sig, m, sk) { | |
| 259 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 260 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 261 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 262 | assert(sig.byteLength === binding.crypto_sign_BYTES, "sig must be 'crypto_sign_BYTES' bytes") | |
| 263 | assert( | |
| 264 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 265 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 266 | ) | |
| 267 | ||
| 268 | const res = binding.crypto_sign_detached(sig, m, sk) | |
| 269 | ||
| 270 | if (res !== 0) throw new Error('status: ' + res) | |
| 271 | } | |
| 272 | ||
| 273 | exports.crypto_sign_verify_detached = function (sig, m, pk) { | |
| 274 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 275 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 276 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 277 | assert( | |
| 278 | sig.byteLength >= binding.crypto_sign_BYTES, | |
| 279 | "sig must be at least 'crypto_sign_BYTES' bytes" | |
| 280 | ) | |
| 281 | assert( | |
| 282 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 283 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 284 | ) | |
| 285 | ||
| 286 | return binding.crypto_sign_verify_detached( | |
| 287 | sig.buffer, | |
| 288 | sig.byteOffset, | |
| 289 | sig.byteLength, | |
| 290 | ||
| 291 | m.buffer, | |
| 292 | m.byteOffset, | |
| 293 | m.byteLength, | |
| 294 | ||
| 295 | pk.buffer, | |
| 296 | pk.byteOffset, | |
| 297 | pk.byteLength | |
| 298 | ) | |
| 299 | } | |
| 300 | ||
| 301 | exports.crypto_sign_ed25519_sk_to_pk = function (pk, sk) { | |
| 302 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 303 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 304 | assert( | |
| 305 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 306 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 307 | ) | |
| 308 | assert( | |
| 309 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 310 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 311 | ) | |
| 312 | ||
| 313 | const res = binding.crypto_sign_ed25519_sk_to_pk(pk, sk) | |
| 314 | ||
| 315 | if (res !== 0) throw new Error('status: ' + res) | |
| 316 | } | |
| 317 | ||
| 318 | exports.crypto_sign_ed25519_pk_to_curve25519 = function (x25519pk, ed25519pk) { | |
| 319 | assert(ArrayBuffer.isView(x25519pk), 'x25519pk must be a typed array') | |
| 320 | assert(ArrayBuffer.isView(ed25519pk), 'ed25519pk must be a typed array') | |
| 321 | assert( | |
| 322 | x25519pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 323 | "x25519pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 324 | ) | |
| 325 | assert( | |
| 326 | ed25519pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 327 | "ed25519pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 328 | ) | |
| 329 | ||
| 330 | const res = binding.crypto_sign_ed25519_pk_to_curve25519(x25519pk, ed25519pk) | |
| 331 | ||
| 332 | if (res !== 0) throw new Error('status: ' + res) | |
| 333 | } | |
| 334 | ||
| 335 | exports.crypto_sign_ed25519_sk_to_curve25519 = function (x25519sk, ed25519sk) { | |
| 336 | assert(ArrayBuffer.isView(x25519sk), 'x25519sk must be a typed array') | |
| 337 | assert(ArrayBuffer.isView(ed25519sk), 'ed25519sk must be a typed array') | |
| 338 | assert( | |
| 339 | x25519sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 340 | "x25519sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 341 | ) | |
| 342 | ||
| 343 | const edLen = ed25519sk.byteLength | |
| 344 | ||
| 345 | assert( | |
| 346 | edLen === binding.crypto_sign_SECRETKEYBYTES || edLen === binding.crypto_box_SECRETKEYBYTES, | |
| 347 | "ed25519sk must be 'crypto_sign_SECRETKEYBYTES' or 'crypto_sign_SECRETKEYBYTES - crypto_sign_PUBLICKEYBYTES' bytes" | |
| 348 | ) | |
| 349 | ||
| 350 | const res = binding.crypto_sign_ed25519_sk_to_curve25519(x25519sk, ed25519sk) | |
| 351 | ||
| 352 | if (res !== 0) throw new Error('status: ' + res) | |
| 353 | } | |
| 354 | ||
| 355 | // crypto_box | |
| 356 | ||
| 357 | exports.crypto_box_keypair = function (pk, sk) { | |
| 358 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 359 | assert( | |
| 360 | pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 361 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 362 | ) | |
| 363 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 364 | assert( | |
| 365 | sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 366 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 367 | ) | |
| 368 | ||
| 369 | const res = binding.crypto_box_keypair(pk, sk) | |
| 370 | ||
| 371 | if (res !== 0) throw new Error('status: ' + res) | |
| 372 | } | |
| 373 | ||
| 374 | exports.crypto_box_seed_keypair = function (pk, sk, seed) { | |
| 375 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 376 | assert( | |
| 377 | pk.byteLength === binding.crypto_box_PUBLICKEYBYTES, | |
| 378 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 379 | ) | |
| 380 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 381 | assert( | |
| 382 | sk.byteLength === binding.crypto_box_SECRETKEYBYTES, | |
| 383 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 384 | ) | |
| 385 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 386 | assert( | |
| 387 | seed.byteLength === binding.crypto_box_SEEDBYTES, | |
| 388 | "seed must be 'crypto_box_SEEDBYTES' bytes" | |
| 389 | ) | |
| 390 | ||
| 391 | const res = binding.crypto_box_seed_keypair(pk, sk, seed) | |
| 392 | ||
| 393 | if (res !== 0) throw new Error('status: ' + res) | |
| 394 | } | |
| 395 | ||
| 396 | exports.crypto_box_easy = function (c, m, n, pk, sk) { | |
| 397 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 398 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 399 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 400 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 401 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 402 | assert( | |
| 403 | c.byteLength === m.byteLength + exports.crypto_box_MACBYTES, | |
| 404 | "c must be 'm.byteLength + crypto_box_MACBYTES' bytes" | |
| 405 | ) | |
| 406 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 407 | assert( | |
| 408 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 409 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 410 | ) | |
| 411 | assert( | |
| 412 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 413 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 414 | ) | |
| 415 | ||
| 416 | const res = binding.crypto_box_easy(c, m, n, pk, sk) | |
| 417 | ||
| 418 | if (res !== 0) throw new Error('status: ' + res) | |
| 419 | } | |
| 420 | ||
| 421 | exports.crypto_box_detached = function (c, mac, m, n, pk, sk) { | |
| 422 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 423 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 424 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 425 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 426 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 427 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 428 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 429 | assert(mac.byteLength === exports.crypto_box_MACBYTES, "mac must be 'crypto_box_MACBYTES' bytes") | |
| 430 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 431 | assert( | |
| 432 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 433 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 434 | ) | |
| 435 | assert( | |
| 436 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 437 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 438 | ) | |
| 439 | ||
| 440 | const res = binding.crypto_box_detached(c, mac, m, n, pk, sk) | |
| 441 | ||
| 442 | if (res !== 0) throw new Error('status: ' + res) | |
| 443 | } | |
| 444 | ||
| 445 | exports.crypto_box_open_easy = function (m, c, n, pk, sk) { | |
| 446 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 447 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 448 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 449 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 450 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 451 | assert( | |
| 452 | c.byteLength >= exports.crypto_box_MACBYTES, | |
| 453 | "c must be at least 'crypto_box_MACBYTES' bytes" | |
| 454 | ) | |
| 455 | assert( | |
| 456 | m.byteLength === c.byteLength - exports.crypto_box_MACBYTES, | |
| 457 | "m must be 'c.byteLength - crypto_box_MACBYTES' bytes" | |
| 458 | ) | |
| 459 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 460 | assert( | |
| 461 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 462 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 463 | ) | |
| 464 | assert( | |
| 465 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 466 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 467 | ) | |
| 468 | ||
| 469 | return binding.crypto_box_open_easy(m, c, n, pk, sk) | |
| 470 | } | |
| 471 | ||
| 472 | exports.crypto_box_open_detached = function (m, c, mac, n, pk, sk) { | |
| 473 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 474 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 475 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 476 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 477 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 478 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 479 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 480 | assert(mac.byteLength === exports.crypto_box_MACBYTES, "mac must be 'crypto_box_MACBYTES' bytes") | |
| 481 | assert(n.byteLength === exports.crypto_box_NONCEBYTES, "n must be 'crypto_box_NONCEBYTES' bytes") | |
| 482 | assert( | |
| 483 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 484 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 485 | ) | |
| 486 | assert( | |
| 487 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 488 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 489 | ) | |
| 490 | ||
| 491 | return binding.crypto_box_open_detached(m, c, mac, n, pk, sk) | |
| 492 | } | |
| 493 | ||
| 494 | exports.crypto_box_seal = function (c, m, pk) { | |
| 495 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 496 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 497 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 498 | assert( | |
| 499 | c.byteLength === m.byteLength + exports.crypto_box_SEALBYTES, | |
| 500 | "c must be 'm.byteLength + crypto_box_SEALBYTES' bytes" | |
| 501 | ) | |
| 502 | assert( | |
| 503 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 504 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 505 | ) | |
| 506 | ||
| 507 | const res = binding.crypto_box_seal(c, m, pk) | |
| 508 | ||
| 509 | if (res !== 0) throw new Error('status: ' + res) | |
| 510 | } | |
| 511 | ||
| 512 | exports.crypto_box_seal_open = function (m, c, pk, sk) { | |
| 513 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 514 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 515 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 516 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 517 | assert( | |
| 518 | c.byteLength >= exports.crypto_box_SEALBYTES, | |
| 519 | "c must be at least 'crypto_box_SEALBYTES' bytes" | |
| 520 | ) | |
| 521 | assert( | |
| 522 | m.byteLength === c.byteLength - exports.crypto_box_SEALBYTES, | |
| 523 | "m must be 'c.byteLength - crypto_box_SEALBYTES' bytes" | |
| 524 | ) | |
| 525 | assert( | |
| 526 | pk.byteLength === exports.crypto_box_PUBLICKEYBYTES, | |
| 527 | "pk must be 'crypto_box_PUBLICKEYBYTES' bytes" | |
| 528 | ) | |
| 529 | assert( | |
| 530 | sk.byteLength === exports.crypto_box_SECRETKEYBYTES, | |
| 531 | "sk must be 'crypto_box_SECRETKEYBYTES' bytes" | |
| 532 | ) | |
| 533 | ||
| 534 | return binding.crypto_box_seal_open( | |
| 535 | m.buffer, | |
| 536 | m.byteOffset, | |
| 537 | m.byteLength, | |
| 538 | ||
| 539 | c.buffer, | |
| 540 | c.byteOffset, | |
| 541 | c.byteLength, | |
| 542 | ||
| 543 | pk.buffer, | |
| 544 | pk.byteOffset, | |
| 545 | pk.byteLength, | |
| 546 | ||
| 547 | sk.buffer, | |
| 548 | sk.byteOffset, | |
| 549 | sk.byteLength | |
| 550 | ) | |
| 551 | } | |
| 552 | ||
| 553 | // crypto_secretbox | |
| 554 | ||
| 555 | exports.crypto_secretbox_easy = function (c, m, n, k) { | |
| 556 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 557 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 558 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 559 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 560 | assert( | |
| 561 | c.byteLength === m.byteLength + binding.crypto_secretbox_MACBYTES, | |
| 562 | "c must be 'm.byteLength + crypto_secretbox_MACBYTES' bytes" | |
| 563 | ) | |
| 564 | assert( | |
| 565 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 566 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 567 | ) | |
| 568 | assert( | |
| 569 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 570 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 571 | ) | |
| 572 | ||
| 573 | const res = binding.crypto_secretbox_easy(c, m, n, k) | |
| 574 | ||
| 575 | if (res !== 0) throw new Error('status: ' + res) | |
| 576 | } | |
| 577 | ||
| 578 | exports.crypto_secretbox_open_easy = function (m, c, n, k) { | |
| 579 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 580 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 581 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 582 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 583 | assert( | |
| 584 | c.byteLength >= binding.crypto_secretbox_MACBYTES, | |
| 585 | "c must be at least 'crypto_secretbox_MACBYTES' bytes" | |
| 586 | ) | |
| 587 | assert( | |
| 588 | m.byteLength === c.byteLength - binding.crypto_secretbox_MACBYTES, | |
| 589 | "m must be 'c.byteLength - crypto_secretbox_MACBYTES' bytes" | |
| 590 | ) | |
| 591 | assert( | |
| 592 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 593 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 594 | ) | |
| 595 | assert( | |
| 596 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 597 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 598 | ) | |
| 599 | ||
| 600 | return binding.crypto_secretbox_open_easy(m, c, n, k) | |
| 601 | } | |
| 602 | ||
| 603 | exports.crypto_secretbox_detached = function (c, mac, m, n, k) { | |
| 604 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 605 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 606 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 607 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 608 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 609 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 610 | assert( | |
| 611 | mac.byteLength === binding.crypto_secretbox_MACBYTES, | |
| 612 | "mac must be 'crypto_secretbox_MACBYTES' bytes" | |
| 613 | ) | |
| 614 | assert( | |
| 615 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 616 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 617 | ) | |
| 618 | assert( | |
| 619 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 620 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 621 | ) | |
| 622 | ||
| 623 | const res = binding.crypto_secretbox_detached(c, mac, m, n, k) | |
| 624 | ||
| 625 | if (res !== 0) throw new Error('status: ' + res) | |
| 626 | } | |
| 627 | ||
| 628 | exports.crypto_secretbox_open_detached = function (m, c, mac, n, k) { | |
| 629 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 630 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 631 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 632 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 633 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 634 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 635 | assert( | |
| 636 | mac.byteLength === binding.crypto_secretbox_MACBYTES, | |
| 637 | "mac must be 'crypto_secretbox_MACBYTES' bytes" | |
| 638 | ) | |
| 639 | assert( | |
| 640 | n.byteLength === binding.crypto_secretbox_NONCEBYTES, | |
| 641 | "n must be 'crypto_secretbox_NONCEBYTES' bytes" | |
| 642 | ) | |
| 643 | assert( | |
| 644 | k.byteLength === binding.crypto_secretbox_KEYBYTES, | |
| 645 | "k must be 'crypto_secretbox_KEYBYTES' bytes" | |
| 646 | ) | |
| 647 | ||
| 648 | return binding.crypto_secretbox_open_detached(m, c, mac, n, k) | |
| 649 | } | |
| 650 | ||
| 651 | // crypto_generichash | |
| 652 | ||
| 653 | exports.crypto_generichash = function (output, input, key) { | |
| 654 | if (!key) key = OPTIONAL | |
| 655 | ||
| 656 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 657 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 658 | assert( | |
| 659 | output.byteLength >= binding.crypto_generichash_BYTES_MIN && | |
| 660 | output.byteLength <= binding.crypto_generichash_BYTES_MAX, | |
| 661 | 'output must be between crypto_generichash_BYTES_MIN and crypto_generichash_BYTES_MAX bytes' | |
| 662 | ) | |
| 663 | ||
| 664 | if (key !== OPTIONAL) { | |
| 665 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 666 | assert( | |
| 667 | key.byteLength >= binding.crypto_generichash_KEYBYTES_MIN && | |
| 668 | key.byteLength <= binding.crypto_generichash_KEYBYTES_MAX, | |
| 669 | 'key must be between crypto_generichash_KEYBYTES_MIN and crypto_generichash_KEYBYTES_MAX bytes' | |
| 670 | ) | |
| 671 | } | |
| 672 | ||
| 673 | const res = binding.crypto_generichash( | |
| 674 | output.buffer, | |
| 675 | output.byteOffset, | |
| 676 | output.byteLength, | |
| 677 | ||
| 678 | input.buffer, | |
| 679 | input.byteOffset, | |
| 680 | input.byteLength, | |
| 681 | ||
| 682 | key.buffer, | |
| 683 | key.byteOffset, | |
| 684 | key.byteLength | |
| 685 | ) | |
| 686 | ||
| 687 | if (res !== 0) throw new Error('status: ' + res) | |
| 688 | } | |
| 689 | ||
| 690 | exports.crypto_generichash_batch = function (output, batch, key) { | |
| 691 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 692 | ||
| 693 | if (isNode || batch.length < 4) { | |
| 694 | const res = binding.crypto_generichash_batch(output, batch, !!key, key || OPTIONAL) | |
| 695 | if (res !== 0) throw new Error('status: ' + res) | |
| 696 | } else { | |
| 697 | const state = Buffer.alloc(binding.crypto_generichash_STATEBYTES) | |
| 698 | ||
| 699 | exports.crypto_generichash_init(state, key, output.byteLength) | |
| 700 | ||
| 701 | for (const buf of batch) { | |
| 702 | exports.crypto_generichash_update(state, buf) | |
| 703 | } | |
| 704 | ||
| 705 | exports.crypto_generichash_final(state, output) | |
| 706 | } | |
| 707 | } | |
| 708 | ||
| 709 | exports.crypto_generichash_keygen = function (key) { | |
| 710 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 711 | assert( | |
| 712 | key.byteLength === binding.crypto_generichash_KEYBYTES, | |
| 713 | "key must be 'crypto_generichash_KEYBYTES' bytes" | |
| 714 | ) | |
| 715 | ||
| 716 | const res = binding.crypto_generichash_keygen(key.buffer, key.byteOffset, key.byteLength) | |
| 717 | ||
| 718 | if (res !== 0) throw new Error('status: ' + res) | |
| 719 | } | |
| 720 | ||
| 721 | exports.crypto_generichash_init = function (state, key, outputLength) { | |
| 722 | if (!key) key = OPTIONAL | |
| 723 | ||
| 724 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 725 | assert( | |
| 726 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 727 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 728 | ) | |
| 729 | ||
| 730 | const res = binding.crypto_generichash_init( | |
| 731 | state.buffer, | |
| 732 | state.byteOffset, | |
| 733 | state.byteLength, | |
| 734 | ||
| 735 | key.buffer, | |
| 736 | key.byteOffset, | |
| 737 | key.byteLength, | |
| 738 | ||
| 739 | outputLength | |
| 740 | ) | |
| 741 | ||
| 742 | if (res !== 0) throw new Error('status: ' + res) | |
| 743 | } | |
| 744 | ||
| 745 | exports.crypto_generichash_update = function (state, input) { | |
| 746 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 747 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 748 | assert( | |
| 749 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 750 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 751 | ) | |
| 752 | ||
| 753 | const res = binding.crypto_generichash_update( | |
| 754 | state.buffer, | |
| 755 | state.byteOffset, | |
| 756 | state.byteLength, | |
| 757 | ||
| 758 | input.buffer, | |
| 759 | input.byteOffset, | |
| 760 | input.byteLength | |
| 761 | ) | |
| 762 | ||
| 763 | if (res !== 0) throw new Error('status: ' + res) | |
| 764 | } | |
| 765 | ||
| 766 | exports.crypto_generichash_final = function (state, output) { | |
| 767 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 768 | assert(ArrayBuffer.isView(output), 'output must be a typed array') | |
| 769 | assert( | |
| 770 | state.byteLength === binding.crypto_generichash_STATEBYTES, | |
| 771 | "state must be 'crypto_generichash_STATEBYTES' bytes" | |
| 772 | ) | |
| 773 | ||
| 774 | const res = binding.crypto_generichash_final( | |
| 775 | state.buffer, | |
| 776 | state.byteOffset, | |
| 777 | state.byteLength, | |
| 778 | ||
| 779 | output.buffer, | |
| 780 | output.byteOffset, | |
| 781 | output.byteLength | |
| 782 | ) | |
| 783 | ||
| 784 | if (res !== 0) throw new Error('status: ' + res) | |
| 785 | } | |
| 786 | ||
| 787 | // secretstream | |
| 788 | ||
| 789 | exports.crypto_secretstream_xchacha20poly1305_keygen = function (k) { | |
| 790 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 791 | assert( | |
| 792 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 793 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 794 | ) | |
| 795 | ||
| 796 | binding.crypto_secretstream_xchacha20poly1305_keygen(k.buffer, k.byteOffset, k.byteLength) | |
| 797 | } | |
| 798 | ||
| 799 | exports.crypto_secretstream_xchacha20poly1305_init_push = function (state, header, k) { | |
| 800 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 801 | assert(ArrayBuffer.isView(header), 'header must be a typed array') | |
| 802 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 803 | assert( | |
| 804 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 805 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 806 | ) | |
| 807 | assert( | |
| 808 | header.byteLength === binding.crypto_secretstream_xchacha20poly1305_HEADERBYTES, | |
| 809 | "header must be 'crypto_secretstream_xchacha20poly1305_HEADERBYTES' bytes" | |
| 810 | ) | |
| 811 | assert( | |
| 812 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 813 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 814 | ) | |
| 815 | ||
| 816 | const res = binding.crypto_secretstream_xchacha20poly1305_init_push( | |
| 817 | state.buffer, | |
| 818 | state.byteOffset, | |
| 819 | state.byteLength, | |
| 820 | ||
| 821 | header.buffer, | |
| 822 | header.byteOffset, | |
| 823 | header.byteLength, | |
| 824 | ||
| 825 | k.buffer, | |
| 826 | k.byteOffset, | |
| 827 | k.byteLength | |
| 828 | ) | |
| 829 | ||
| 830 | if (res !== 0) throw new Error('status: ' + res) | |
| 831 | } | |
| 832 | ||
| 833 | exports.crypto_secretstream_xchacha20poly1305_init_pull = function (state, header, k) { | |
| 834 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 835 | assert(ArrayBuffer.isView(header), 'header must be a typed array') | |
| 836 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 837 | assert( | |
| 838 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 839 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 840 | ) | |
| 841 | assert( | |
| 842 | header.byteLength === binding.crypto_secretstream_xchacha20poly1305_HEADERBYTES, | |
| 843 | "header must be 'crypto_secretstream_xchacha20poly1305_HEADERBYTES' bytes" | |
| 844 | ) | |
| 845 | assert( | |
| 846 | k.byteLength === binding.crypto_secretstream_xchacha20poly1305_KEYBYTES, | |
| 847 | "k must be 'crypto_secretstream_xchacha20poly1305_KEYBYTES' bytes" | |
| 848 | ) | |
| 849 | ||
| 850 | const res = binding.crypto_secretstream_xchacha20poly1305_init_pull( | |
| 851 | state.buffer, | |
| 852 | state.byteOffset, | |
| 853 | state.byteLength, | |
| 854 | ||
| 855 | header.buffer, | |
| 856 | header.byteOffset, | |
| 857 | header.byteLength, | |
| 858 | ||
| 859 | k.buffer, | |
| 860 | k.byteOffset, | |
| 861 | k.byteLength | |
| 862 | ) | |
| 863 | ||
| 864 | if (res !== 0) throw new Error('status: ' + res) | |
| 865 | } | |
| 866 | ||
| 867 | exports.crypto_secretstream_xchacha20poly1305_push = function (state, c, m, ad, tag) { | |
| 868 | if (!ad) ad = OPTIONAL | |
| 869 | ||
| 870 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 871 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 872 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 873 | assert( | |
| 874 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 875 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 876 | ) | |
| 877 | assert( | |
| 878 | c.byteLength === m.byteLength + binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 879 | "c must be 'm.byteLength + crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 880 | ) | |
| 881 | ||
| 882 | const res = binding.crypto_secretstream_xchacha20poly1305_push( | |
| 883 | state.buffer, | |
| 884 | state.byteOffset, | |
| 885 | state.byteLength, | |
| 886 | ||
| 887 | c.buffer, | |
| 888 | c.byteOffset, | |
| 889 | c.byteLength, | |
| 890 | ||
| 891 | m.buffer, | |
| 892 | m.byteOffset, | |
| 893 | m.byteLength, | |
| 894 | ||
| 895 | ad.buffer, | |
| 896 | ad.byteOffset, | |
| 897 | ad.byteLength, | |
| 898 | ||
| 899 | tag | |
| 900 | ) | |
| 901 | ||
| 902 | if (res < 0) throw new Error('push failed') | |
| 903 | ||
| 904 | return res | |
| 905 | } | |
| 906 | ||
| 907 | exports.crypto_secretstream_xchacha20poly1305_pull = function (state, m, tag, c, ad) { | |
| 908 | if (!ad) ad = OPTIONAL | |
| 909 | ||
| 910 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 911 | assert( | |
| 912 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 913 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 914 | ) | |
| 915 | assert(ArrayBuffer.isView(tag), 'tag must be a typed array') | |
| 916 | assert(tag.byteLength === 1, 'tag must be 1 byte') | |
| 917 | ||
| 918 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 919 | assert( | |
| 920 | c.byteLength >= binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 921 | "c must be at least 'crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 922 | ) | |
| 923 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 924 | assert( | |
| 925 | m.byteLength === c.byteLength - binding.crypto_secretstream_xchacha20poly1305_ABYTES, | |
| 926 | "m must be 'c.byteLength - crypto_secretstream_xchacha20poly1305_ABYTES' bytes" | |
| 927 | ) | |
| 928 | ||
| 929 | const res = binding.crypto_secretstream_xchacha20poly1305_pull( | |
| 930 | state.buffer, | |
| 931 | state.byteOffset, | |
| 932 | state.byteLength, | |
| 933 | ||
| 934 | m.buffer, | |
| 935 | m.byteOffset, | |
| 936 | m.byteLength, | |
| 937 | ||
| 938 | tag.buffer, | |
| 939 | tag.byteOffset, | |
| 940 | tag.byteLength, | |
| 941 | ||
| 942 | c.buffer, | |
| 943 | c.byteOffset, | |
| 944 | c.byteLength, | |
| 945 | ||
| 946 | ad.buffer, | |
| 947 | ad.byteOffset, | |
| 948 | ad.byteLength | |
| 949 | ) | |
| 950 | ||
| 951 | if (res < 0) throw new Error('pull failed') | |
| 952 | ||
| 953 | return res | |
| 954 | } | |
| 955 | ||
| 956 | exports.crypto_secretstream_xchacha20poly1305_rekey = function (state) { | |
| 957 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 958 | assert( | |
| 959 | state.byteLength === binding.crypto_secretstream_xchacha20poly1305_STATEBYTES, | |
| 960 | "state must be 'crypto_secretstream_xchacha20poly1305_STATEBYTES' bytes" | |
| 961 | ) | |
| 962 | ||
| 963 | binding.crypto_secretstream_xchacha20poly1305_rekey( | |
| 964 | state.buffer, | |
| 965 | state.byteOffset, | |
| 966 | state.byteLength | |
| 967 | ) | |
| 968 | } | |
| 969 | ||
| 970 | // crypto_stream | |
| 971 | ||
| 972 | exports.crypto_stream = function (c, n, k) { | |
| 973 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 974 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 975 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 976 | assert( | |
| 977 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 978 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 979 | ) | |
| 980 | assert( | |
| 981 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 982 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 983 | ) | |
| 984 | ||
| 985 | const res = binding.crypto_stream(c, n, k) | |
| 986 | ||
| 987 | if (res !== 0) throw new Error('status: ' + res) | |
| 988 | } | |
| 989 | ||
| 990 | exports.crypto_stream_xor = function (c, m, n, k) { | |
| 991 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 992 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 993 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 994 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 995 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 996 | assert( | |
| 997 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 998 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 999 | ) | |
| 1000 | assert( | |
| 1001 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 1002 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 1003 | ) | |
| 1004 | ||
| 1005 | const res = binding.crypto_stream_xor( | |
| 1006 | c.buffer, | |
| 1007 | c.byteOffset, | |
| 1008 | c.byteLength, | |
| 1009 | ||
| 1010 | m.buffer, | |
| 1011 | m.byteOffset, | |
| 1012 | m.byteLength, | |
| 1013 | ||
| 1014 | n.buffer, | |
| 1015 | n.byteOffset, | |
| 1016 | n.byteLength, | |
| 1017 | ||
| 1018 | k.buffer, | |
| 1019 | k.byteOffset, | |
| 1020 | k.byteLength | |
| 1021 | ) | |
| 1022 | ||
| 1023 | if (res !== 0) throw new Error('status: ' + res) | |
| 1024 | } | |
| 1025 | ||
| 1026 | exports.crypto_stream_chacha20 = function (c, n, k) { | |
| 1027 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1028 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1029 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1030 | assert( | |
| 1031 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1032 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1033 | ) | |
| 1034 | assert( | |
| 1035 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1036 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1037 | ) | |
| 1038 | ||
| 1039 | const res = binding.crypto_stream_chacha20(c, n, k) | |
| 1040 | ||
| 1041 | if (res !== 0) throw new Error('status: ' + res) | |
| 1042 | } | |
| 1043 | ||
| 1044 | exports.crypto_stream_chacha20_xor = function (c, m, n, k) { | |
| 1045 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1046 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1047 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1048 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1049 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1050 | assert( | |
| 1051 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1052 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1053 | ) | |
| 1054 | assert( | |
| 1055 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1056 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1057 | ) | |
| 1058 | ||
| 1059 | const res = binding.crypto_stream_chacha20_xor(c, m, n, k) | |
| 1060 | ||
| 1061 | if (res !== 0) throw new Error('status: ' + res) | |
| 1062 | } | |
| 1063 | ||
| 1064 | exports.crypto_stream_chacha20_xor_ic = function (c, m, n, ic, k) { | |
| 1065 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1066 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1067 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1068 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1069 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1070 | assert( | |
| 1071 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 1072 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 1073 | ) | |
| 1074 | assert( | |
| 1075 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 1076 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 1077 | ) | |
| 1078 | ||
| 1079 | const res = binding.crypto_stream_chacha20_xor_ic(c, m, n, ic, k) | |
| 1080 | ||
| 1081 | if (res !== 0) throw new Error('status: ' + res) | |
| 1082 | } | |
| 1083 | ||
| 1084 | exports.crypto_stream_chacha20_ietf = function (c, n, k) { | |
| 1085 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1086 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1087 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1088 | assert( | |
| 1089 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1090 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1091 | ) | |
| 1092 | assert( | |
| 1093 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1094 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1095 | ) | |
| 1096 | ||
| 1097 | const res = binding.crypto_stream_chacha20_ietf(c, n, k) | |
| 1098 | ||
| 1099 | if (res !== 0) throw new Error('status: ' + res) | |
| 1100 | } | |
| 1101 | ||
| 1102 | exports.crypto_stream_chacha20_ietf_xor = function (c, m, n, k) { | |
| 1103 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1104 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1105 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1106 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1107 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1108 | assert( | |
| 1109 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1110 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1111 | ) | |
| 1112 | assert( | |
| 1113 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1114 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1115 | ) | |
| 1116 | ||
| 1117 | const res = binding.crypto_stream_chacha20_ietf_xor(c, m, n, k) | |
| 1118 | ||
| 1119 | if (res !== 0) throw new Error('status: ' + res) | |
| 1120 | } | |
| 1121 | ||
| 1122 | exports.crypto_stream_chacha20_ietf_xor_ic = function (c, m, n, ic, k) { | |
| 1123 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1124 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1125 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1126 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1127 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1128 | assert( | |
| 1129 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 1130 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 1131 | ) | |
| 1132 | assert( | |
| 1133 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 1134 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 1135 | ) | |
| 1136 | ||
| 1137 | const res = binding.crypto_stream_chacha20_ietf_xor_ic(c, m, n, ic, k) | |
| 1138 | ||
| 1139 | if (res !== 0) throw new Error('status: ' + res) | |
| 1140 | } | |
| 1141 | ||
| 1142 | exports.crypto_stream_xchacha20 = function (c, n, k) { | |
| 1143 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1144 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1145 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1146 | assert( | |
| 1147 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1148 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1149 | ) | |
| 1150 | assert( | |
| 1151 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1152 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1153 | ) | |
| 1154 | ||
| 1155 | const res = binding.crypto_stream_xchacha20(c, n, k) | |
| 1156 | ||
| 1157 | if (res !== 0) throw new Error('status: ' + res) | |
| 1158 | } | |
| 1159 | ||
| 1160 | exports.crypto_stream_xchacha20_xor = function (c, m, n, k) { | |
| 1161 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1162 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1163 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1164 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1165 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1166 | assert( | |
| 1167 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1168 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1169 | ) | |
| 1170 | assert( | |
| 1171 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1172 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1173 | ) | |
| 1174 | ||
| 1175 | const res = binding.crypto_stream_xchacha20_xor(c, m, n, k) | |
| 1176 | ||
| 1177 | if (res !== 0) throw new Error('status: ' + res) | |
| 1178 | } | |
| 1179 | ||
| 1180 | exports.crypto_stream_xchacha20_xor_ic = function (c, m, n, ic, k) { | |
| 1181 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1182 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1183 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1184 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1185 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1186 | assert( | |
| 1187 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 1188 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 1189 | ) | |
| 1190 | assert( | |
| 1191 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 1192 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 1193 | ) | |
| 1194 | ||
| 1195 | const res = binding.crypto_stream_xchacha20_xor_ic(c, m, n, ic, k) | |
| 1196 | ||
| 1197 | if (res !== 0) throw new Error('status: ' + res) | |
| 1198 | } | |
| 1199 | ||
| 1200 | exports.crypto_stream_salsa20 = function (c, n, k) { | |
| 1201 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1202 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1203 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1204 | assert( | |
| 1205 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1206 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1207 | ) | |
| 1208 | assert( | |
| 1209 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1210 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1211 | ) | |
| 1212 | ||
| 1213 | const res = binding.crypto_stream_salsa20(c, n, k) | |
| 1214 | ||
| 1215 | if (res !== 0) throw new Error('status: ' + res) | |
| 1216 | } | |
| 1217 | ||
| 1218 | exports.crypto_stream_salsa20_xor = function (c, m, n, k) { | |
| 1219 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1220 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1221 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1222 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1223 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1224 | assert( | |
| 1225 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1226 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1227 | ) | |
| 1228 | assert( | |
| 1229 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1230 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1231 | ) | |
| 1232 | ||
| 1233 | const res = binding.crypto_stream_salsa20_xor(c, m, n, k) | |
| 1234 | ||
| 1235 | if (res !== 0) throw new Error('status: ' + res) | |
| 1236 | } | |
| 1237 | ||
| 1238 | exports.crypto_stream_salsa20_xor_ic = function (c, m, n, ic, k) { | |
| 1239 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 1240 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 1241 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 1242 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1243 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 1244 | assert( | |
| 1245 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 1246 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 1247 | ) | |
| 1248 | assert( | |
| 1249 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 1250 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 1251 | ) | |
| 1252 | ||
| 1253 | const res = binding.crypto_stream_salsa20_xor_ic(c, m, n, ic, k) | |
| 1254 | ||
| 1255 | if (res !== 0) throw new Error('status: ' + res) | |
| 1256 | } | |
| 1257 | ||
| 1258 | // crypto_auth | |
| 1259 | ||
| 1260 | exports.crypto_auth = function (out, input, k) { | |
| 1261 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1262 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1263 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1264 | assert(out.byteLength === binding.crypto_auth_BYTES, "out must be 'crypto_auth_BYTES' bytes") | |
| 1265 | assert(k.byteLength === binding.crypto_auth_KEYBYTES, "k must be 'crypto_auth_KEYBYTES' bytes") | |
| 1266 | ||
| 1267 | const res = binding.crypto_auth(out, input, k) | |
| 1268 | ||
| 1269 | if (res !== 0) throw new Error('status: ' + res) | |
| 1270 | } | |
| 1271 | ||
| 1272 | exports.crypto_auth_verify = function (h, input, k) { | |
| 1273 | assert(ArrayBuffer.isView(h), 'h must be a typed array') | |
| 1274 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1275 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1276 | assert(h.byteLength === binding.crypto_auth_BYTES, "h must be 'crypto_auth_BYTES' bytes") | |
| 1277 | assert(k.byteLength === binding.crypto_auth_KEYBYTES, "k must be 'crypto_auth_KEYBYTES' bytes") | |
| 1278 | ||
| 1279 | return binding.crypto_auth_verify(h, input, k) | |
| 1280 | } | |
| 1281 | ||
| 1282 | // crypto_onetimeauth | |
| 1283 | ||
| 1284 | exports.crypto_onetimeauth = function (out, input, k) { | |
| 1285 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1286 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1287 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1288 | assert( | |
| 1289 | out.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1290 | "out must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1291 | ) | |
| 1292 | assert( | |
| 1293 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1294 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1295 | ) | |
| 1296 | ||
| 1297 | const res = binding.crypto_onetimeauth(out, input, k) | |
| 1298 | ||
| 1299 | if (res !== 0) throw new Error('status: ' + res) | |
| 1300 | } | |
| 1301 | ||
| 1302 | exports.crypto_onetimeauth_init = function (state, k) { | |
| 1303 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1304 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1305 | assert( | |
| 1306 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1307 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1308 | ) | |
| 1309 | assert( | |
| 1310 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1311 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1312 | ) | |
| 1313 | ||
| 1314 | const res = binding.crypto_onetimeauth_init(state, k) | |
| 1315 | ||
| 1316 | if (res !== 0) throw new Error('status: ' + res) | |
| 1317 | } | |
| 1318 | ||
| 1319 | exports.crypto_onetimeauth_update = function (state, input) { | |
| 1320 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1321 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1322 | assert( | |
| 1323 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1324 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1325 | ) | |
| 1326 | ||
| 1327 | const res = binding.crypto_onetimeauth_update(state, input) | |
| 1328 | ||
| 1329 | if (res !== 0) throw new Error('status: ' + res) | |
| 1330 | } | |
| 1331 | ||
| 1332 | exports.crypto_onetimeauth_final = function (state, out) { | |
| 1333 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 1334 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1335 | assert( | |
| 1336 | state.byteLength === binding.crypto_onetimeauth_STATEBYTES, | |
| 1337 | "state must be 'crypto_onetimeauth_STATEBYTES' bytes" | |
| 1338 | ) | |
| 1339 | assert( | |
| 1340 | out.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1341 | "out must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1342 | ) | |
| 1343 | ||
| 1344 | const res = binding.crypto_onetimeauth_final(state, out) | |
| 1345 | ||
| 1346 | if (res !== 0) throw new Error('status: ' + res) | |
| 1347 | } | |
| 1348 | ||
| 1349 | exports.crypto_onetimeauth_verify = function (h, input, k) { | |
| 1350 | assert(ArrayBuffer.isView(h), 'h must be a typed array') | |
| 1351 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 1352 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 1353 | assert( | |
| 1354 | h.byteLength === binding.crypto_onetimeauth_BYTES, | |
| 1355 | "h must be 'crypto_onetimeauth_BYTES' bytes" | |
| 1356 | ) | |
| 1357 | assert( | |
| 1358 | k.byteLength === binding.crypto_onetimeauth_KEYBYTES, | |
| 1359 | "k must be 'crypto_onetimeauth_KEYBYTES' bytes" | |
| 1360 | ) | |
| 1361 | ||
| 1362 | return binding.crypto_onetimeauth_verify(h, input, k) | |
| 1363 | } | |
| 1364 | ||
| 1365 | // crypto_pwhash | |
| 1366 | ||
| 1367 | exports.crypto_pwhash = function (out, passwd, salt, opslimit, memlimit, alg) { | |
| 1368 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1369 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1370 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1371 | assert( | |
| 1372 | out.byteLength >= binding.crypto_pwhash_BYTES_MIN, | |
| 1373 | "out must be at least 'crypto_pwhash_BYTES_MIN' bytes" | |
| 1374 | ) | |
| 1375 | assert( | |
| 1376 | out.byteLength <= binding.crypto_pwhash_BYTES_MAX, | |
| 1377 | "out must be at most 'crypto_pwhash_BYTES_MAX' bytes" | |
| 1378 | ) | |
| 1379 | assert( | |
| 1380 | salt.byteLength === binding.crypto_pwhash_SALTBYTES, | |
| 1381 | "salt must be 'crypto_pwhash_SALTBYTES' bytes" | |
| 1382 | ) | |
| 1383 | assert( | |
| 1384 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1385 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1386 | ) | |
| 1387 | assert( | |
| 1388 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1389 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1390 | ) | |
| 1391 | assert( | |
| 1392 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1393 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1394 | ) | |
| 1395 | assert( | |
| 1396 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1397 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1398 | ) | |
| 1399 | assert(alg >= 1 && alg <= 2, 'alg must be either Argon2i 1.3 or Argon2id 1.3') | |
| 1400 | ||
| 1401 | const res = binding.crypto_pwhash(out, passwd, salt, opslimit, memlimit, alg) | |
| 1402 | ||
| 1403 | if (res !== 0) throw new Error('status: ' + res) | |
| 1404 | } | |
| 1405 | ||
| 1406 | exports.crypto_pwhash_async = function ( | |
| 1407 | out, | |
| 1408 | passwd, | |
| 1409 | salt, | |
| 1410 | opslimit, | |
| 1411 | memlimit, | |
| 1412 | alg, | |
| 1413 | callback = undefined | |
| 1414 | ) { | |
| 1415 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1416 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1417 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1418 | assert( | |
| 1419 | out.byteLength >= binding.crypto_pwhash_BYTES_MIN, | |
| 1420 | "out must be at least 'crypto_pwhash_BYTES_MIN' bytes" | |
| 1421 | ) | |
| 1422 | assert( | |
| 1423 | out.byteLength <= binding.crypto_pwhash_BYTES_MAX, | |
| 1424 | "out must be at most 'crypto_pwhash_BYTES_MAX' bytes" | |
| 1425 | ) | |
| 1426 | assert( | |
| 1427 | salt.byteLength === binding.crypto_pwhash_SALTBYTES, | |
| 1428 | "salt must be 'crypto_pwhash_SALTBYTES' bytes" | |
| 1429 | ) | |
| 1430 | assert( | |
| 1431 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1432 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1433 | ) | |
| 1434 | assert( | |
| 1435 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1436 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1437 | ) | |
| 1438 | assert( | |
| 1439 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1440 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1441 | ) | |
| 1442 | assert( | |
| 1443 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1444 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1445 | ) | |
| 1446 | assert(alg >= 1 && alg <= 2, 'alg must be either Argon2i 1.3 or Argon2id 1.3') | |
| 1447 | ||
| 1448 | const [done, promise] = checkStatus(callback) | |
| 1449 | ||
| 1450 | binding.crypto_pwhash_async( | |
| 1451 | out.buffer, | |
| 1452 | out.byteOffset, | |
| 1453 | out.byteLength, | |
| 1454 | ||
| 1455 | passwd.buffer, | |
| 1456 | passwd.byteOffset, | |
| 1457 | passwd.byteLength, | |
| 1458 | ||
| 1459 | salt.buffer, | |
| 1460 | salt.byteOffset, | |
| 1461 | salt.byteLength, | |
| 1462 | ||
| 1463 | opslimit, | |
| 1464 | memlimit, | |
| 1465 | alg, | |
| 1466 | ||
| 1467 | done | |
| 1468 | ) | |
| 1469 | ||
| 1470 | return promise | |
| 1471 | } | |
| 1472 | ||
| 1473 | exports.crypto_pwhash_str = function (out, passwd, opslimit, memlimit) { | |
| 1474 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1475 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1476 | assert( | |
| 1477 | out.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1478 | "out must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1479 | ) | |
| 1480 | assert(typeof opslimit === 'number', 'opslimit must be a number') | |
| 1481 | assert( | |
| 1482 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1483 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1484 | ) | |
| 1485 | assert( | |
| 1486 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1487 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1488 | ) | |
| 1489 | assert(typeof memlimit === 'number', 'memlimit must be a number') | |
| 1490 | assert( | |
| 1491 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1492 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1493 | ) | |
| 1494 | assert( | |
| 1495 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1496 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1497 | ) | |
| 1498 | ||
| 1499 | const res = binding.crypto_pwhash_str(out, passwd, opslimit, memlimit) | |
| 1500 | ||
| 1501 | if (res !== 0) throw new Error('status: ' + res) | |
| 1502 | } | |
| 1503 | ||
| 1504 | exports.crypto_pwhash_str_async = function (out, passwd, opslimit, memlimit, callback = undefined) { | |
| 1505 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1506 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1507 | assert( | |
| 1508 | out.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1509 | "out must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1510 | ) | |
| 1511 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1512 | assert(typeof opslimit === 'number', 'opslimit must be a number') | |
| 1513 | assert( | |
| 1514 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1515 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1516 | ) | |
| 1517 | assert( | |
| 1518 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1519 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1520 | ) | |
| 1521 | assert(typeof memlimit === 'number', 'memlimit must be a number') | |
| 1522 | assert( | |
| 1523 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1524 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1525 | ) | |
| 1526 | assert( | |
| 1527 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1528 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1529 | ) | |
| 1530 | ||
| 1531 | const [done, promise] = checkStatus(callback) | |
| 1532 | ||
| 1533 | binding.crypto_pwhash_str_async( | |
| 1534 | out.buffer, | |
| 1535 | out.byteOffset, | |
| 1536 | out.byteLength, | |
| 1537 | ||
| 1538 | passwd.buffer, | |
| 1539 | passwd.byteOffset, | |
| 1540 | passwd.byteLength, | |
| 1541 | ||
| 1542 | opslimit, | |
| 1543 | memlimit, | |
| 1544 | ||
| 1545 | done | |
| 1546 | ) | |
| 1547 | ||
| 1548 | return promise | |
| 1549 | } | |
| 1550 | ||
| 1551 | exports.crypto_pwhash_str_verify = function (str, passwd) { | |
| 1552 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1553 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1554 | assert( | |
| 1555 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1556 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1557 | ) | |
| 1558 | ||
| 1559 | return binding.crypto_pwhash_str_verify(str, passwd) | |
| 1560 | } | |
| 1561 | ||
| 1562 | exports.crypto_pwhash_str_verify_async = function (str, passwd, callback = undefined) { | |
| 1563 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1564 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1565 | assert( | |
| 1566 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1567 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1568 | ) | |
| 1569 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1570 | ||
| 1571 | const [done, promise] = checkStatus(callback, true) | |
| 1572 | ||
| 1573 | binding.crypto_pwhash_str_verify_async( | |
| 1574 | str.buffer, | |
| 1575 | str.byteOffset, | |
| 1576 | str.byteLength, | |
| 1577 | ||
| 1578 | passwd.buffer, | |
| 1579 | passwd.byteOffset, | |
| 1580 | passwd.byteLength, | |
| 1581 | ||
| 1582 | done | |
| 1583 | ) | |
| 1584 | ||
| 1585 | return promise | |
| 1586 | } | |
| 1587 | ||
| 1588 | exports.crypto_pwhash_str_needs_rehash = function (str, opslimit, memlimit) { | |
| 1589 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1590 | assert( | |
| 1591 | str.byteLength === binding.crypto_pwhash_STRBYTES, | |
| 1592 | "str must be 'crypto_pwhash_STRBYTES' bytes" | |
| 1593 | ) | |
| 1594 | assert( | |
| 1595 | opslimit >= binding.crypto_pwhash_OPSLIMIT_MIN, | |
| 1596 | "opslimit must be at least 'crypto_pwhash_OPSLIMIT_MIN'" | |
| 1597 | ) | |
| 1598 | assert( | |
| 1599 | opslimit <= binding.crypto_pwhash_OPSLIMIT_MAX, | |
| 1600 | "opslimit must be at most 'crypto_pwhash_OPSLIMIT_MAX'" | |
| 1601 | ) | |
| 1602 | assert( | |
| 1603 | memlimit >= binding.crypto_pwhash_MEMLIMIT_MIN, | |
| 1604 | "memlimit must be at least 'crypto_pwhash_MEMLIMIT_MIN'" | |
| 1605 | ) | |
| 1606 | assert( | |
| 1607 | memlimit <= binding.crypto_pwhash_MEMLIMIT_MAX, | |
| 1608 | "memlimit must be at most 'crypto_pwhash_MEMLIMIT_MAX'" | |
| 1609 | ) | |
| 1610 | ||
| 1611 | return binding.crypto_pwhash_str_needs_rehash(str, opslimit, memlimit) | |
| 1612 | } | |
| 1613 | ||
| 1614 | exports.crypto_pwhash_scryptsalsa208sha256 = function (out, passwd, salt, opslimit, memlimit) { | |
| 1615 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1616 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1617 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1618 | assert( | |
| 1619 | out.byteLength >= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN, | |
| 1620 | "out must be at least 'crypto_pwhash_scryptsalsa208sha256_BYTES_MIN' bytes" | |
| 1621 | ) | |
| 1622 | assert( | |
| 1623 | out.byteLength <= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX, | |
| 1624 | "out must be at most 'crypto_pwhash_scryptsalsa208sha256_BYTES_MAX' bytes" | |
| 1625 | ) | |
| 1626 | assert( | |
| 1627 | salt.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_SALTBYTES, | |
| 1628 | "salt must be 'crypto_pwhash_scryptsalsa208sha256_SALTBYTES' bytes" | |
| 1629 | ) | |
| 1630 | assert( | |
| 1631 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1632 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1633 | ) | |
| 1634 | assert( | |
| 1635 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1636 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1637 | ) | |
| 1638 | assert( | |
| 1639 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1640 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1641 | ) | |
| 1642 | assert( | |
| 1643 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1644 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1645 | ) | |
| 1646 | ||
| 1647 | const res = binding.crypto_pwhash_scryptsalsa208sha256(out, passwd, salt, opslimit, memlimit) | |
| 1648 | ||
| 1649 | if (res !== 0) throw new Error('status: ' + res) | |
| 1650 | } | |
| 1651 | ||
| 1652 | exports.crypto_pwhash_scryptsalsa208sha256_async = function ( | |
| 1653 | out, | |
| 1654 | passwd, | |
| 1655 | salt, | |
| 1656 | opslimit, | |
| 1657 | memlimit, | |
| 1658 | callback = undefined | |
| 1659 | ) { | |
| 1660 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1661 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1662 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 1663 | assert( | |
| 1664 | out.byteLength >= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN, | |
| 1665 | "out must be at least 'crypto_pwhash_scryptsalsa208sha256_BYTES_MIN' bytes" | |
| 1666 | ) | |
| 1667 | assert( | |
| 1668 | out.byteLength <= binding.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX, | |
| 1669 | "out must be at most 'crypto_pwhash_scryptsalsa208sha256_BYTES_MAX' bytes" | |
| 1670 | ) | |
| 1671 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1672 | assert( | |
| 1673 | salt.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_SALTBYTES, | |
| 1674 | "salt must be 'crypto_pwhash_scryptsalsa208sha256_SALTBYTES' bytes" | |
| 1675 | ) | |
| 1676 | assert( | |
| 1677 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1678 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1679 | ) | |
| 1680 | assert( | |
| 1681 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1682 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1683 | ) | |
| 1684 | assert( | |
| 1685 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1686 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1687 | ) | |
| 1688 | assert( | |
| 1689 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1690 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1691 | ) | |
| 1692 | ||
| 1693 | const [done, promise] = checkStatus(callback) | |
| 1694 | ||
| 1695 | binding.crypto_pwhash_scryptsalsa208sha256_async( | |
| 1696 | out.buffer, | |
| 1697 | out.byteOffset, | |
| 1698 | out.byteLength, | |
| 1699 | ||
| 1700 | passwd.buffer, | |
| 1701 | passwd.byteOffset, | |
| 1702 | passwd.byteLength, | |
| 1703 | ||
| 1704 | salt.buffer, | |
| 1705 | salt.byteOffset, | |
| 1706 | salt.byteLength, | |
| 1707 | ||
| 1708 | opslimit, | |
| 1709 | memlimit, | |
| 1710 | ||
| 1711 | done | |
| 1712 | ) | |
| 1713 | ||
| 1714 | return promise | |
| 1715 | } | |
| 1716 | ||
| 1717 | exports.crypto_pwhash_scryptsalsa208sha256_str_async = function ( | |
| 1718 | out, | |
| 1719 | passwd, | |
| 1720 | opslimit, | |
| 1721 | memlimit, | |
| 1722 | callback = undefined | |
| 1723 | ) { | |
| 1724 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1725 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1726 | assert( | |
| 1727 | out.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1728 | "out must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1729 | ) | |
| 1730 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1731 | assert( | |
| 1732 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1733 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1734 | ) | |
| 1735 | assert( | |
| 1736 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1737 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1738 | ) | |
| 1739 | assert( | |
| 1740 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1741 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1742 | ) | |
| 1743 | assert( | |
| 1744 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1745 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1746 | ) | |
| 1747 | ||
| 1748 | const [done, promise] = checkStatus(callback) | |
| 1749 | ||
| 1750 | binding.crypto_pwhash_scryptsalsa208sha256_str_async( | |
| 1751 | out.buffer, | |
| 1752 | out.byteOffset, | |
| 1753 | out.byteLength, | |
| 1754 | ||
| 1755 | passwd.buffer, | |
| 1756 | passwd.byteOffset, | |
| 1757 | passwd.byteLength, | |
| 1758 | ||
| 1759 | opslimit, | |
| 1760 | memlimit, | |
| 1761 | ||
| 1762 | done | |
| 1763 | ) | |
| 1764 | ||
| 1765 | return promise | |
| 1766 | } | |
| 1767 | ||
| 1768 | exports.crypto_pwhash_scryptsalsa208sha256_str = function (out, passwd, opslimit, memlimit) { | |
| 1769 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 1770 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1771 | assert( | |
| 1772 | out.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1773 | "out must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1774 | ) | |
| 1775 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1776 | assert( | |
| 1777 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1778 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1779 | ) | |
| 1780 | assert( | |
| 1781 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1782 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1783 | ) | |
| 1784 | assert( | |
| 1785 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1786 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1787 | ) | |
| 1788 | assert( | |
| 1789 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1790 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1791 | ) | |
| 1792 | ||
| 1793 | const res = binding.crypto_pwhash_scryptsalsa208sha256_str(out, passwd, opslimit, memlimit) | |
| 1794 | ||
| 1795 | if (res !== 0) throw new Error('status: ' + res) | |
| 1796 | } | |
| 1797 | ||
| 1798 | exports.crypto_pwhash_scryptsalsa208sha256_str_verify_async = function ( | |
| 1799 | str, | |
| 1800 | passwd, | |
| 1801 | callback = undefined | |
| 1802 | ) { | |
| 1803 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1804 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1805 | assert( | |
| 1806 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1807 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1808 | ) | |
| 1809 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1810 | ||
| 1811 | const [done, promise] = checkStatus(callback, true) | |
| 1812 | ||
| 1813 | binding.crypto_pwhash_scryptsalsa208sha256_str_verify_async( | |
| 1814 | str.buffer, | |
| 1815 | str.byteOffset, | |
| 1816 | str.byteLength, | |
| 1817 | ||
| 1818 | passwd.buffer, | |
| 1819 | passwd.byteOffset, | |
| 1820 | passwd.byteLength, | |
| 1821 | ||
| 1822 | done | |
| 1823 | ) | |
| 1824 | ||
| 1825 | return promise | |
| 1826 | } | |
| 1827 | ||
| 1828 | exports.crypto_pwhash_scryptsalsa208sha256_str_verify = function (str, passwd) { | |
| 1829 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1830 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 1831 | assert( | |
| 1832 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1833 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1834 | ) | |
| 1835 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 1836 | ||
| 1837 | return binding.crypto_pwhash_scryptsalsa208sha256_str_verify(str, passwd) | |
| 1838 | } | |
| 1839 | ||
| 1840 | exports.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash = function (str, opslimit, memlimit) { | |
| 1841 | assert(ArrayBuffer.isView(str), 'str must be a typed array') | |
| 1842 | assert( | |
| 1843 | str.byteLength === binding.crypto_pwhash_scryptsalsa208sha256_STRBYTES, | |
| 1844 | "str must be 'crypto_pwhash_scryptsalsa208sha256_STRBYTES' bytes" | |
| 1845 | ) | |
| 1846 | assert( | |
| 1847 | opslimit >= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN, | |
| 1848 | "opslimit must be at least 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN'" | |
| 1849 | ) | |
| 1850 | assert( | |
| 1851 | opslimit <= binding.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX, | |
| 1852 | "opslimit must be at most 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX'" | |
| 1853 | ) | |
| 1854 | assert( | |
| 1855 | memlimit >= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN, | |
| 1856 | "memlimit must be at least 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN'" | |
| 1857 | ) | |
| 1858 | assert( | |
| 1859 | memlimit <= binding.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX, | |
| 1860 | "memlimit must be at most 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX'" | |
| 1861 | ) | |
| 1862 | ||
| 1863 | return binding.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(str, opslimit, memlimit) | |
| 1864 | } | |
| 1865 | ||
| 1866 | // crypto_kx | |
| 1867 | ||
| 1868 | exports.crypto_kx_keypair = function (pk, sk) { | |
| 1869 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 1870 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 1871 | assert( | |
| 1872 | pk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1873 | "pk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1874 | ) | |
| 1875 | assert( | |
| 1876 | sk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1877 | "sk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1878 | ) | |
| 1879 | ||
| 1880 | const res = binding.crypto_kx_keypair(pk, sk) | |
| 1881 | ||
| 1882 | if (res !== 0) throw new Error('status: ' + res) | |
| 1883 | } | |
| 1884 | ||
| 1885 | exports.crypto_kx_seed_keypair = function (pk, sk, seed) { | |
| 1886 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 1887 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 1888 | assert(ArrayBuffer.isView(seed), 'seed must be a typed array') | |
| 1889 | assert( | |
| 1890 | pk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1891 | "pk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1892 | ) | |
| 1893 | assert( | |
| 1894 | sk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1895 | "sk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1896 | ) | |
| 1897 | assert( | |
| 1898 | seed.byteLength === binding.crypto_kx_SEEDBYTES, | |
| 1899 | "seed must be 'crypto_kx_SEEDBYTES' bytes" | |
| 1900 | ) | |
| 1901 | ||
| 1902 | const res = binding.crypto_kx_seed_keypair(pk, sk, seed) | |
| 1903 | ||
| 1904 | if (res !== 0) throw new Error('status: ' + res) | |
| 1905 | } | |
| 1906 | ||
| 1907 | exports.crypto_kx_client_session_keys = function (rx, tx, clientPk, clientSk, serverPk) { | |
| 1908 | if (!rx) rx = undefined | |
| 1909 | if (!tx) tx = undefined | |
| 1910 | ||
| 1911 | assert(rx || tx, 'at least one session key must be specified') | |
| 1912 | ||
| 1913 | if (rx) { | |
| 1914 | assert(ArrayBuffer.isView(rx), 'rx must be a typed array') | |
| 1915 | assert( | |
| 1916 | rx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1917 | "rx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1918 | ) | |
| 1919 | } | |
| 1920 | ||
| 1921 | if (tx) { | |
| 1922 | assert(ArrayBuffer.isView(tx), 'tx must be a typed array') | |
| 1923 | assert( | |
| 1924 | tx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1925 | "tx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1926 | ) | |
| 1927 | } | |
| 1928 | ||
| 1929 | assert(ArrayBuffer.isView(clientPk), 'clientPk must be a typed array') | |
| 1930 | assert(ArrayBuffer.isView(clientSk), 'clientSk must be a typed array') | |
| 1931 | assert(ArrayBuffer.isView(serverPk), 'serverPk must be a typed array') | |
| 1932 | assert( | |
| 1933 | clientPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1934 | "clientPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1935 | ) | |
| 1936 | assert( | |
| 1937 | clientSk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1938 | "clientSk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1939 | ) | |
| 1940 | assert( | |
| 1941 | serverPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1942 | "serverPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1943 | ) | |
| 1944 | ||
| 1945 | const res = binding.crypto_kx_client_session_keys(rx, tx, clientPk, clientSk, serverPk) | |
| 1946 | ||
| 1947 | if (res !== 0) throw new Error('status: ' + res) | |
| 1948 | } | |
| 1949 | ||
| 1950 | exports.crypto_kx_server_session_keys = function (rx, tx, serverPk, serverSk, clientPk) { | |
| 1951 | if (!rx) rx = undefined | |
| 1952 | if (!tx) tx = undefined | |
| 1953 | ||
| 1954 | assert(rx || tx, 'at least one session key must be specified') | |
| 1955 | ||
| 1956 | if (rx) { | |
| 1957 | assert(ArrayBuffer.isView(rx), 'rx must be a typed array') | |
| 1958 | assert( | |
| 1959 | rx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1960 | "rx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1961 | ) | |
| 1962 | } | |
| 1963 | ||
| 1964 | if (tx) { | |
| 1965 | assert(ArrayBuffer.isView(tx), 'tx must be a typed array') | |
| 1966 | assert( | |
| 1967 | tx.byteLength === binding.crypto_kx_SESSIONKEYBYTES, | |
| 1968 | "tx must be 'crypto_kx_SESSIONKEYBYTES' bytes" | |
| 1969 | ) | |
| 1970 | } | |
| 1971 | ||
| 1972 | assert(ArrayBuffer.isView(serverPk), 'serverPk must be a typed array') | |
| 1973 | assert(ArrayBuffer.isView(serverSk), 'serverSk must be a typed array') | |
| 1974 | assert(ArrayBuffer.isView(clientPk), 'clientPk must be a typed array') | |
| 1975 | assert( | |
| 1976 | serverPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1977 | "serverPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1978 | ) | |
| 1979 | assert( | |
| 1980 | serverSk.byteLength === binding.crypto_kx_SECRETKEYBYTES, | |
| 1981 | "serverSk must be 'crypto_kx_SECRETKEYBYTES' bytes" | |
| 1982 | ) | |
| 1983 | assert( | |
| 1984 | clientPk.byteLength === binding.crypto_kx_PUBLICKEYBYTES, | |
| 1985 | "clientPk must be 'crypto_kx_PUBLICKEYBYTES' bytes" | |
| 1986 | ) | |
| 1987 | ||
| 1988 | const res = binding.crypto_kx_server_session_keys(rx, tx, serverPk, serverSk, clientPk) | |
| 1989 | ||
| 1990 | if (res !== 0) throw new Error('status: ' + res) | |
| 1991 | } | |
| 1992 | ||
| 1993 | // crypto_scalarmult | |
| 1994 | ||
| 1995 | exports.crypto_scalarmult_base = function (q, n) { | |
| 1996 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 1997 | assert( | |
| 1998 | q.byteLength === binding.crypto_scalarmult_BYTES, | |
| 1999 | "q must be 'crypto_scalarmult_BYTES' bytes" | |
| 2000 | ) | |
| 2001 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2002 | assert( | |
| 2003 | n.byteLength === binding.crypto_scalarmult_SCALARBYTES, | |
| 2004 | "n must be 'crypto_scalarmult_SCALARBYTES' bytes" | |
| 2005 | ) | |
| 2006 | ||
| 2007 | const res = binding.crypto_scalarmult_base(q, n) | |
| 2008 | ||
| 2009 | if (res !== 0) throw new Error('status: ' + res) | |
| 2010 | } | |
| 2011 | ||
| 2012 | exports.crypto_scalarmult = function (q, n, p) { | |
| 2013 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2014 | assert( | |
| 2015 | q.byteLength === binding.crypto_scalarmult_BYTES, | |
| 2016 | "q must be 'crypto_scalarmult_BYTES' bytes" | |
| 2017 | ) | |
| 2018 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2019 | assert( | |
| 2020 | n.byteLength === binding.crypto_scalarmult_SCALARBYTES, | |
| 2021 | "n must be 'crypto_scalarmult_SCALARBYTES' bytes" | |
| 2022 | ) | |
| 2023 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2024 | assert( | |
| 2025 | p.byteLength === binding.crypto_scalarmult_BYTES, | |
| 2026 | "p must be 'crypto_scalarmult_BYTES' bytes" | |
| 2027 | ) | |
| 2028 | ||
| 2029 | const res = binding.crypto_scalarmult(q, n, p) | |
| 2030 | ||
| 2031 | if (res !== 0) throw new Error('status: ' + res) | |
| 2032 | } | |
| 2033 | ||
| 2034 | exports.crypto_scalarmult_ed25519_base = function (q, n) { | |
| 2035 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2036 | assert( | |
| 2037 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2038 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2039 | ) | |
| 2040 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2041 | assert( | |
| 2042 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2043 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2044 | ) | |
| 2045 | ||
| 2046 | const res = binding.crypto_scalarmult_ed25519_base(q, n) | |
| 2047 | ||
| 2048 | if (res !== 0) throw new Error('status: ' + res) | |
| 2049 | } | |
| 2050 | ||
| 2051 | exports.crypto_scalarmult_ed25519 = function (q, n, p) { | |
| 2052 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2053 | assert( | |
| 2054 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2055 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2056 | ) | |
| 2057 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2058 | assert( | |
| 2059 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2060 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2061 | ) | |
| 2062 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2063 | assert( | |
| 2064 | p.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2065 | "p must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2066 | ) | |
| 2067 | ||
| 2068 | const res = binding.crypto_scalarmult_ed25519(q, n, p) | |
| 2069 | ||
| 2070 | if (res !== 0) throw new Error('status: ' + res) | |
| 2071 | } | |
| 2072 | ||
| 2073 | exports.crypto_core_ed25519_is_valid_point = function (p) { | |
| 2074 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2075 | assert( | |
| 2076 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2077 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2078 | ) | |
| 2079 | ||
| 2080 | return binding.crypto_core_ed25519_is_valid_point(p) | |
| 2081 | } | |
| 2082 | ||
| 2083 | exports.crypto_core_ed25519_from_uniform = function (p, r) { | |
| 2084 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2085 | assert( | |
| 2086 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2087 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2088 | ) | |
| 2089 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2090 | assert( | |
| 2091 | r.byteLength === binding.crypto_core_ed25519_UNIFORMBYTES, | |
| 2092 | "r must be 'crypto_core_ed25519_UNIFORMBYTES' bytes" | |
| 2093 | ) | |
| 2094 | ||
| 2095 | const res = binding.crypto_core_ed25519_from_uniform(p, r) | |
| 2096 | ||
| 2097 | if (res !== 0) throw new Error('status: ' + res) | |
| 2098 | } | |
| 2099 | ||
| 2100 | exports.crypto_scalarmult_ed25519_base_noclamp = function (q, n) { | |
| 2101 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2102 | assert( | |
| 2103 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2104 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2105 | ) | |
| 2106 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2107 | assert( | |
| 2108 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2109 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2110 | ) | |
| 2111 | ||
| 2112 | const res = binding.crypto_scalarmult_ed25519_base_noclamp(q, n) | |
| 2113 | ||
| 2114 | if (res !== 0) throw new Error('status: ' + res) | |
| 2115 | } | |
| 2116 | ||
| 2117 | exports.crypto_scalarmult_ed25519_noclamp = function (q, n, p) { | |
| 2118 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2119 | assert( | |
| 2120 | q.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2121 | "q must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2122 | ) | |
| 2123 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2124 | assert( | |
| 2125 | n.byteLength === binding.crypto_scalarmult_ed25519_SCALARBYTES, | |
| 2126 | "n must be 'crypto_scalarmult_ed25519_SCALARBYTES' bytes" | |
| 2127 | ) | |
| 2128 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2129 | assert( | |
| 2130 | p.byteLength === binding.crypto_scalarmult_ed25519_BYTES, | |
| 2131 | "p must be 'crypto_scalarmult_ed25519_BYTES' bytes" | |
| 2132 | ) | |
| 2133 | ||
| 2134 | const res = binding.crypto_scalarmult_ed25519_noclamp(q, n, p) | |
| 2135 | ||
| 2136 | if (res !== 0) throw new Error('status: ' + res) | |
| 2137 | } | |
| 2138 | ||
| 2139 | // crypto_core | |
| 2140 | ||
| 2141 | exports.crypto_core_ed25519_add = function (r, p, q) { | |
| 2142 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2143 | assert( | |
| 2144 | r.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2145 | "r must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2146 | ) | |
| 2147 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2148 | assert( | |
| 2149 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2150 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2151 | ) | |
| 2152 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2153 | assert( | |
| 2154 | q.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2155 | "q must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2156 | ) | |
| 2157 | ||
| 2158 | const res = binding.crypto_core_ed25519_add(r, p, q) | |
| 2159 | ||
| 2160 | if (res !== 0) throw new Error('status: ' + res) | |
| 2161 | } | |
| 2162 | ||
| 2163 | exports.crypto_core_ed25519_sub = function (r, p, q) { | |
| 2164 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2165 | assert( | |
| 2166 | r.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2167 | "r must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2168 | ) | |
| 2169 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2170 | assert( | |
| 2171 | p.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2172 | "p must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2173 | ) | |
| 2174 | assert(ArrayBuffer.isView(q), 'q must be a typed array') | |
| 2175 | assert( | |
| 2176 | q.byteLength === binding.crypto_core_ed25519_BYTES, | |
| 2177 | "q must be 'crypto_core_ed25519_BYTES' bytes" | |
| 2178 | ) | |
| 2179 | ||
| 2180 | const res = binding.crypto_core_ed25519_sub(r, p, q) | |
| 2181 | ||
| 2182 | if (res !== 0) throw new Error('status: ' + res) | |
| 2183 | } | |
| 2184 | ||
| 2185 | exports.crypto_core_ed25519_scalar_random = function (r) { | |
| 2186 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2187 | assert( | |
| 2188 | r.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2189 | "r must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2190 | ) | |
| 2191 | ||
| 2192 | binding.crypto_core_ed25519_scalar_random(r) | |
| 2193 | } | |
| 2194 | ||
| 2195 | exports.crypto_core_ed25519_scalar_reduce = function (r, s) { | |
| 2196 | assert(ArrayBuffer.isView(r), 'r must be a typed array') | |
| 2197 | assert( | |
| 2198 | r.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2199 | "r must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2200 | ) | |
| 2201 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2202 | assert( | |
| 2203 | s.byteLength === binding.crypto_core_ed25519_NONREDUCEDSCALARBYTES, | |
| 2204 | "s must be 'crypto_core_ed25519_NONREDUCEDSCALARBYTES' bytes" | |
| 2205 | ) | |
| 2206 | ||
| 2207 | binding.crypto_core_ed25519_scalar_reduce(r, s) | |
| 2208 | } | |
| 2209 | ||
| 2210 | exports.crypto_core_ed25519_scalar_invert = function (recip, s) { | |
| 2211 | assert(ArrayBuffer.isView(recip), 'recip must be a typed array') | |
| 2212 | assert( | |
| 2213 | recip.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2214 | "recip must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2215 | ) | |
| 2216 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2217 | assert( | |
| 2218 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2219 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2220 | ) | |
| 2221 | ||
| 2222 | binding.crypto_core_ed25519_scalar_invert(recip, s) | |
| 2223 | } | |
| 2224 | ||
| 2225 | exports.crypto_core_ed25519_scalar_negate = function (neg, s) { | |
| 2226 | assert(ArrayBuffer.isView(neg), 'neg must be a typed array') | |
| 2227 | assert( | |
| 2228 | neg.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2229 | "neg must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2230 | ) | |
| 2231 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2232 | assert( | |
| 2233 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2234 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2235 | ) | |
| 2236 | ||
| 2237 | binding.crypto_core_ed25519_scalar_negate(neg, s) | |
| 2238 | } | |
| 2239 | ||
| 2240 | exports.crypto_core_ed25519_scalar_complement = function (comp, s) { | |
| 2241 | assert(ArrayBuffer.isView(comp), 'comp must be a typed array') | |
| 2242 | assert( | |
| 2243 | comp.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2244 | "comp must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2245 | ) | |
| 2246 | assert(ArrayBuffer.isView(s), 's must be a typed array') | |
| 2247 | assert( | |
| 2248 | s.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2249 | "s must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2250 | ) | |
| 2251 | ||
| 2252 | binding.crypto_core_ed25519_scalar_complement(comp, s) | |
| 2253 | } | |
| 2254 | ||
| 2255 | exports.crypto_core_ed25519_scalar_add = function (z, x, y) { | |
| 2256 | assert(ArrayBuffer.isView(z), 'z must be a typed array') | |
| 2257 | assert( | |
| 2258 | z.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2259 | "z must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2260 | ) | |
| 2261 | assert(ArrayBuffer.isView(x), 'x must be a typed array') | |
| 2262 | assert( | |
| 2263 | x.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2264 | "x must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2265 | ) | |
| 2266 | assert(ArrayBuffer.isView(y), 'y must be a typed array') | |
| 2267 | assert( | |
| 2268 | y.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2269 | "y must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2270 | ) | |
| 2271 | ||
| 2272 | binding.crypto_core_ed25519_scalar_add(z, x, y) | |
| 2273 | } | |
| 2274 | ||
| 2275 | exports.crypto_core_ed25519_scalar_sub = function (z, x, y) { | |
| 2276 | assert(ArrayBuffer.isView(z), 'z must be a typed array') | |
| 2277 | assert( | |
| 2278 | z.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2279 | "z must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2280 | ) | |
| 2281 | assert(ArrayBuffer.isView(x), 'x must be a typed array') | |
| 2282 | assert( | |
| 2283 | x.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2284 | "x must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2285 | ) | |
| 2286 | assert(ArrayBuffer.isView(y), 'y must be a typed array') | |
| 2287 | assert( | |
| 2288 | y.byteLength === binding.crypto_core_ed25519_SCALARBYTES, | |
| 2289 | "y must be 'crypto_core_ed25519_SCALARBYTES' bytes" | |
| 2290 | ) | |
| 2291 | ||
| 2292 | binding.crypto_core_ed25519_scalar_sub(z, x, y) | |
| 2293 | } | |
| 2294 | ||
| 2295 | // crypto_shorthash | |
| 2296 | ||
| 2297 | exports.crypto_shorthash = function (out, input, k) { | |
| 2298 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2299 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2300 | assert( | |
| 2301 | out.byteLength === binding.crypto_shorthash_BYTES, | |
| 2302 | "out must be 'crypto_shorthash_BYTES' bytes" | |
| 2303 | ) | |
| 2304 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2305 | assert( | |
| 2306 | k.byteLength === binding.crypto_shorthash_KEYBYTES, | |
| 2307 | "k must be 'crypto_shorthash_KEYBYTES' bytes" | |
| 2308 | ) | |
| 2309 | ||
| 2310 | const res = binding.crypto_shorthash(out, input, k) | |
| 2311 | ||
| 2312 | if (res !== 0) throw new Error('status: ' + res) | |
| 2313 | } | |
| 2314 | ||
| 2315 | // crypto_kdf | |
| 2316 | ||
| 2317 | exports.crypto_kdf_keygen = function (key) { | |
| 2318 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 2319 | assert(key.byteLength === binding.crypto_kdf_KEYBYTES, "key must be 'crypto_kdf_KEYBYTES' bytes") | |
| 2320 | ||
| 2321 | binding.crypto_kdf_keygen(key) | |
| 2322 | } | |
| 2323 | ||
| 2324 | exports.crypto_kdf_derive_from_key = function (subkey, subkeyId, ctx, key) { | |
| 2325 | assert(ArrayBuffer.isView(subkey), 'subkey must be a typed array') | |
| 2326 | assert( | |
| 2327 | subkey.byteLength >= binding.crypto_kdf_BYTES_MIN, | |
| 2328 | "subkey must be at least 'crypto_kdf_BYTES_MIN' bytes" | |
| 2329 | ) | |
| 2330 | assert( | |
| 2331 | subkey.byteLength <= binding.crypto_kdf_BYTES_MAX, | |
| 2332 | "subkey must be at most 'crypto_kdf_BYTES_MAX' bytes" | |
| 2333 | ) | |
| 2334 | assert(ArrayBuffer.isView(ctx), 'ctx must be a typed array') | |
| 2335 | assert( | |
| 2336 | ctx.byteLength === binding.crypto_kdf_CONTEXTBYTES, | |
| 2337 | "ctx must be 'crypto_kdf_CONTEXTBYTES' bytes" | |
| 2338 | ) | |
| 2339 | assert(ArrayBuffer.isView(key), 'key must be a typed array') | |
| 2340 | assert(key.byteLength === binding.crypto_kdf_KEYBYTES, "key must be 'crypto_kdf_KEYBYTES' bytes") | |
| 2341 | ||
| 2342 | const res = binding.crypto_kdf_derive_from_key(subkey, subkeyId, ctx, key) | |
| 2343 | ||
| 2344 | if (res !== 0) throw new Error('status: ' + res) | |
| 2345 | } | |
| 2346 | ||
| 2347 | // crypto_hash | |
| 2348 | ||
| 2349 | exports.crypto_hash = function (out, input) { | |
| 2350 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2351 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2352 | assert(out.byteLength === binding.crypto_hash_BYTES, "out must be 'crypto_hash_BYTES' bytes") | |
| 2353 | ||
| 2354 | const res = binding.crypto_hash(out, input) | |
| 2355 | ||
| 2356 | if (res !== 0) throw new Error('status: ' + res) | |
| 2357 | } | |
| 2358 | ||
| 2359 | exports.crypto_hash_sha256 = function (out, input) { | |
| 2360 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2361 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2362 | assert( | |
| 2363 | out.byteLength === binding.crypto_hash_sha256_BYTES, | |
| 2364 | "out must be 'crypto_hash_sha256_BYTES' bytes" | |
| 2365 | ) | |
| 2366 | ||
| 2367 | const res = binding.crypto_hash_sha256(out, input) | |
| 2368 | ||
| 2369 | if (res !== 0) throw new Error('status: ' + res) | |
| 2370 | } | |
| 2371 | ||
| 2372 | exports.crypto_hash_sha256_init = function (state) { | |
| 2373 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2374 | assert( | |
| 2375 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2376 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2377 | ) | |
| 2378 | ||
| 2379 | const res = binding.crypto_hash_sha256_init(state) | |
| 2380 | ||
| 2381 | if (res !== 0) throw new Error('status: ' + res) | |
| 2382 | } | |
| 2383 | ||
| 2384 | exports.crypto_hash_sha256_update = function (state, input) { | |
| 2385 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2386 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2387 | assert( | |
| 2388 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2389 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2390 | ) | |
| 2391 | ||
| 2392 | const res = binding.crypto_hash_sha256_update(state, input) | |
| 2393 | ||
| 2394 | if (res !== 0) throw new Error('status: ' + res) | |
| 2395 | } | |
| 2396 | ||
| 2397 | exports.crypto_hash_sha256_final = function (state, out) { | |
| 2398 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2399 | assert( | |
| 2400 | state.byteLength === binding.crypto_hash_sha256_STATEBYTES, | |
| 2401 | "state must be 'crypto_hash_sha256_STATEBYTES' bytes" | |
| 2402 | ) | |
| 2403 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2404 | assert( | |
| 2405 | out.byteLength === binding.crypto_hash_sha256_BYTES, | |
| 2406 | "out must be 'crypto_hash_sha256_BYTES' bytes" | |
| 2407 | ) | |
| 2408 | ||
| 2409 | const res = binding.crypto_hash_sha256_final(state, out) | |
| 2410 | ||
| 2411 | if (res !== 0) throw new Error('status: ' + res) | |
| 2412 | } | |
| 2413 | ||
| 2414 | exports.crypto_hash_sha512 = function (out, input) { | |
| 2415 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2416 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2417 | assert( | |
| 2418 | out.byteLength === binding.crypto_hash_sha512_BYTES, | |
| 2419 | "out must be 'crypto_hash_sha512_BYTES' bytes" | |
| 2420 | ) | |
| 2421 | ||
| 2422 | const res = binding.crypto_hash_sha512(out, input) | |
| 2423 | ||
| 2424 | if (res !== 0) throw new Error('status: ' + res) | |
| 2425 | } | |
| 2426 | ||
| 2427 | exports.crypto_hash_sha512_init = function (state) { | |
| 2428 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2429 | assert( | |
| 2430 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2431 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2432 | ) | |
| 2433 | ||
| 2434 | const res = binding.crypto_hash_sha512_init(state) | |
| 2435 | ||
| 2436 | if (res !== 0) throw new Error('status: ' + res) | |
| 2437 | } | |
| 2438 | ||
| 2439 | exports.crypto_hash_sha512_update = function (state, input) { | |
| 2440 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2441 | assert(ArrayBuffer.isView(input), 'input must be a typed array') | |
| 2442 | assert( | |
| 2443 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2444 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2445 | ) | |
| 2446 | ||
| 2447 | const res = binding.crypto_hash_sha512_update(state, input) | |
| 2448 | ||
| 2449 | if (res !== 0) throw new Error('status: ' + res) | |
| 2450 | } | |
| 2451 | ||
| 2452 | exports.crypto_hash_sha512_final = function (state, out) { | |
| 2453 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2454 | assert( | |
| 2455 | state.byteLength === binding.crypto_hash_sha512_STATEBYTES, | |
| 2456 | "state must be 'crypto_hash_sha512_STATEBYTES' bytes" | |
| 2457 | ) | |
| 2458 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 2459 | assert( | |
| 2460 | out.byteLength === binding.crypto_hash_sha512_BYTES, | |
| 2461 | "out must be 'crypto_hash_sha512_BYTES' bytes" | |
| 2462 | ) | |
| 2463 | ||
| 2464 | const res = binding.crypto_hash_sha512_final(state, out) | |
| 2465 | ||
| 2466 | if (res !== 0) throw new Error('status: ' + res) | |
| 2467 | } | |
| 2468 | ||
| 2469 | // crypto_aead | |
| 2470 | ||
| 2471 | exports.crypto_aead_xchacha20poly1305_ietf_keygen = function (k) { | |
| 2472 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2473 | assert( | |
| 2474 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2475 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2476 | ) | |
| 2477 | ||
| 2478 | binding.crypto_aead_xchacha20poly1305_ietf_keygen(k) | |
| 2479 | } | |
| 2480 | ||
| 2481 | exports.crypto_aead_xchacha20poly1305_ietf_encrypt = function (c, m, ad, nsec, npub, k) { | |
| 2482 | if (!ad) ad = undefined | |
| 2483 | ||
| 2484 | assert(nsec === null, 'nsec must always be set to null') | |
| 2485 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2486 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2487 | assert( | |
| 2488 | c.byteLength === m.byteLength + binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2489 | "c must be 'm.byteLength + crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2490 | ) | |
| 2491 | assert(c.byteLength <= 0xffffffff, 'c.byteLength must be a 32bit integer') | |
| 2492 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2493 | assert( | |
| 2494 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2495 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2496 | ) | |
| 2497 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2498 | assert( | |
| 2499 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2500 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2501 | ) | |
| 2502 | ||
| 2503 | const res = binding.crypto_aead_xchacha20poly1305_ietf_encrypt(c, m, ad, npub, k) | |
| 2504 | ||
| 2505 | if (res < 0) throw new Error('could not encrypt data') | |
| 2506 | ||
| 2507 | return res | |
| 2508 | } | |
| 2509 | ||
| 2510 | exports.crypto_aead_xchacha20poly1305_ietf_decrypt = function (m, nsec, c, ad, npub, k) { | |
| 2511 | if (!ad) ad = undefined | |
| 2512 | ||
| 2513 | assert(nsec === null, 'nsec must always be set to null') | |
| 2514 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2515 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2516 | assert( | |
| 2517 | m.byteLength === c.byteLength - binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2518 | "m must be 'c.byteLength - crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2519 | ) | |
| 2520 | assert(m.byteLength <= 0xffffffff, 'm.byteLength must be a 32bit integer') | |
| 2521 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2522 | assert( | |
| 2523 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2524 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2525 | ) | |
| 2526 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2527 | assert( | |
| 2528 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2529 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2530 | ) | |
| 2531 | ||
| 2532 | const res = binding.crypto_aead_xchacha20poly1305_ietf_decrypt(m, c, ad, npub, k) | |
| 2533 | ||
| 2534 | if (res < 0) throw new Error('could not verify data') | |
| 2535 | ||
| 2536 | return res | |
| 2537 | } | |
| 2538 | ||
| 2539 | exports.crypto_aead_xchacha20poly1305_ietf_encrypt_detached = function ( | |
| 2540 | c, | |
| 2541 | mac, | |
| 2542 | m, | |
| 2543 | ad, | |
| 2544 | nsec, | |
| 2545 | npub, | |
| 2546 | k | |
| 2547 | ) { | |
| 2548 | if (!ad) ad = undefined | |
| 2549 | ||
| 2550 | assert(nsec === null, 'nsec must always be set to null') | |
| 2551 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2552 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2553 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2554 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2555 | assert( | |
| 2556 | mac.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2557 | "mac must be 'crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2558 | ) | |
| 2559 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2560 | assert( | |
| 2561 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2562 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2563 | ) | |
| 2564 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2565 | assert( | |
| 2566 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2567 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2568 | ) | |
| 2569 | ||
| 2570 | const res = binding.crypto_aead_xchacha20poly1305_ietf_encrypt_detached(c, mac, m, ad, npub, k) | |
| 2571 | ||
| 2572 | if (res < 0) throw new Error('could not encrypt data') | |
| 2573 | ||
| 2574 | return res | |
| 2575 | } | |
| 2576 | ||
| 2577 | exports.crypto_aead_xchacha20poly1305_ietf_decrypt_detached = function ( | |
| 2578 | m, | |
| 2579 | nsec, | |
| 2580 | c, | |
| 2581 | mac, | |
| 2582 | ad, | |
| 2583 | npub, | |
| 2584 | k | |
| 2585 | ) { | |
| 2586 | if (!ad) ad = undefined | |
| 2587 | ||
| 2588 | assert(nsec === null, 'nsec must always be set to null') | |
| 2589 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2590 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2591 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 2592 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2593 | assert( | |
| 2594 | mac.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_ABYTES, | |
| 2595 | "mac must be 'crypto_aead_xchacha20poly1305_ietf_ABYTES' bytes" | |
| 2596 | ) | |
| 2597 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2598 | assert( | |
| 2599 | npub.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, | |
| 2600 | "npub must be 'crypto_aead_xchacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2601 | ) | |
| 2602 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2603 | assert( | |
| 2604 | k.byteLength === binding.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, | |
| 2605 | "k must be 'crypto_aead_xchacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2606 | ) | |
| 2607 | ||
| 2608 | const res = binding.crypto_aead_xchacha20poly1305_ietf_decrypt_detached(m, c, mac, ad, npub, k) | |
| 2609 | ||
| 2610 | if (res !== 0) throw new Error('could not verify data') | |
| 2611 | } | |
| 2612 | ||
| 2613 | exports.crypto_aead_chacha20poly1305_ietf_keygen = function (k) { | |
| 2614 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2615 | assert( | |
| 2616 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2617 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2618 | ) | |
| 2619 | ||
| 2620 | binding.crypto_aead_chacha20poly1305_ietf_keygen(k) | |
| 2621 | } | |
| 2622 | ||
| 2623 | exports.crypto_aead_chacha20poly1305_ietf_encrypt = function (c, m, ad, nsec, npub, k) { | |
| 2624 | if (!ad) ad = undefined | |
| 2625 | ||
| 2626 | assert(nsec === null, 'nsec must always be set to null') | |
| 2627 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2628 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2629 | assert( | |
| 2630 | c.byteLength === m.byteLength + binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2631 | "c must be 'm.byteLength + crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2632 | ) | |
| 2633 | assert(c.byteLength <= 0xffffffff, 'c.byteLength must be a 32bit integer') | |
| 2634 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2635 | assert( | |
| 2636 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2637 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2638 | ) | |
| 2639 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2640 | assert( | |
| 2641 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2642 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2643 | ) | |
| 2644 | ||
| 2645 | const res = binding.crypto_aead_chacha20poly1305_ietf_encrypt(c, m, ad, npub, k) | |
| 2646 | ||
| 2647 | if (res < 0) throw new Error('could not encrypt data') | |
| 2648 | ||
| 2649 | return res | |
| 2650 | } | |
| 2651 | ||
| 2652 | exports.crypto_aead_chacha20poly1305_ietf_decrypt = function (m, nsec, c, ad, npub, k) { | |
| 2653 | if (!ad) ad = undefined | |
| 2654 | ||
| 2655 | assert(nsec === null, 'nsec must always be set to null') | |
| 2656 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2657 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2658 | assert( | |
| 2659 | m.byteLength === c.byteLength - binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2660 | "m must be 'c.byteLength - crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2661 | ) | |
| 2662 | assert(m.byteLength <= 0xffffffff, 'm.byteLength must be a 32bit integer') | |
| 2663 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2664 | assert( | |
| 2665 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2666 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2667 | ) | |
| 2668 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2669 | assert( | |
| 2670 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2671 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2672 | ) | |
| 2673 | ||
| 2674 | const res = binding.crypto_aead_chacha20poly1305_ietf_decrypt(m, c, ad, npub, k) | |
| 2675 | ||
| 2676 | if (res < 0) throw new Error('could not verify data') | |
| 2677 | ||
| 2678 | return res | |
| 2679 | } | |
| 2680 | ||
| 2681 | exports.crypto_aead_chacha20poly1305_ietf_encrypt_detached = function ( | |
| 2682 | c, | |
| 2683 | mac, | |
| 2684 | m, | |
| 2685 | ad, | |
| 2686 | nsec, | |
| 2687 | npub, | |
| 2688 | k | |
| 2689 | ) { | |
| 2690 | if (!ad) ad = undefined | |
| 2691 | ||
| 2692 | assert(nsec === null, 'nsec must always be set to null') | |
| 2693 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2694 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2695 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2696 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2697 | assert( | |
| 2698 | mac.byteLength === binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2699 | "mac must be 'crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2700 | ) | |
| 2701 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2702 | assert( | |
| 2703 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2704 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2705 | ) | |
| 2706 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2707 | assert( | |
| 2708 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2709 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2710 | ) | |
| 2711 | ||
| 2712 | const res = binding.crypto_aead_chacha20poly1305_ietf_encrypt_detached(c, mac, m, ad, npub, k) | |
| 2713 | ||
| 2714 | if (res < 0) throw new Error('could not encrypt data') | |
| 2715 | ||
| 2716 | return res | |
| 2717 | } | |
| 2718 | ||
| 2719 | exports.crypto_aead_chacha20poly1305_ietf_decrypt_detached = function ( | |
| 2720 | m, | |
| 2721 | nsec, | |
| 2722 | c, | |
| 2723 | mac, | |
| 2724 | ad, | |
| 2725 | npub, | |
| 2726 | k | |
| 2727 | ) { | |
| 2728 | if (!ad) ad = undefined | |
| 2729 | ||
| 2730 | assert(nsec === null, 'nsec must always be set to null') | |
| 2731 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2732 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2733 | assert(m.byteLength === c.byteLength, "m must be 'c.byteLength' bytes") | |
| 2734 | assert(ArrayBuffer.isView(mac), 'mac must be a typed array') | |
| 2735 | assert( | |
| 2736 | mac.byteLength === binding.crypto_aead_chacha20poly1305_ietf_ABYTES, | |
| 2737 | "mac must be 'crypto_aead_chacha20poly1305_ietf_ABYTES' bytes" | |
| 2738 | ) | |
| 2739 | assert(ArrayBuffer.isView(npub), 'npub must be a typed array') | |
| 2740 | assert( | |
| 2741 | npub.byteLength === binding.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, | |
| 2742 | "npub must be 'crypto_aead_chacha20poly1305_ietf_NPUBBYTES' bytes" | |
| 2743 | ) | |
| 2744 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2745 | assert( | |
| 2746 | k.byteLength === binding.crypto_aead_chacha20poly1305_ietf_KEYBYTES, | |
| 2747 | "k must be 'crypto_aead_chacha20poly1305_ietf_KEYBYTES' bytes" | |
| 2748 | ) | |
| 2749 | ||
| 2750 | const res = binding.crypto_aead_chacha20poly1305_ietf_decrypt_detached(m, c, mac, ad, npub, k) | |
| 2751 | ||
| 2752 | if (res !== 0) throw new Error('could not verify data') | |
| 2753 | } | |
| 2754 | ||
| 2755 | // crypto_stream | |
| 2756 | ||
| 2757 | exports.crypto_stream_xor_wrap_init = function (state, n, k) { | |
| 2758 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2759 | assert( | |
| 2760 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2761 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2762 | ) | |
| 2763 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2764 | assert( | |
| 2765 | n.byteLength === binding.crypto_stream_NONCEBYTES, | |
| 2766 | "n must be 'crypto_stream_NONCEBYTES' bytes" | |
| 2767 | ) | |
| 2768 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2769 | assert( | |
| 2770 | k.byteLength === binding.crypto_stream_KEYBYTES, | |
| 2771 | "k must be 'crypto_stream_KEYBYTES' bytes" | |
| 2772 | ) | |
| 2773 | ||
| 2774 | binding.crypto_stream_xor_wrap_init(state, n, k) | |
| 2775 | } | |
| 2776 | ||
| 2777 | exports.crypto_stream_xor_wrap_update = function (state, c, m) { | |
| 2778 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2779 | assert( | |
| 2780 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2781 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2782 | ) | |
| 2783 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2784 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2785 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2786 | ||
| 2787 | binding.crypto_stream_xor_wrap_update(state, c, m) | |
| 2788 | } | |
| 2789 | ||
| 2790 | exports.crypto_stream_xor_wrap_final = function (state) { | |
| 2791 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2792 | assert( | |
| 2793 | state.byteLength === binding.sn_crypto_stream_xor_STATEBYTES, | |
| 2794 | "state must be 'sn_crypto_stream_xor_STATEBYTES' bytes" | |
| 2795 | ) | |
| 2796 | ||
| 2797 | binding.crypto_stream_xor_wrap_final(state) | |
| 2798 | } | |
| 2799 | ||
| 2800 | exports.crypto_stream_chacha20_xor_wrap_init = function (state, n, k) { | |
| 2801 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2802 | assert( | |
| 2803 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2804 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2805 | ) | |
| 2806 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2807 | assert( | |
| 2808 | n.byteLength === binding.crypto_stream_chacha20_NONCEBYTES, | |
| 2809 | "n must be 'crypto_stream_chacha20_NONCEBYTES' bytes" | |
| 2810 | ) | |
| 2811 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2812 | assert( | |
| 2813 | k.byteLength === binding.crypto_stream_chacha20_KEYBYTES, | |
| 2814 | "k must be 'crypto_stream_chacha20_KEYBYTES' bytes" | |
| 2815 | ) | |
| 2816 | ||
| 2817 | binding.crypto_stream_chacha20_xor_wrap_init(state, n, k) | |
| 2818 | } | |
| 2819 | ||
| 2820 | exports.crypto_stream_chacha20_xor_wrap_update = function (state, c, m) { | |
| 2821 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2822 | assert( | |
| 2823 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2824 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2825 | ) | |
| 2826 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2827 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2828 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2829 | ||
| 2830 | binding.crypto_stream_chacha20_xor_wrap_update(state, c, m) | |
| 2831 | } | |
| 2832 | ||
| 2833 | exports.crypto_stream_chacha20_xor_wrap_final = function (state) { | |
| 2834 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2835 | assert( | |
| 2836 | state.byteLength === binding.crypto_stream_chacha20_xor_STATEBYTES, | |
| 2837 | "state must be 'crypto_stream_chacha20_xor_STATEBYTES' bytes" | |
| 2838 | ) | |
| 2839 | ||
| 2840 | binding.crypto_stream_chacha20_xor_wrap_final(state) | |
| 2841 | } | |
| 2842 | ||
| 2843 | exports.crypto_stream_chacha20_ietf_xor_wrap_init = function (state, n, k) { | |
| 2844 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2845 | assert( | |
| 2846 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2847 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2848 | ) | |
| 2849 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2850 | assert( | |
| 2851 | n.byteLength === binding.crypto_stream_chacha20_ietf_NONCEBYTES, | |
| 2852 | "n must be 'crypto_stream_chacha20_ietf_NONCEBYTES' bytes" | |
| 2853 | ) | |
| 2854 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2855 | assert( | |
| 2856 | k.byteLength === binding.crypto_stream_chacha20_ietf_KEYBYTES, | |
| 2857 | "k must be 'crypto_stream_chacha20_ietf_KEYBYTES' bytes" | |
| 2858 | ) | |
| 2859 | ||
| 2860 | binding.crypto_stream_chacha20_ietf_xor_wrap_init(state, n, k) | |
| 2861 | } | |
| 2862 | ||
| 2863 | exports.crypto_stream_chacha20_ietf_xor_wrap_update = function (state, c, m) { | |
| 2864 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2865 | assert( | |
| 2866 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2867 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2868 | ) | |
| 2869 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2870 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2871 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2872 | ||
| 2873 | binding.crypto_stream_chacha20_ietf_xor_wrap_update(state, c, m) | |
| 2874 | } | |
| 2875 | ||
| 2876 | exports.crypto_stream_chacha20_ietf_xor_wrap_final = function (state) { | |
| 2877 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2878 | assert( | |
| 2879 | state.byteLength === binding.crypto_stream_chacha20_ietf_xor_STATEBYTES, | |
| 2880 | "state must be 'crypto_stream_chacha20_ietf_xor_STATEBYTES' bytes" | |
| 2881 | ) | |
| 2882 | ||
| 2883 | binding.crypto_stream_chacha20_ietf_xor_wrap_final(state) | |
| 2884 | } | |
| 2885 | ||
| 2886 | exports.crypto_stream_xchacha20_xor_wrap_init = function (state, n, k) { | |
| 2887 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2888 | assert( | |
| 2889 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2890 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2891 | ) | |
| 2892 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2893 | assert( | |
| 2894 | n.byteLength === binding.crypto_stream_xchacha20_NONCEBYTES, | |
| 2895 | "n must be 'crypto_stream_xchacha20_NONCEBYTES' bytes" | |
| 2896 | ) | |
| 2897 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2898 | assert( | |
| 2899 | k.byteLength === binding.crypto_stream_xchacha20_KEYBYTES, | |
| 2900 | "k must be 'crypto_stream_xchacha20_KEYBYTES' bytes" | |
| 2901 | ) | |
| 2902 | ||
| 2903 | binding.crypto_stream_xchacha20_xor_wrap_init(state, n, k) | |
| 2904 | } | |
| 2905 | ||
| 2906 | exports.crypto_stream_xchacha20_xor_wrap_update = function (state, c, m) { | |
| 2907 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2908 | assert( | |
| 2909 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2910 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2911 | ) | |
| 2912 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2913 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2914 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2915 | ||
| 2916 | binding.crypto_stream_xchacha20_xor_wrap_update(state, c, m) | |
| 2917 | } | |
| 2918 | ||
| 2919 | exports.crypto_stream_xchacha20_xor_wrap_final = function (state) { | |
| 2920 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2921 | assert( | |
| 2922 | state.byteLength === binding.crypto_stream_xchacha20_xor_STATEBYTES, | |
| 2923 | "state must be 'crypto_stream_xchacha20_xor_STATEBYTES' bytes" | |
| 2924 | ) | |
| 2925 | ||
| 2926 | binding.crypto_stream_xchacha20_xor_wrap_final(state) | |
| 2927 | } | |
| 2928 | ||
| 2929 | exports.crypto_stream_salsa20_xor_wrap_init = function (state, n, k) { | |
| 2930 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2931 | assert( | |
| 2932 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2933 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2934 | ) | |
| 2935 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2936 | assert( | |
| 2937 | n.byteLength === binding.crypto_stream_salsa20_NONCEBYTES, | |
| 2938 | "n must be 'crypto_stream_salsa20_NONCEBYTES' bytes" | |
| 2939 | ) | |
| 2940 | assert(ArrayBuffer.isView(k), 'k must be a typed array') | |
| 2941 | assert( | |
| 2942 | k.byteLength === binding.crypto_stream_salsa20_KEYBYTES, | |
| 2943 | "k must be 'crypto_stream_salsa20_KEYBYTES' bytes" | |
| 2944 | ) | |
| 2945 | ||
| 2946 | binding.crypto_stream_salsa20_xor_wrap_init(state, n, k) | |
| 2947 | } | |
| 2948 | ||
| 2949 | exports.crypto_stream_salsa20_xor_wrap_update = function (state, c, m) { | |
| 2950 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2951 | assert( | |
| 2952 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2953 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2954 | ) | |
| 2955 | assert(ArrayBuffer.isView(c), 'c must be a typed array') | |
| 2956 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2957 | assert(c.byteLength === m.byteLength, "c must be 'm.byteLength' bytes") | |
| 2958 | ||
| 2959 | binding.crypto_stream_salsa20_xor_wrap_update(state, c, m) | |
| 2960 | } | |
| 2961 | ||
| 2962 | exports.crypto_stream_salsa20_xor_wrap_final = function (state) { | |
| 2963 | assert(ArrayBuffer.isView(state), 'state must be a typed array') | |
| 2964 | assert( | |
| 2965 | state.byteLength === binding.crypto_stream_salsa20_xor_STATEBYTES, | |
| 2966 | "state must be 'crypto_stream_salsa20_xor_STATEBYTES' bytes" | |
| 2967 | ) | |
| 2968 | ||
| 2969 | binding.crypto_stream_salsa20_xor_wrap_final(state) | |
| 2970 | } | |
| 2971 | ||
| 2972 | // experimental | |
| 2973 | ||
| 2974 | exports.extension_tweak_ed25519_base = function (n, p, ns) { | |
| 2975 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 2976 | assert( | |
| 2977 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 2978 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 2979 | ) | |
| 2980 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 2981 | assert( | |
| 2982 | p.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 2983 | "p must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 2984 | ) | |
| 2985 | ||
| 2986 | binding.extension_tweak_ed25519_base(n, p, ns) | |
| 2987 | } | |
| 2988 | ||
| 2989 | exports.extension_tweak_ed25519_sign_detached = function (sig, m, scalar, pk) { | |
| 2990 | assert(ArrayBuffer.isView(sig), 'sig must be a typed array') | |
| 2991 | assert(sig.byteLength === binding.crypto_sign_BYTES, "sig must be 'crypto_sign_BYTES' bytes") | |
| 2992 | assert(ArrayBuffer.isView(m), 'm must be a typed array') | |
| 2993 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 2994 | assert( | |
| 2995 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 2996 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 2997 | ) | |
| 2998 | ||
| 2999 | if (pk) { | |
| 3000 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3001 | assert( | |
| 3002 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3003 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3004 | ) | |
| 3005 | } | |
| 3006 | ||
| 3007 | const res = binding.extension_tweak_ed25519_sign_detached(sig, m, scalar, pk) | |
| 3008 | ||
| 3009 | if (res !== 0) throw new Error('failed to compute signature') | |
| 3010 | } | |
| 3011 | ||
| 3012 | exports.extension_tweak_ed25519_sk_to_scalar = function (n, sk) { | |
| 3013 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 3014 | assert( | |
| 3015 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3016 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3017 | ) | |
| 3018 | assert(ArrayBuffer.isView(sk), 'sk must be a typed array') | |
| 3019 | assert( | |
| 3020 | sk.byteLength === binding.crypto_sign_SECRETKEYBYTES, | |
| 3021 | "sk must be 'crypto_sign_SECRETKEYBYTES' bytes" | |
| 3022 | ) | |
| 3023 | ||
| 3024 | binding.extension_tweak_ed25519_sk_to_scalar(n, sk) | |
| 3025 | } | |
| 3026 | ||
| 3027 | exports.extension_tweak_ed25519_scalar = function (scalarOut, scalar, ns) { | |
| 3028 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3029 | assert( | |
| 3030 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3031 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3032 | ) | |
| 3033 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 3034 | assert( | |
| 3035 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3036 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3037 | ) | |
| 3038 | ||
| 3039 | binding.extension_tweak_ed25519_scalar(scalarOut, scalar, ns) | |
| 3040 | } | |
| 3041 | ||
| 3042 | exports.extension_tweak_ed25519_pk = function (tpk, pk, ns) { | |
| 3043 | assert(ArrayBuffer.isView(tpk), 'tpk must be a typed array') | |
| 3044 | assert( | |
| 3045 | tpk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3046 | "tpk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3047 | ) | |
| 3048 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3049 | assert( | |
| 3050 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3051 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3052 | ) | |
| 3053 | ||
| 3054 | const res = binding.extension_tweak_ed25519_pk(tpk, pk, ns) | |
| 3055 | ||
| 3056 | if (res !== 0) throw new Error('failed to tweak public key') | |
| 3057 | } | |
| 3058 | ||
| 3059 | exports.extension_tweak_ed25519_keypair = function (pk, scalarOut, scalarIn, ns) { | |
| 3060 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3061 | assert( | |
| 3062 | pk.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 3063 | "pk must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 3064 | ) | |
| 3065 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3066 | assert( | |
| 3067 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3068 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3069 | ) | |
| 3070 | assert(ArrayBuffer.isView(scalarIn), 'scalarIn must be a typed array') | |
| 3071 | assert( | |
| 3072 | scalarIn.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3073 | "scalarIn must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3074 | ) | |
| 3075 | ||
| 3076 | binding.extension_tweak_ed25519_keypair(pk, scalarOut, scalarIn, ns) | |
| 3077 | } | |
| 3078 | ||
| 3079 | exports.extension_tweak_ed25519_scalar_add = function (scalarOut, scalar, n) { | |
| 3080 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3081 | assert( | |
| 3082 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3083 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3084 | ) | |
| 3085 | assert(ArrayBuffer.isView(scalar), 'scalar must be a typed array') | |
| 3086 | assert( | |
| 3087 | scalar.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3088 | "scalar must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3089 | ) | |
| 3090 | assert(ArrayBuffer.isView(n), 'n must be a typed array') | |
| 3091 | assert( | |
| 3092 | n.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3093 | "n must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3094 | ) | |
| 3095 | ||
| 3096 | binding.extension_tweak_ed25519_scalar_add(scalarOut, scalar, n) | |
| 3097 | } | |
| 3098 | ||
| 3099 | exports.extension_tweak_ed25519_pk_add = function (tpk, pk, p) { | |
| 3100 | assert(ArrayBuffer.isView(tpk), 'tpk must be a typed array') | |
| 3101 | assert( | |
| 3102 | tpk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3103 | "tpk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3104 | ) | |
| 3105 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3106 | assert( | |
| 3107 | pk.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3108 | "pk must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3109 | ) | |
| 3110 | assert(ArrayBuffer.isView(p), 'p must be a typed array') | |
| 3111 | assert( | |
| 3112 | p.byteLength === binding.crypto_sign_PUBLICKEYBYTES, | |
| 3113 | "p must be 'crypto_sign_PUBLICKEYBYTES' bytes" | |
| 3114 | ) | |
| 3115 | ||
| 3116 | const res = binding.extension_tweak_ed25519_pk_add(tpk, pk, p) | |
| 3117 | ||
| 3118 | if (res !== 0) throw new Error('failed to add tweak to public key') | |
| 3119 | } | |
| 3120 | ||
| 3121 | exports.extension_tweak_ed25519_keypair_add = function (pk, scalarOut, scalarIn, tweak) { | |
| 3122 | assert(ArrayBuffer.isView(pk), 'pk must be a typed array') | |
| 3123 | assert( | |
| 3124 | pk.byteLength === binding.extension_tweak_ed25519_BYTES, | |
| 3125 | "pk must be 'extension_tweak_ed25519_BYTES' bytes" | |
| 3126 | ) | |
| 3127 | assert(ArrayBuffer.isView(scalarOut), 'scalarOut must be a typed array') | |
| 3128 | assert( | |
| 3129 | scalarOut.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3130 | "scalarOut must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3131 | ) | |
| 3132 | assert(ArrayBuffer.isView(scalarIn), 'scalarIn must be a typed array') | |
| 3133 | assert( | |
| 3134 | scalarIn.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3135 | "scalarIn must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3136 | ) | |
| 3137 | assert(ArrayBuffer.isView(tweak), 'tweak must be a typed array') | |
| 3138 | assert( | |
| 3139 | tweak.byteLength === binding.extension_tweak_ed25519_SCALARBYTES, | |
| 3140 | "tweak must be 'extension_tweak_ed25519_SCALARBYTES' bytes" | |
| 3141 | ) | |
| 3142 | ||
| 3143 | const res = binding.extension_tweak_ed25519_keypair_add(pk, scalarOut, scalarIn, tweak) | |
| 3144 | ||
| 3145 | if (res !== 0) throw new Error('failed to add tweak to keypair') | |
| 3146 | } | |
| 3147 | ||
| 3148 | exports.extension_pbkdf2_sha512_async = function (out, passwd, salt, iter, outlen, callback) { | |
| 3149 | assert( | |
| 3150 | iter >= binding.extension_pbkdf2_sha512_ITERATIONS_MIN, | |
| 3151 | "iter must be at least 'extension_pbkdf2_sha512_ITERATIONS_MIN'" | |
| 3152 | ) | |
| 3153 | assert( | |
| 3154 | outlen <= binding.extension_pbkdf2_sha512_BYTES_MAX, | |
| 3155 | "outlen must be at most 'extension_pbkdf2_sha512_BYTES_MAX'" | |
| 3156 | ) | |
| 3157 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 3158 | assert(out.byteLength >= outlen, "out must be at least 'outlen' bytes") | |
| 3159 | assert(out.byteLength > 0, 'out must not be empty') | |
| 3160 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 3161 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 3162 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 3163 | assert(salt.byteLength > 0, 'salt must not be empty') | |
| 3164 | ||
| 3165 | const [done, promise] = checkStatus(callback) | |
| 3166 | ||
| 3167 | binding.extension_pbkdf2_sha512_async( | |
| 3168 | out.buffer, | |
| 3169 | out.byteOffset, | |
| 3170 | out.byteLength, | |
| 3171 | ||
| 3172 | passwd.buffer, | |
| 3173 | passwd.byteOffset, | |
| 3174 | passwd.byteLength, | |
| 3175 | ||
| 3176 | salt.buffer, | |
| 3177 | salt.byteOffset, | |
| 3178 | salt.byteLength, | |
| 3179 | ||
| 3180 | iter, | |
| 3181 | outlen, | |
| 3182 | ||
| 3183 | done | |
| 3184 | ) | |
| 3185 | ||
| 3186 | return promise | |
| 3187 | } | |
| 3188 | ||
| 3189 | exports.extension_pbkdf2_sha512 = function (out, passwd, salt, iter, outlen) { | |
| 3190 | assert( | |
| 3191 | iter >= binding.extension_pbkdf2_sha512_ITERATIONS_MIN, | |
| 3192 | "iter must be at least 'extension_pbkdf2_sha512_ITERATIONS_MIN'" | |
| 3193 | ) | |
| 3194 | assert( | |
| 3195 | outlen <= binding.extension_pbkdf2_sha512_BYTES_MAX, | |
| 3196 | "outlen must be at most 'extension_pbkdf2_sha512_BYTES_MAX'" | |
| 3197 | ) | |
| 3198 | assert(ArrayBuffer.isView(out), 'out must be a typed array') | |
| 3199 | assert(out.byteLength >= outlen, "out must be at least 'outlen' bytes") | |
| 3200 | assert(out.byteLength > 0, 'out must not be empty') | |
| 3201 | assert(ArrayBuffer.isView(passwd), 'passwd must be a typed array') | |
| 3202 | assert(passwd.byteLength > 0, 'passwd must not be empty') | |
| 3203 | assert(ArrayBuffer.isView(salt), 'salt must be a typed array') | |
| 3204 | assert(salt.byteLength > 0, 'salt must not be empty') | |
| 3205 | ||
| 3206 | const res = binding.extension_pbkdf2_sha512(out, passwd, salt, iter, outlen) | |
| 3207 | ||
| 3208 | if (res !== 0) throw new Error('failed to add tweak to public key') | |
| 3209 | } | |
| 3210 | ||
| 3211 | function checkStatus(callback, booleanResult = false) { | |
| 3212 | let done, promise | |
| 3213 | ||
| 3214 | if (typeof callback === 'function') { | |
| 3215 | done = function (status) { | |
| 3216 | if (booleanResult) callback(null, status === 0) | |
| 3217 | else if (status === 0) callback(null) | |
| 3218 | else callback(new Error('status: ' + status)) | |
| 3219 | } | |
| 3220 | } else { | |
| 3221 | promise = new Promise(function (resolve, reject) { | |
| 3222 | done = function (status) { | |
| 3223 | if (booleanResult) resolve(status === 0) | |
| 3224 | else if (status === 0) resolve() | |
| 3225 | else reject(new Error('status: ' + status)) | |
| 3226 | } | |
| 3227 | }) | |
| 3228 | } | |
| 3229 | ||
| 3230 | return [done, promise] | |
| 3231 | } |
| … | … | @@ -0,0 +1,62 @@ |
| 1 | { | |
| 2 | "name": "sodium-native", | |
| 3 | "version": "5.1.0", | |
| 4 | "description": "Low level bindings for libsodium", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js", | |
| 8 | "binding.cc", | |
| 9 | "binding.js", | |
| 10 | "extensions", | |
| 11 | "prebuilds", | |
| 12 | "CMakeLists.txt" | |
| 13 | ], | |
| 14 | "imports": { | |
| 15 | "assert": { | |
| 16 | "bare": "bare-assert", | |
| 17 | "node": "assert" | |
| 18 | } | |
| 19 | }, | |
| 20 | "addon": true, | |
| 21 | "dependencies": { | |
| 22 | "bare-assert": "^1.2.0", | |
| 23 | "require-addon": "^1.1.0", | |
| 24 | "which-runtime": "^1.2.1" | |
| 25 | }, | |
| 26 | "devDependencies": { | |
| 27 | "bare-compat-napi": "^1.3.5", | |
| 28 | "brittle": "^3.16.2", | |
| 29 | "cmake-bare": "^1.6.1", | |
| 30 | "cmake-fetch": "^1.4.3", | |
| 31 | "cmake-napi": "^1.2.1", | |
| 32 | "prettier": "^3.6.2", | |
| 33 | "prettier-config-holepunch": "^2.0.0" | |
| 34 | }, | |
| 35 | "scripts": { | |
| 36 | "test": "prettier . --check && npm run test:node && npm run test:bare", | |
| 37 | "test:node": "node test/all.js", | |
| 38 | "test:bare": "bare test/all.js" | |
| 39 | }, | |
| 40 | "standard": { | |
| 41 | "ignore": [ | |
| 42 | "/test/fixtures/*.js" | |
| 43 | ] | |
| 44 | }, | |
| 45 | "engines": { | |
| 46 | "bare": ">=1.16.0" | |
| 47 | }, | |
| 48 | "repository": { | |
| 49 | "type": "git", | |
| 50 | "url": "git+https://github.com/holepunchto/sodium-native.git" | |
| 51 | }, | |
| 52 | "contributors": [ | |
| 53 | "Emil Bay <github@tixz.dk> (http://bayes.dk)", | |
| 54 | "Mathias Buus <mathiasbuus@gmail.com> (https://mafinto.sh)", | |
| 55 | "Christophe Diederichs <chm-diederichs@hyperdivision.dk>" | |
| 56 | ], | |
| 57 | "license": "MIT", | |
| 58 | "bugs": { | |
| 59 | "url": "https://github.com/holepunchto/sodium-native/issues" | |
| 60 | }, | |
| 61 | "homepage": "https://github.com/holepunchto/sodium-native" | |
| 62 | } |
| … | … | @@ -0,0 +1,7 @@ |
| 1 | Copyright 2017 Mathias Buus & Emil Bay | |
| 2 | ||
| 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
| 4 | ||
| 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
| 6 | ||
| 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| … | … | @@ -0,0 +1,288 @@ |
| 1 | # sodium-universal | |
| 2 | ||
| 3 | Universal wrapper for `sodium-javascript` and `sodium-native` working in Node.js and the Browser | |
| 4 | ||
| 5 | ```sh | |
| 6 | npm install sodium-universal | |
| 7 | ``` | |
| 8 | ||
| 9 | `sodium-native` ships with prebuilds for macOS, Windows, and Linux. | |
| 10 | ||
| 11 | Note that the module itself is generated on a prepublish hook. | |
| 12 | ||
| 13 | ## Usage | |
| 14 | ||
| 15 | ```js | |
| 16 | var sodium = require('sodium-universal') | |
| 17 | ||
| 18 | var rnd = Buffer.allocUnsafe(12) // Cryptographically random data | |
| 19 | sodium.randombytes_buf(rnd) | |
| 20 | ||
| 21 | console.log(rnd.toString('hex')) | |
| 22 | ||
| 23 | ``` | |
| 24 | ||
| 25 | Works seamlessly with Node.js: | |
| 26 | ||
| 27 | ```sh | |
| 28 | $ node example.js | |
| 29 | c7dbd46a6cc84ff2e0d1285c | |
| 30 | ``` | |
| 31 | ||
| 32 | And the browser: | |
| 33 | ||
| 34 | First install the peer dep, `sodium-javascript`. | |
| 35 | ||
| 36 | ```sh | |
| 37 | npm install --save sodium-javascript | |
| 38 | ``` | |
| 39 | ||
| 40 | And then browserify | |
| 41 | ||
| 42 | ```sh | |
| 43 | browserify example.js > bundle.js | |
| 44 | ``` | |
| 45 | ||
| 46 | ## Introduction | |
| 47 | ||
| 48 | [`libsodium`](https://download.libsodium.org/doc/) is a collection of cryptographic primitives, providing a low-level foundation to build higher-level cryptographic applications and protocols. It is often put in contrast to RSA based cryptography and OpenSSL, even though they all share overlapping algorithms and target slightly different audiences. Sodium is a collection of modern collection of primitives, fulfilling the same cryptographic tasks, but based on simpler and more efficient algorithms. | |
| 49 | ||
| 50 | This library provides seamless bindings to [`sodium-native`](https://github.com/sodium-friends/sodium-native), which is the original C implementation of `libsodium` exposed as a Node native module. For the browser we expose [`sodium-javascript`](https://github.com/sodium-friends/sodium-javascript), using the `package.json` [`browser`](https://github.com/defunctzombie/package-browser-field-spec) field, which is supported by most bundlers. | |
| 51 | ||
| 52 | ## API | |
| 53 | ||
| 54 | Please refer to [`sodium-native`](https://github.com/sodium-friends/sodium-native#api) and [`sodium-javascript`](https://github.com/sodium-friends/sodium-javascript#api). Note that the two modules do not yet have feature parity, where `sodium-native` is the more featureful of the two. | |
| 55 | ||
| 56 | ## Compatibilty | |
| 57 | ||
| 58 | <!-- Generated by build-scripts/symbols.js with | |
| 59 | sodium-native@3.2.0 | |
| 60 | sodium-javascript@0.6.0 --> | |
| 61 | | C Library Symbol | `sodium-native` | `sodium-javascript` | | |
| 62 | |:--------------------------------------------------------:|:--------------------:|:--------------------:| | |
| 63 | | `crypto_aead_chacha20poly1305_ietf_ABYTES` | :white_check_mark: | :white_check_mark: | | |
| 64 | | `crypto_aead_chacha20poly1305_ietf_decrypt` | :white_check_mark: | :white_check_mark: | | |
| 65 | | `crypto_aead_chacha20poly1305_ietf_decrypt_detached` | :white_check_mark: | :white_check_mark: | | |
| 66 | | `crypto_aead_chacha20poly1305_ietf_encrypt` | :white_check_mark: | :white_check_mark: | | |
| 67 | | `crypto_aead_chacha20poly1305_ietf_encrypt_detached` | :white_check_mark: | :white_check_mark: | | |
| 68 | | `crypto_aead_chacha20poly1305_ietf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 69 | | `crypto_aead_chacha20poly1305_ietf_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 70 | | `crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 71 | | `crypto_aead_chacha20poly1305_ietf_NPUBBYTES` | :white_check_mark: | :white_check_mark: | | |
| 72 | | `crypto_aead_chacha20poly1305_ietf_NSECBYTES` | :white_check_mark: | :white_check_mark: | | |
| 73 | | `crypto_aead_xchacha20poly1305_ietf_ABYTES` | :white_check_mark: | :small_red_triangle: | | |
| 74 | | `crypto_aead_xchacha20poly1305_ietf_decrypt` | :white_check_mark: | :small_red_triangle: | | |
| 75 | | `crypto_aead_xchacha20poly1305_ietf_decrypt_detached` | :white_check_mark: | :small_red_triangle: | | |
| 76 | | `crypto_aead_xchacha20poly1305_ietf_encrypt` | :white_check_mark: | :small_red_triangle: | | |
| 77 | | `crypto_aead_xchacha20poly1305_ietf_encrypt_detached` | :white_check_mark: | :small_red_triangle: | | |
| 78 | | `crypto_aead_xchacha20poly1305_ietf_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 79 | | `crypto_aead_xchacha20poly1305_ietf_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 80 | | `crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 81 | | `crypto_aead_xchacha20poly1305_ietf_NPUBBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 82 | | `crypto_aead_xchacha20poly1305_ietf_NSECBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 83 | | `crypto_auth` | :white_check_mark: | :small_red_triangle: | | |
| 84 | | `crypto_auth_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 85 | | `crypto_auth_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 86 | | `crypto_auth_verify` | :white_check_mark: | :small_red_triangle: | | |
| 87 | | `crypto_box_BEFORENMBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 88 | | `crypto_box_BOXZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 89 | | `crypto_box_detached` | :white_check_mark: | :small_red_triangle: | | |
| 90 | | `crypto_box_easy` | :white_check_mark: | :small_red_triangle: | | |
| 91 | | `crypto_box_keypair` | :white_check_mark: | :white_check_mark: | | |
| 92 | | `crypto_box_MACBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 93 | | `crypto_box_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 94 | | `crypto_box_open_detached` | :white_check_mark: | :small_red_triangle: | | |
| 95 | | `crypto_box_open_easy` | :white_check_mark: | :small_red_triangle: | | |
| 96 | | `crypto_box_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 97 | | `crypto_box_seal` | :white_check_mark: | :white_check_mark: | | |
| 98 | | `crypto_box_seal_open` | :white_check_mark: | :white_check_mark: | | |
| 99 | | `crypto_box_SEALBYTES` | :white_check_mark: | :white_check_mark: | | |
| 100 | | `crypto_box_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 101 | | `crypto_box_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 102 | | `crypto_box_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 103 | | `crypto_box_ZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 104 | | `crypto_core_ed25519_add` | :white_check_mark: | :small_red_triangle: | | |
| 105 | | `crypto_core_ed25519_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 106 | | `crypto_core_ed25519_from_uniform` | :white_check_mark: | :small_red_triangle: | | |
| 107 | | `crypto_core_ed25519_is_valid_point` | :white_check_mark: | :small_red_triangle: | | |
| 108 | | `crypto_core_ed25519_NONREDUCEDSCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 109 | | `crypto_core_ed25519_scalar_add` | :white_check_mark: | :small_red_triangle: | | |
| 110 | | `crypto_core_ed25519_scalar_complement` | :white_check_mark: | :small_red_triangle: | | |
| 111 | | `crypto_core_ed25519_scalar_invert` | :white_check_mark: | :small_red_triangle: | | |
| 112 | | `crypto_core_ed25519_scalar_negate` | :white_check_mark: | :small_red_triangle: | | |
| 113 | | `crypto_core_ed25519_scalar_random` | :white_check_mark: | :small_red_triangle: | | |
| 114 | | `crypto_core_ed25519_scalar_reduce` | :white_check_mark: | :small_red_triangle: | | |
| 115 | | `crypto_core_ed25519_scalar_sub` | :white_check_mark: | :small_red_triangle: | | |
| 116 | | `crypto_core_ed25519_SCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 117 | | `crypto_core_ed25519_sub` | :white_check_mark: | :small_red_triangle: | | |
| 118 | | `crypto_core_ed25519_UNIFORMBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 119 | | `crypto_generichash` | :white_check_mark: | :white_check_mark: | | |
| 120 | | `crypto_generichash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 121 | | `crypto_generichash_BYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 122 | | `crypto_generichash_BYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 123 | | `crypto_generichash_final` | :white_check_mark: | :small_red_triangle: | | |
| 124 | | `crypto_generichash_init` | :white_check_mark: | :small_red_triangle: | | |
| 125 | | `crypto_generichash_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 126 | | `crypto_generichash_KEYBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 127 | | `crypto_generichash_KEYBYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 128 | | `crypto_generichash_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 129 | | `crypto_generichash_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 130 | | `crypto_generichash_update` | :white_check_mark: | :small_red_triangle: | | |
| 131 | | `crypto_hash` | :white_check_mark: | :white_check_mark: | | |
| 132 | | `crypto_hash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 133 | | `crypto_hash_sha256` | :white_check_mark: | :white_check_mark: | | |
| 134 | | `crypto_hash_sha256_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 135 | | `crypto_hash_sha256_final` | :white_check_mark: | :small_red_triangle: | | |
| 136 | | `crypto_hash_sha256_init` | :white_check_mark: | :small_red_triangle: | | |
| 137 | | `crypto_hash_sha256_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 138 | | `crypto_hash_sha256_update` | :white_check_mark: | :small_red_triangle: | | |
| 139 | | `crypto_hash_sha512` | :white_check_mark: | :white_check_mark: | | |
| 140 | | `crypto_hash_sha512_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 141 | | `crypto_hash_sha512_final` | :white_check_mark: | :small_red_triangle: | | |
| 142 | | `crypto_hash_sha512_init` | :white_check_mark: | :small_red_triangle: | | |
| 143 | | `crypto_hash_sha512_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 144 | | `crypto_hash_sha512_update` | :white_check_mark: | :small_red_triangle: | | |
| 145 | | `crypto_kdf_BYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 146 | | `crypto_kdf_BYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 147 | | `crypto_kdf_CONTEXTBYTES` | :white_check_mark: | :white_check_mark: | | |
| 148 | | `crypto_kdf_derive_from_key` | :white_check_mark: | :white_check_mark: | | |
| 149 | | `crypto_kdf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 150 | | `crypto_kdf_keygen` | :white_check_mark: | :white_check_mark: | | |
| 151 | | `crypto_kx_client_session_keys` | :white_check_mark: | :small_red_triangle: | | |
| 152 | | `crypto_kx_keypair` | :white_check_mark: | :white_check_mark: | | |
| 153 | | `crypto_kx_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 154 | | `crypto_kx_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 155 | | `crypto_kx_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 156 | | `crypto_kx_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 157 | | `crypto_kx_server_session_keys` | :white_check_mark: | :small_red_triangle: | | |
| 158 | | `crypto_kx_SESSIONKEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 159 | | `crypto_onetimeauth` | :white_check_mark: | :white_check_mark: | | |
| 160 | | `crypto_onetimeauth_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 161 | | `crypto_onetimeauth_final` | :white_check_mark: | :small_red_triangle: | | |
| 162 | | `crypto_onetimeauth_init` | :white_check_mark: | :small_red_triangle: | | |
| 163 | | `crypto_onetimeauth_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 164 | | `crypto_onetimeauth_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 165 | | `crypto_onetimeauth_update` | :white_check_mark: | :small_red_triangle: | | |
| 166 | | `crypto_onetimeauth_verify` | :white_check_mark: | :white_check_mark: | | |
| 167 | | `crypto_pwhash` | :white_check_mark: | :small_red_triangle: | | |
| 168 | | `crypto_pwhash_BYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 169 | | `crypto_pwhash_BYTES_MIN` | :white_check_mark: | :small_red_triangle: | | |
| 170 | | `crypto_pwhash_SALTBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 171 | | `crypto_pwhash_scryptsalsa208sha256` | :white_check_mark: | :small_red_triangle: | | |
| 172 | | `crypto_pwhash_scryptsalsa208sha256_BYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 173 | | `crypto_pwhash_scryptsalsa208sha256_BYTES_MIN` | :white_check_mark: | :small_red_triangle: | | |
| 174 | | `crypto_pwhash_scryptsalsa208sha256_SALTBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 175 | | `crypto_pwhash_scryptsalsa208sha256_str` | :white_check_mark: | :small_red_triangle: | | |
| 176 | | `crypto_pwhash_scryptsalsa208sha256_str_needs_rehash` | :white_check_mark: | :small_red_triangle: | | |
| 177 | | `crypto_pwhash_scryptsalsa208sha256_str_verify` | :white_check_mark: | :small_red_triangle: | | |
| 178 | | `crypto_pwhash_scryptsalsa208sha256_STRBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 179 | | `crypto_pwhash_str` | :white_check_mark: | :small_red_triangle: | | |
| 180 | | `crypto_pwhash_str_needs_rehash` | :white_check_mark: | :small_red_triangle: | | |
| 181 | | `crypto_pwhash_str_verify` | :white_check_mark: | :small_red_triangle: | | |
| 182 | | `crypto_pwhash_STRBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 183 | | `crypto_scalarmult` | :white_check_mark: | :white_check_mark: | | |
| 184 | | `crypto_scalarmult_base` | :white_check_mark: | :white_check_mark: | | |
| 185 | | `crypto_scalarmult_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 186 | | `crypto_scalarmult_ed25519` | :white_check_mark: | :small_red_triangle: | | |
| 187 | | `crypto_scalarmult_ed25519_base` | :white_check_mark: | :small_red_triangle: | | |
| 188 | | `crypto_scalarmult_ed25519_base_noclamp` | :white_check_mark: | :small_red_triangle: | | |
| 189 | | `crypto_scalarmult_ed25519_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 190 | | `crypto_scalarmult_ed25519_noclamp` | :white_check_mark: | :small_red_triangle: | | |
| 191 | | `crypto_scalarmult_ed25519_SCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 192 | | `crypto_scalarmult_SCALARBYTES` | :white_check_mark: | :white_check_mark: | | |
| 193 | | `crypto_secretbox` | :small_red_triangle: | :white_check_mark: | | |
| 194 | | `crypto_secretbox_BOXZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 195 | | `crypto_secretbox_detached` | :white_check_mark: | :white_check_mark: | | |
| 196 | | `crypto_secretbox_easy` | :white_check_mark: | :white_check_mark: | | |
| 197 | | `crypto_secretbox_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 198 | | `crypto_secretbox_MACBYTES` | :white_check_mark: | :white_check_mark: | | |
| 199 | | `crypto_secretbox_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 200 | | `crypto_secretbox_open` | :small_red_triangle: | :white_check_mark: | | |
| 201 | | `crypto_secretbox_open_detached` | :white_check_mark: | :white_check_mark: | | |
| 202 | | `crypto_secretbox_open_easy` | :white_check_mark: | :white_check_mark: | | |
| 203 | | `crypto_secretbox_ZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 204 | | `crypto_secretstream_xchacha20poly1305_ABYTES` | :white_check_mark: | :small_red_triangle: | | |
| 205 | | `crypto_secretstream_xchacha20poly1305_HEADERBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 206 | | `crypto_secretstream_xchacha20poly1305_init_pull` | :white_check_mark: | :small_red_triangle: | | |
| 207 | | `crypto_secretstream_xchacha20poly1305_init_push` | :white_check_mark: | :small_red_triangle: | | |
| 208 | | `crypto_secretstream_xchacha20poly1305_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 209 | | `crypto_secretstream_xchacha20poly1305_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 210 | | `crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 211 | | `crypto_secretstream_xchacha20poly1305_pull` | :white_check_mark: | :small_red_triangle: | | |
| 212 | | `crypto_secretstream_xchacha20poly1305_push` | :white_check_mark: | :small_red_triangle: | | |
| 213 | | `crypto_secretstream_xchacha20poly1305_rekey` | :white_check_mark: | :small_red_triangle: | | |
| 214 | | `crypto_secretstream_xchacha20poly1305_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 215 | | `crypto_shorthash` | :white_check_mark: | :white_check_mark: | | |
| 216 | | `crypto_shorthash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 217 | | `crypto_shorthash_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 218 | | `crypto_sign` | :white_check_mark: | :white_check_mark: | | |
| 219 | | `crypto_sign_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 220 | | `crypto_sign_detached` | :white_check_mark: | :white_check_mark: | | |
| 221 | | `crypto_sign_ed25519_pk_to_curve25519` | :white_check_mark: | :small_red_triangle: | | |
| 222 | | `crypto_sign_ed25519_sk_to_curve25519` | :white_check_mark: | :small_red_triangle: | | |
| 223 | | `crypto_sign_ed25519_sk_to_pk` | :white_check_mark: | :small_red_triangle: | | |
| 224 | | `crypto_sign_keypair` | :white_check_mark: | :white_check_mark: | | |
| 225 | | `crypto_sign_open` | :white_check_mark: | :white_check_mark: | | |
| 226 | | `crypto_sign_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 227 | | `crypto_sign_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 228 | | `crypto_sign_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 229 | | `crypto_sign_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 230 | | `crypto_sign_verify_detached` | :white_check_mark: | :white_check_mark: | | |
| 231 | | `crypto_stream` | :white_check_mark: | :white_check_mark: | | |
| 232 | | `crypto_stream_chacha20` | :white_check_mark: | :white_check_mark: | | |
| 233 | | `crypto_stream_chacha20_ietf` | :white_check_mark: | :white_check_mark: | | |
| 234 | | `crypto_stream_chacha20_ietf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 235 | | `crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 236 | | `crypto_stream_chacha20_ietf_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 237 | | `crypto_stream_chacha20_ietf_xor` | :white_check_mark: | :white_check_mark: | | |
| 238 | | `crypto_stream_chacha20_ietf_xor_ic` | :white_check_mark: | :white_check_mark: | | |
| 239 | | `crypto_stream_chacha20_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 240 | | `crypto_stream_chacha20_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 241 | | `crypto_stream_chacha20_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 242 | | `crypto_stream_chacha20_xor` | :white_check_mark: | :white_check_mark: | | |
| 243 | | `crypto_stream_chacha20_xor_ic` | :white_check_mark: | :white_check_mark: | | |
| 244 | | `crypto_stream_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 245 | | `crypto_stream_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 246 | | `crypto_stream_salsa20_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 247 | | `crypto_stream_salsa20_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 248 | | `crypto_stream_salsa20_NONCEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 249 | | `crypto_stream_salsa20_xor` | :white_check_mark: | :small_red_triangle: | | |
| 250 | | `crypto_stream_salsa20_xor_ic` | :white_check_mark: | :small_red_triangle: | | |
| 251 | | `crypto_stream_xchacha20` | :white_check_mark: | :small_red_triangle: | | |
| 252 | | `crypto_stream_xchacha20_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 253 | | `crypto_stream_xchacha20_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 254 | | `crypto_stream_xchacha20_NONCEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 255 | | `crypto_stream_xchacha20_xor` | :white_check_mark: | :small_red_triangle: | | |
| 256 | | `crypto_stream_xchacha20_xor_ic` | :white_check_mark: | :small_red_triangle: | | |
| 257 | | `crypto_stream_xor` | :white_check_mark: | :white_check_mark: | | |
| 258 | | `crypto_verify_16` | :small_red_triangle: | :white_check_mark: | | |
| 259 | | `crypto_verify_32` | :small_red_triangle: | :white_check_mark: | | |
| 260 | | `randombytes_buf` | :white_check_mark: | :white_check_mark: | | |
| 261 | | `randombytes_buf_deterministic` | :white_check_mark: | :small_red_triangle: | | |
| 262 | | `randombytes_random` | :white_check_mark: | :small_red_triangle: | | |
| 263 | | `randombytes_SEEDBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 264 | | `randombytes_uniform` | :white_check_mark: | :small_red_triangle: | | |
| 265 | | `sodium_add` | :white_check_mark: | :small_red_triangle: | | |
| 266 | | `sodium_compare` | :white_check_mark: | :small_red_triangle: | | |
| 267 | | `sodium_free` | :white_check_mark: | :small_red_triangle: | | |
| 268 | | `sodium_increment` | :white_check_mark: | :small_red_triangle: | | |
| 269 | | `sodium_is_zero` | :white_check_mark: | :white_check_mark: | | |
| 270 | | `sodium_malloc` | :white_check_mark: | :white_check_mark: | | |
| 271 | | `sodium_memcmp` | :white_check_mark: | :white_check_mark: | | |
| 272 | | `sodium_memzero` | :white_check_mark: | :white_check_mark: | | |
| 273 | | `sodium_mlock` | :white_check_mark: | :small_red_triangle: | | |
| 274 | | `sodium_mprotect_noaccess` | :white_check_mark: | :small_red_triangle: | | |
| 275 | | `sodium_mprotect_readonly` | :white_check_mark: | :small_red_triangle: | | |
| 276 | | `sodium_mprotect_readwrite` | :white_check_mark: | :small_red_triangle: | | |
| 277 | | `sodium_munlock` | :white_check_mark: | :small_red_triangle: | | |
| 278 | | `sodium_pad` | :white_check_mark: | :small_red_triangle: | | |
| 279 | | `sodium_sub` | :white_check_mark: | :small_red_triangle: | | |
| 280 | | `sodium_unpad` | :white_check_mark: | :small_red_triangle: | | |
| 281 | ||
| 282 | ### Missing altogether (Ctrl + F friendly) | |
| 283 | ||
| 284 | `crypto_aead_aes256gcm_ABYTES`, `crypto_aead_aes256gcm_beforenm`, `crypto_aead_aes256gcm_decrypt`, `crypto_aead_aes256gcm_decrypt_afternm`, `crypto_aead_aes256gcm_decrypt_detached`, `crypto_aead_aes256gcm_decrypt_detached_afternm`, `crypto_aead_aes256gcm_encrypt`, `crypto_aead_aes256gcm_encrypt_afternm`, `crypto_aead_aes256gcm_encrypt_detached`, `crypto_aead_aes256gcm_encrypt_detached_afternm`, `crypto_aead_aes256gcm_is_available`, `crypto_aead_aes256gcm_KEYBYTES`, `crypto_aead_aes256gcm_keygen`, `crypto_aead_aes256gcm_MESSAGEBYTES_MAX`, `crypto_aead_aes256gcm_NPUBBYTES`, `crypto_aead_aes256gcm_NSECBYTES`, `crypto_aead_aes256gcm_STATEBYTES`, `crypto_aead_chacha20poly1305_ABYTES`, `crypto_aead_chacha20poly1305_decrypt`, `crypto_aead_chacha20poly1305_decrypt_detached`, `crypto_aead_chacha20poly1305_encrypt`, `crypto_aead_chacha20poly1305_encrypt_detached`, `crypto_aead_chacha20poly1305_KEYBYTES`, `crypto_aead_chacha20poly1305_keygen`, `crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX`, `crypto_aead_chacha20poly1305_NPUBBYTES`, `crypto_aead_chacha20poly1305_NSECBYTES`, `crypto_auth_hmacsha256`, `crypto_auth_hmacsha256_BYTES`, `crypto_auth_hmacsha256_final`, `crypto_auth_hmacsha256_init`, `crypto_auth_hmacsha256_KEYBYTES`, `crypto_auth_hmacsha256_keygen`, `crypto_auth_hmacsha256_STATEBYTES`, `crypto_auth_hmacsha256_update`, `crypto_auth_hmacsha256_verify`, `crypto_auth_hmacsha512`, `crypto_auth_hmacsha512256`, `crypto_auth_hmacsha512256_BYTES`, `crypto_auth_hmacsha512256_final`, `crypto_auth_hmacsha512256_init`, `crypto_auth_hmacsha512256_KEYBYTES`, `crypto_auth_hmacsha512256_keygen`, `crypto_auth_hmacsha512256_STATEBYTES`, `crypto_auth_hmacsha512256_update`, `crypto_auth_hmacsha512256_verify`, `crypto_auth_hmacsha512_BYTES`, `crypto_auth_hmacsha512_final`, `crypto_auth_hmacsha512_init`, `crypto_auth_hmacsha512_KEYBYTES`, `crypto_auth_hmacsha512_keygen`, `crypto_auth_hmacsha512_STATEBYTES`, `crypto_auth_hmacsha512_update`, `crypto_auth_hmacsha512_verify`, `crypto_auth_keygen`, `crypto_auth_primitive`, `crypto_box`, `crypto_box_afternm`, `crypto_box_beforenm`, `crypto_box_curve25519xchacha20poly1305_beforenm`, `crypto_box_curve25519xchacha20poly1305_BEFORENMBYTES`, `crypto_box_curve25519xchacha20poly1305_detached`, `crypto_box_curve25519xchacha20poly1305_detached_afternm`, `crypto_box_curve25519xchacha20poly1305_easy`, `crypto_box_curve25519xchacha20poly1305_easy_afternm`, `crypto_box_curve25519xchacha20poly1305_keypair`, `crypto_box_curve25519xchacha20poly1305_MACBYTES`, `crypto_box_curve25519xchacha20poly1305_MESSAGEBYTES_MAX`, `crypto_box_curve25519xchacha20poly1305_NONCEBYTES`, `crypto_box_curve25519xchacha20poly1305_open_detached`, `crypto_box_curve25519xchacha20poly1305_open_detached_afternm`, `crypto_box_curve25519xchacha20poly1305_open_easy`, `crypto_box_curve25519xchacha20poly1305_open_easy_afternm`, `crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES`, `crypto_box_curve25519xchacha20poly1305_seal`, `crypto_box_curve25519xchacha20poly1305_seal_open`, `crypto_box_curve25519xchacha20poly1305_SEALBYTES`, `crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES`, `crypto_box_curve25519xchacha20poly1305_seed_keypair`, `crypto_box_curve25519xchacha20poly1305_SEEDBYTES`, `crypto_box_curve25519xsalsa20poly1305`, `crypto_box_curve25519xsalsa20poly1305_afternm`, `crypto_box_curve25519xsalsa20poly1305_beforenm`, `crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES`, `crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES`, `crypto_box_curve25519xsalsa20poly1305_keypair`, `crypto_box_curve25519xsalsa20poly1305_MACBYTES`, `crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX`, `crypto_box_curve25519xsalsa20poly1305_NONCEBYTES`, `crypto_box_curve25519xsalsa20poly1305_open`, `crypto_box_curve25519xsalsa20poly1305_open_afternm`, `crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES`, `crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES`, `crypto_box_curve25519xsalsa20poly1305_seed_keypair`, `crypto_box_curve25519xsalsa20poly1305_SEEDBYTES`, `crypto_box_curve25519xsalsa20poly1305_ZEROBYTES`, `crypto_box_detached_afternm`, `crypto_box_easy_afternm`, `crypto_box_MESSAGEBYTES_MAX`, `crypto_box_open`, `crypto_box_open_afternm`, `crypto_box_open_detached_afternm`, `crypto_box_open_easy_afternm`, `crypto_box_primitive`, `crypto_core_ed25519_from_hash`, `crypto_core_ed25519_HASHBYTES`, `crypto_core_ed25519_random`, `crypto_core_ed25519_scalar_mul`, `crypto_core_hchacha20`, `crypto_core_hchacha20_CONSTBYTES`, `crypto_core_hchacha20_INPUTBYTES`, `crypto_core_hchacha20_KEYBYTES`, `crypto_core_hchacha20_OUTPUTBYTES`, `crypto_core_hsalsa20`, `crypto_core_hsalsa20_CONSTBYTES`, `crypto_core_hsalsa20_INPUTBYTES`, `crypto_core_hsalsa20_KEYBYTES`, `crypto_core_hsalsa20_OUTPUTBYTES`, `crypto_core_ristretto255_add`, `crypto_core_ristretto255_BYTES`, `crypto_core_ristretto255_from_hash`, `crypto_core_ristretto255_HASHBYTES`, `crypto_core_ristretto255_is_valid_point`, `crypto_core_ristretto255_NONREDUCEDSCALARBYTES`, `crypto_core_ristretto255_random`, `crypto_core_ristretto255_scalar_add`, `crypto_core_ristretto255_scalar_complement`, `crypto_core_ristretto255_scalar_invert`, `crypto_core_ristretto255_scalar_mul`, `crypto_core_ristretto255_scalar_negate`, `crypto_core_ristretto255_scalar_random`, `crypto_core_ristretto255_scalar_reduce`, `crypto_core_ristretto255_scalar_sub`, `crypto_core_ristretto255_SCALARBYTES`, `crypto_core_ristretto255_sub`, `crypto_core_salsa20`, `crypto_core_salsa2012`, `crypto_core_salsa2012_CONSTBYTES`, `crypto_core_salsa2012_INPUTBYTES`, `crypto_core_salsa2012_KEYBYTES`, `crypto_core_salsa2012_OUTPUTBYTES`, `crypto_core_salsa208`, `crypto_core_salsa208_CONSTBYTES`, `crypto_core_salsa208_INPUTBYTES`, `crypto_core_salsa208_KEYBYTES`, `crypto_core_salsa208_OUTPUTBYTES`, `crypto_core_salsa20_CONSTBYTES`, `crypto_core_salsa20_INPUTBYTES`, `crypto_core_salsa20_KEYBYTES`, `crypto_core_salsa20_OUTPUTBYTES`, `crypto_generichash_blake2b`, `crypto_generichash_blake2b_BYTES`, `crypto_generichash_blake2b_BYTES_MAX`, `crypto_generichash_blake2b_BYTES_MIN`, `crypto_generichash_blake2b_final`, `crypto_generichash_blake2b_init`, `crypto_generichash_blake2b_init_salt_personal`, `crypto_generichash_blake2b_KEYBYTES`, `crypto_generichash_blake2b_KEYBYTES_MAX`, `crypto_generichash_blake2b_KEYBYTES_MIN`, `crypto_generichash_blake2b_keygen`, `crypto_generichash_blake2b_PERSONALBYTES`, `crypto_generichash_blake2b_salt_personal`, `crypto_generichash_blake2b_SALTBYTES`, `crypto_generichash_blake2b_STATEBYTES`, `crypto_generichash_blake2b_update`, `crypto_generichash_primitive`, `crypto_hash_primitive`, `crypto_kdf_blake2b_BYTES_MAX`, `crypto_kdf_blake2b_BYTES_MIN`, `crypto_kdf_blake2b_CONTEXTBYTES`, `crypto_kdf_blake2b_derive_from_key`, `crypto_kdf_blake2b_KEYBYTES`, `crypto_kdf_primitive`, `crypto_kx_primitive`, `crypto_onetimeauth_keygen`, `crypto_onetimeauth_poly1305`, `crypto_onetimeauth_poly1305_BYTES`, `crypto_onetimeauth_poly1305_final`, `crypto_onetimeauth_poly1305_init`, `crypto_onetimeauth_poly1305_KEYBYTES`, `crypto_onetimeauth_poly1305_keygen`, `crypto_onetimeauth_poly1305_STATEBYTES`, `crypto_onetimeauth_poly1305_update`, `crypto_onetimeauth_poly1305_verify`, `crypto_onetimeauth_primitive`, `crypto_pwhash_alg_argon2i13`, `crypto_pwhash_alg_argon2id13`, `crypto_pwhash_alg_default`, `crypto_pwhash_argon2i`, `crypto_pwhash_argon2i_alg_argon2i13`, `crypto_pwhash_argon2i_BYTES_MAX`, `crypto_pwhash_argon2i_BYTES_MIN`, `crypto_pwhash_argon2i_memlimit_interactive`, `crypto_pwhash_argon2i_memlimit_max`, `crypto_pwhash_argon2i_memlimit_min`, `crypto_pwhash_argon2i_memlimit_moderate`, `crypto_pwhash_argon2i_memlimit_sensitive`, `crypto_pwhash_argon2i_opslimit_interactive`, `crypto_pwhash_argon2i_opslimit_max`, `crypto_pwhash_argon2i_opslimit_min`, `crypto_pwhash_argon2i_opslimit_moderate`, `crypto_pwhash_argon2i_opslimit_sensitive`, `crypto_pwhash_argon2i_passwd_max`, `crypto_pwhash_argon2i_passwd_min`, `crypto_pwhash_argon2i_SALTBYTES`, `crypto_pwhash_argon2i_str`, `crypto_pwhash_argon2i_str_needs_rehash`, `crypto_pwhash_argon2i_str_verify`, `crypto_pwhash_argon2i_STRBYTES`, `crypto_pwhash_argon2i_strprefix`, `crypto_pwhash_argon2id`, `crypto_pwhash_argon2id_alg_argon2id13`, `crypto_pwhash_argon2id_BYTES_MAX`, `crypto_pwhash_argon2id_BYTES_MIN`, `crypto_pwhash_argon2id_memlimit_interactive`, `crypto_pwhash_argon2id_memlimit_max`, `crypto_pwhash_argon2id_memlimit_min`, `crypto_pwhash_argon2id_memlimit_moderate`, `crypto_pwhash_argon2id_memlimit_sensitive`, `crypto_pwhash_argon2id_opslimit_interactive`, `crypto_pwhash_argon2id_opslimit_max`, `crypto_pwhash_argon2id_opslimit_min`, `crypto_pwhash_argon2id_opslimit_moderate`, `crypto_pwhash_argon2id_opslimit_sensitive`, `crypto_pwhash_argon2id_passwd_max`, `crypto_pwhash_argon2id_passwd_min`, `crypto_pwhash_argon2id_SALTBYTES`, `crypto_pwhash_argon2id_str`, `crypto_pwhash_argon2id_str_needs_rehash`, `crypto_pwhash_argon2id_str_verify`, `crypto_pwhash_argon2id_STRBYTES`, `crypto_pwhash_argon2id_strprefix`, `crypto_pwhash_memlimit_interactive`, `crypto_pwhash_memlimit_max`, `crypto_pwhash_memlimit_min`, `crypto_pwhash_memlimit_moderate`, `crypto_pwhash_memlimit_sensitive`, `crypto_pwhash_opslimit_interactive`, `crypto_pwhash_opslimit_max`, `crypto_pwhash_opslimit_min`, `crypto_pwhash_opslimit_moderate`, `crypto_pwhash_opslimit_sensitive`, `crypto_pwhash_passwd_max`, `crypto_pwhash_passwd_min`, `crypto_pwhash_primitive`, `crypto_pwhash_scryptsalsa208sha256_ll`, `crypto_pwhash_scryptsalsa208sha256_memlimit_interactive`, `crypto_pwhash_scryptsalsa208sha256_memlimit_max`, `crypto_pwhash_scryptsalsa208sha256_memlimit_min`, `crypto_pwhash_scryptsalsa208sha256_memlimit_sensitive`, `crypto_pwhash_scryptsalsa208sha256_opslimit_interactive`, `crypto_pwhash_scryptsalsa208sha256_opslimit_max`, `crypto_pwhash_scryptsalsa208sha256_opslimit_min`, `crypto_pwhash_scryptsalsa208sha256_opslimit_sensitive`, `crypto_pwhash_scryptsalsa208sha256_passwd_max`, `crypto_pwhash_scryptsalsa208sha256_passwd_min`, `crypto_pwhash_scryptsalsa208sha256_strprefix`, `crypto_pwhash_str_alg`, `crypto_pwhash_strprefix`, `crypto_scalarmult_curve25519`, `crypto_scalarmult_curve25519_base`, `crypto_scalarmult_curve25519_BYTES`, `crypto_scalarmult_curve25519_SCALARBYTES`, `crypto_scalarmult_primitive`, `crypto_scalarmult_ristretto255`, `crypto_scalarmult_ristretto255_base`, `crypto_scalarmult_ristretto255_BYTES`, `crypto_scalarmult_ristretto255_SCALARBYTES`, `crypto_secretbox_keygen`, `crypto_secretbox_MESSAGEBYTES_MAX`, `crypto_secretbox_primitive`, `crypto_secretbox_xchacha20poly1305_detached`, `crypto_secretbox_xchacha20poly1305_easy`, `crypto_secretbox_xchacha20poly1305_KEYBYTES`, `crypto_secretbox_xchacha20poly1305_MACBYTES`, `crypto_secretbox_xchacha20poly1305_MESSAGEBYTES_MAX`, `crypto_secretbox_xchacha20poly1305_NONCEBYTES`, `crypto_secretbox_xchacha20poly1305_open_detached`, `crypto_secretbox_xchacha20poly1305_open_easy`, `crypto_secretbox_xsalsa20poly1305`, `crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES`, `crypto_secretbox_xsalsa20poly1305_KEYBYTES`, `crypto_secretbox_xsalsa20poly1305_keygen`, `crypto_secretbox_xsalsa20poly1305_MACBYTES`, `crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX`, `crypto_secretbox_xsalsa20poly1305_NONCEBYTES`, `crypto_secretbox_xsalsa20poly1305_open`, `crypto_secretbox_xsalsa20poly1305_ZEROBYTES`, `crypto_secretstream_xchacha20poly1305_tag_final`, `crypto_secretstream_xchacha20poly1305_tag_message`, `crypto_secretstream_xchacha20poly1305_tag_push`, `crypto_secretstream_xchacha20poly1305_tag_rekey`, `crypto_shorthash_keygen`, `crypto_shorthash_primitive`, `crypto_shorthash_siphash24`, `crypto_shorthash_siphash24_BYTES`, `crypto_shorthash_siphash24_KEYBYTES`, `crypto_shorthash_siphashx24`, `crypto_shorthash_siphashx24_BYTES`, `crypto_shorthash_siphashx24_KEYBYTES`, `crypto_sign_ed25519`, `crypto_sign_ed25519_BYTES`, `crypto_sign_ed25519_detached`, `crypto_sign_ed25519_keypair`, `crypto_sign_ed25519_MESSAGEBYTES_MAX`, `crypto_sign_ed25519_open`, `crypto_sign_ed25519_PUBLICKEYBYTES`, `crypto_sign_ed25519_SECRETKEYBYTES`, `crypto_sign_ed25519_seed_keypair`, `crypto_sign_ed25519_SEEDBYTES`, `crypto_sign_ed25519_sk_to_seed`, `crypto_sign_ed25519_verify_detached`, `crypto_sign_ed25519ph_final_create`, `crypto_sign_ed25519ph_final_verify`, `crypto_sign_ed25519ph_init`, `crypto_sign_ed25519ph_STATEBYTES`, `crypto_sign_ed25519ph_update`, `crypto_sign_edwards25519sha512batch`, `crypto_sign_edwards25519sha512batch_keypair`, `crypto_sign_edwards25519sha512batch_open`, `crypto_sign_final_create`, `crypto_sign_final_verify`, `crypto_sign_init`, `crypto_sign_MESSAGEBYTES_MAX`, `crypto_sign_primitive`, `crypto_sign_STATEBYTES`, `crypto_sign_update`, `crypto_stream_chacha20_ietf_keygen`, `crypto_stream_chacha20_keygen`, `crypto_stream_keygen`, `crypto_stream_MESSAGEBYTES_MAX`, `crypto_stream_primitive`, `crypto_stream_salsa20`, `crypto_stream_salsa2012`, `crypto_stream_salsa2012_KEYBYTES`, `crypto_stream_salsa2012_keygen`, `crypto_stream_salsa2012_MESSAGEBYTES_MAX`, `crypto_stream_salsa2012_NONCEBYTES`, `crypto_stream_salsa2012_xor`, `crypto_stream_salsa208`, `crypto_stream_salsa208_KEYBYTES`, `crypto_stream_salsa208_keygen`, `crypto_stream_salsa208_MESSAGEBYTES_MAX`, `crypto_stream_salsa208_NONCEBYTES`, `crypto_stream_salsa208_xor`, `crypto_stream_salsa20_keygen`, `crypto_stream_xchacha20_keygen`, `crypto_stream_xsalsa20`, `crypto_stream_xsalsa20_KEYBYTES`, `crypto_stream_xsalsa20_keygen`, `crypto_stream_xsalsa20_MESSAGEBYTES_MAX`, `crypto_stream_xsalsa20_NONCEBYTES`, `crypto_stream_xsalsa20_xor`, `crypto_stream_xsalsa20_xor_ic`, `crypto_verify_16_BYTES`, `crypto_verify_32_BYTES`, `crypto_verify_64`, `crypto_verify_64_BYTES`, `randombytes`, `randombytes_close`, `randombytes_implementation_name`, `randombytes_internal_implementation`, `randombytes_set_implementation`, `randombytes_stir`, `randombytes_sysrandom_implementation`, `sodium_allocarray`, `sodium_base642bin`, `sodium_base64_encoded_len`, `sodium_bin2base64`, `sodium_bin2hex`, `sodium_hex2bin`, `sodium_init`, `sodium_library_minimal`, `sodium_library_version_major`, `sodium_library_version_minor`, `sodium_misuse`, `sodium_runtime_has_aesni`, `sodium_runtime_has_avx`, `sodium_runtime_has_avx2`, `sodium_runtime_has_avx512f`, `sodium_runtime_has_neon`, `sodium_runtime_has_pclmul`, `sodium_runtime_has_rdrand`, `sodium_runtime_has_sse2`, `sodium_runtime_has_sse3`, `sodium_runtime_has_sse41`, `sodium_runtime_has_ssse3`, `sodium_set_misuse_handler`, `sodium_stackzero`, `sodium_version_string` | |
| 285 | ||
| 286 | ## License | |
| 287 | ||
| 288 | [MIT](LICENSE.md) |
| … | … | @@ -0,0 +1,33 @@ |
| 1 | #!/usr/bin/env node | |
| 2 | ||
| 3 | const fs = require('fs') | |
| 4 | const path = require('path') | |
| 5 | const root = path.dirname(require.resolve('sodium-javascript/package.json')) | |
| 6 | ||
| 7 | const tmp = "module.exports = require('sodium-native')\n" | |
| 8 | ||
| 9 | function recurse (dir) { | |
| 10 | const ls = fs.readdirSync(dir) | |
| 11 | const subdir = path.relative(root, dir) | |
| 12 | if (subdir) fs.mkdirSync(subdir, { recursive: true }) | |
| 13 | ||
| 14 | for (const file of ls) { | |
| 15 | if (file === 'internal') recurse(path.join(dir, file)) | |
| 16 | if (!/\.js$/i.test(file)) continue | |
| 17 | if (file === 'example.js') continue | |
| 18 | if (file === 'test.js') continue | |
| 19 | ||
| 20 | fs.writeFileSync(path.join(__dirname, '..', subdir, file), tmp) | |
| 21 | } | |
| 22 | } | |
| 23 | ||
| 24 | recurse(root) | |
| 25 | ||
| 26 | const pkg = require('sodium-javascript/package.json') | |
| 27 | const myPkg = require('../package.json') | |
| 28 | ||
| 29 | for (const key of Object.keys(pkg.dependencies)) { | |
| 30 | myPkg.dependencies[key] = pkg.dependencies[key] | |
| 31 | } | |
| 32 | ||
| 33 | fs.writeFileSync(path.join(__dirname, '../package.json'), JSON.stringify(myPkg, null, 2) + '\n') |
| … | … | @@ -0,0 +1,32 @@ |
| 1 | var exec = require('child_process').execSync | |
| 2 | var path = require('path') | |
| 3 | ||
| 4 | var symbols = exec(`nm -gU "${path.join(require.resolve('sodium-native'), '../prebuilds/darwin-x64/libsodium.dylib')}"`).toString().trim().split('\n') | |
| 5 | var native = new Set(Object.keys(require('sodium-native')).sort()) | |
| 6 | var js = new Set(Object.keys(require('sodium-javascript')).sort()) | |
| 7 | ||
| 8 | symbols = symbols.map(l => l.split(' ').pop().replace(/^_/, '').replace(/_[^_]*bytes.*/, (s) => s.toUpperCase())) | |
| 9 | ||
| 10 | var missing = [] | |
| 11 | ||
| 12 | console.log(` | |
| 13 | <!-- Generated by build-scripts/symbols.js with | |
| 14 | sodium-native@${require('sodium-native/package.json').version} | |
| 15 | sodium-javascript@${require('sodium-javascript/package.json').version} -->\ | |
| 16 | `) | |
| 17 | ||
| 18 | const max = symbols.filter(s => native.has(s) || js.has(s)).map(s => s.length).reduce((a, b) => Math.max(a, b)) | |
| 19 | const lines = ':-' + ''.padEnd(max, '--') + '-:' | |
| 20 | const center = s => s.padStart(Math.floor((max + s.length) / 2), ' ').padEnd(max, ' ') | |
| 21 | ||
| 22 | console.log(`| ${center('C Library Symbol')} | \`sodium-native\` | \`sodium-javascript\` |`) | |
| 23 | console.log(`|${lines}|:--------------------:|:--------------------:|`) | |
| 24 | ||
| 25 | for (var i = 0; i < symbols.length; i++) { | |
| 26 | var s = symbols[i] | |
| 27 | if (!(native.has(s) || js.has(s))) missing.push(s) | |
| 28 | else console.log(`| \`${(s + '`').padEnd(max + 1, ' ')} | ${native.has(s) ? ' :white_check_mark: ' : ':small_red_triangle:'} | ${js.has(s) ? ' :white_check_mark: ' : ':small_red_triangle:'} |`) | |
| 29 | } | |
| 30 | ||
| 31 | console.log('\n### Missing altogether (Ctrl + F friendly)\n') | |
| 32 | console.log(missing.map(s => '`' + s + '`').join(', ')) |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | const fs = require('fs') | |
| 2 | const path = require('path') | |
| 3 | const { Transform, PassThrough } = require('stream') | |
| 4 | ||
| 5 | module.exports = function (file, opts) { | |
| 6 | const basedir = path.resolve(__dirname, '..') | |
| 7 | const relname = path.relative(basedir, file) | |
| 8 | ||
| 9 | if (opts._flags.browserField === false) { | |
| 10 | return new PassThrough() | |
| 11 | } | |
| 12 | ||
| 13 | return new Transform({ | |
| 14 | transform (data, enc, cb) { | |
| 15 | cb() | |
| 16 | }, | |
| 17 | flush (cb) { | |
| 18 | const filename = require.resolve('sodium-javascript/' + relname) | |
| 19 | fs.readFile(filename, (err, buf) => { | |
| 20 | if (err) return cb(err) | |
| 21 | this.push(buf) | |
| 22 | cb(null) | |
| 23 | }) | |
| 24 | } | |
| 25 | }) | |
| 26 | } |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1,49 @@ |
| 1 | { | |
| 2 | "name": "sodium-universal", | |
| 3 | "version": "5.0.1", | |
| 4 | "description": "Universal wrapper for sodium-javascript and sodium-native working in Node.js and the Browser", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "sodium-native": "^5.0.1" | |
| 8 | }, | |
| 9 | "peerDependencies": { | |
| 10 | "sodium-javascript": "~0.8.0" | |
| 11 | }, | |
| 12 | "peerDependenciesMeta": { | |
| 13 | "sodium-javascript": { | |
| 14 | "optional": true | |
| 15 | } | |
| 16 | }, | |
| 17 | "scripts": { | |
| 18 | "prepublish": "./build-scripts/generate.js" | |
| 19 | }, | |
| 20 | "browser": { | |
| 21 | "sodium-native": "sodium-javascript" | |
| 22 | }, | |
| 23 | "browserify": { | |
| 24 | "transform": [ | |
| 25 | "./build-scripts/transform.js" | |
| 26 | ] | |
| 27 | }, | |
| 28 | "repository": { | |
| 29 | "type": "git", | |
| 30 | "url": "git+https://github.com/holepunchto/sodium-universal.git" | |
| 31 | }, | |
| 32 | "keywords": [ | |
| 33 | "libsodium", | |
| 34 | "sodium", | |
| 35 | "sodium-native", | |
| 36 | "sodium-javascript", | |
| 37 | "browserify" | |
| 38 | ], | |
| 39 | "contributors": [ | |
| 40 | "Emil Bay <github@tixz.dk> (http://bayes.dk)", | |
| 41 | "Mathias Buus <mathiasbuus@gmail.com> (https://mafinto.sh)", | |
| 42 | "Christophe Diederichs <chm-diederichs@hyperdivision.dk>" | |
| 43 | ], | |
| 44 | "license": "MIT", | |
| 45 | "bugs": { | |
| 46 | "url": "https://github.com/holepunchto/sodium-universal/issues" | |
| 47 | }, | |
| 48 | "homepage": "https://github.com/holepunchto/sodium-universal#readme" | |
| 49 | } |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | { | |
| 2 | "name": "sodium-secretstream", | |
| 3 | "version": "1.2.0", | |
| 4 | "description": "Wraps libsodiums secretstream in a higher level abstraction", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "b4a": "^1.1.1", | |
| 8 | "sodium-universal": "^5.0.0" | |
| 9 | }, | |
| 10 | "devDependencies": { | |
| 11 | "standard": "^17.0.0" | |
| 12 | }, | |
| 13 | "scripts": { | |
| 14 | "test": "standard" | |
| 15 | }, | |
| 16 | "repository": { | |
| 17 | "type": "git", | |
| 18 | "url": "https://github.com/mafintosh/sodium-secretstream.git" | |
| 19 | }, | |
| 20 | "author": "Mathias Buus (@mafintosh)", | |
| 21 | "license": "MIT", | |
| 22 | "bugs": { | |
| 23 | "url": "https://github.com/mafintosh/sodium-secretstream/issues" | |
| 24 | }, | |
| 25 | "homepage": "https://github.com/mafintosh/sodium-secretstream" | |
| 26 | } |
| … | … | @@ -0,0 +1,7 @@ |
| 1 | Copyright 2017 Mathias Buus & Emil Bay | |
| 2 | ||
| 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
| 4 | ||
| 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
| 6 | ||
| 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| … | … | @@ -0,0 +1,288 @@ |
| 1 | # sodium-universal | |
| 2 | ||
| 3 | Universal wrapper for `sodium-javascript` and `sodium-native` working in Node.js and the Browser | |
| 4 | ||
| 5 | ```sh | |
| 6 | npm install sodium-universal | |
| 7 | ``` | |
| 8 | ||
| 9 | `sodium-native` ships with prebuilds for macOS, Windows, and Linux. | |
| 10 | ||
| 11 | Note that the module itself is generated on a prepublish hook. | |
| 12 | ||
| 13 | ## Usage | |
| 14 | ||
| 15 | ```js | |
| 16 | var sodium = require('sodium-universal') | |
| 17 | ||
| 18 | var rnd = Buffer.allocUnsafe(12) // Cryptographically random data | |
| 19 | sodium.randombytes_buf(rnd) | |
| 20 | ||
| 21 | console.log(rnd.toString('hex')) | |
| 22 | ||
| 23 | ``` | |
| 24 | ||
| 25 | Works seamlessly with Node.js: | |
| 26 | ||
| 27 | ```sh | |
| 28 | $ node example.js | |
| 29 | c7dbd46a6cc84ff2e0d1285c | |
| 30 | ``` | |
| 31 | ||
| 32 | And the browser: | |
| 33 | ||
| 34 | First install the peer dep, `sodium-javascript`. | |
| 35 | ||
| 36 | ```sh | |
| 37 | npm install --save sodium-javascript | |
| 38 | ``` | |
| 39 | ||
| 40 | And then browserify | |
| 41 | ||
| 42 | ```sh | |
| 43 | browserify example.js > bundle.js | |
| 44 | ``` | |
| 45 | ||
| 46 | ## Introduction | |
| 47 | ||
| 48 | [`libsodium`](https://download.libsodium.org/doc/) is a collection of cryptographic primitives, providing a low-level foundation to build higher-level cryptographic applications and protocols. It is often put in contrast to RSA based cryptography and OpenSSL, even though they all share overlapping algorithms and target slightly different audiences. Sodium is a collection of modern collection of primitives, fulfilling the same cryptographic tasks, but based on simpler and more efficient algorithms. | |
| 49 | ||
| 50 | This library provides seamless bindings to [`sodium-native`](https://github.com/sodium-friends/sodium-native), which is the original C implementation of `libsodium` exposed as a Node native module. For the browser we expose [`sodium-javascript`](https://github.com/sodium-friends/sodium-javascript), using the `package.json` [`browser`](https://github.com/defunctzombie/package-browser-field-spec) field, which is supported by most bundlers. | |
| 51 | ||
| 52 | ## API | |
| 53 | ||
| 54 | Please refer to [`sodium-native`](https://github.com/sodium-friends/sodium-native#api) and [`sodium-javascript`](https://github.com/sodium-friends/sodium-javascript#api). Note that the two modules do not yet have feature parity, where `sodium-native` is the more featureful of the two. | |
| 55 | ||
| 56 | ## Compatibilty | |
| 57 | ||
| 58 | <!-- Generated by build-scripts/symbols.js with | |
| 59 | sodium-native@3.2.0 | |
| 60 | sodium-javascript@0.6.0 --> | |
| 61 | | C Library Symbol | `sodium-native` | `sodium-javascript` | | |
| 62 | |:--------------------------------------------------------:|:--------------------:|:--------------------:| | |
| 63 | | `crypto_aead_chacha20poly1305_ietf_ABYTES` | :white_check_mark: | :white_check_mark: | | |
| 64 | | `crypto_aead_chacha20poly1305_ietf_decrypt` | :white_check_mark: | :white_check_mark: | | |
| 65 | | `crypto_aead_chacha20poly1305_ietf_decrypt_detached` | :white_check_mark: | :white_check_mark: | | |
| 66 | | `crypto_aead_chacha20poly1305_ietf_encrypt` | :white_check_mark: | :white_check_mark: | | |
| 67 | | `crypto_aead_chacha20poly1305_ietf_encrypt_detached` | :white_check_mark: | :white_check_mark: | | |
| 68 | | `crypto_aead_chacha20poly1305_ietf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 69 | | `crypto_aead_chacha20poly1305_ietf_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 70 | | `crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 71 | | `crypto_aead_chacha20poly1305_ietf_NPUBBYTES` | :white_check_mark: | :white_check_mark: | | |
| 72 | | `crypto_aead_chacha20poly1305_ietf_NSECBYTES` | :white_check_mark: | :white_check_mark: | | |
| 73 | | `crypto_aead_xchacha20poly1305_ietf_ABYTES` | :white_check_mark: | :small_red_triangle: | | |
| 74 | | `crypto_aead_xchacha20poly1305_ietf_decrypt` | :white_check_mark: | :small_red_triangle: | | |
| 75 | | `crypto_aead_xchacha20poly1305_ietf_decrypt_detached` | :white_check_mark: | :small_red_triangle: | | |
| 76 | | `crypto_aead_xchacha20poly1305_ietf_encrypt` | :white_check_mark: | :small_red_triangle: | | |
| 77 | | `crypto_aead_xchacha20poly1305_ietf_encrypt_detached` | :white_check_mark: | :small_red_triangle: | | |
| 78 | | `crypto_aead_xchacha20poly1305_ietf_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 79 | | `crypto_aead_xchacha20poly1305_ietf_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 80 | | `crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 81 | | `crypto_aead_xchacha20poly1305_ietf_NPUBBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 82 | | `crypto_aead_xchacha20poly1305_ietf_NSECBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 83 | | `crypto_auth` | :white_check_mark: | :small_red_triangle: | | |
| 84 | | `crypto_auth_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 85 | | `crypto_auth_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 86 | | `crypto_auth_verify` | :white_check_mark: | :small_red_triangle: | | |
| 87 | | `crypto_box_BEFORENMBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 88 | | `crypto_box_BOXZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 89 | | `crypto_box_detached` | :white_check_mark: | :small_red_triangle: | | |
| 90 | | `crypto_box_easy` | :white_check_mark: | :small_red_triangle: | | |
| 91 | | `crypto_box_keypair` | :white_check_mark: | :white_check_mark: | | |
| 92 | | `crypto_box_MACBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 93 | | `crypto_box_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 94 | | `crypto_box_open_detached` | :white_check_mark: | :small_red_triangle: | | |
| 95 | | `crypto_box_open_easy` | :white_check_mark: | :small_red_triangle: | | |
| 96 | | `crypto_box_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 97 | | `crypto_box_seal` | :white_check_mark: | :white_check_mark: | | |
| 98 | | `crypto_box_seal_open` | :white_check_mark: | :white_check_mark: | | |
| 99 | | `crypto_box_SEALBYTES` | :white_check_mark: | :white_check_mark: | | |
| 100 | | `crypto_box_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 101 | | `crypto_box_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 102 | | `crypto_box_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 103 | | `crypto_box_ZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 104 | | `crypto_core_ed25519_add` | :white_check_mark: | :small_red_triangle: | | |
| 105 | | `crypto_core_ed25519_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 106 | | `crypto_core_ed25519_from_uniform` | :white_check_mark: | :small_red_triangle: | | |
| 107 | | `crypto_core_ed25519_is_valid_point` | :white_check_mark: | :small_red_triangle: | | |
| 108 | | `crypto_core_ed25519_NONREDUCEDSCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 109 | | `crypto_core_ed25519_scalar_add` | :white_check_mark: | :small_red_triangle: | | |
| 110 | | `crypto_core_ed25519_scalar_complement` | :white_check_mark: | :small_red_triangle: | | |
| 111 | | `crypto_core_ed25519_scalar_invert` | :white_check_mark: | :small_red_triangle: | | |
| 112 | | `crypto_core_ed25519_scalar_negate` | :white_check_mark: | :small_red_triangle: | | |
| 113 | | `crypto_core_ed25519_scalar_random` | :white_check_mark: | :small_red_triangle: | | |
| 114 | | `crypto_core_ed25519_scalar_reduce` | :white_check_mark: | :small_red_triangle: | | |
| 115 | | `crypto_core_ed25519_scalar_sub` | :white_check_mark: | :small_red_triangle: | | |
| 116 | | `crypto_core_ed25519_SCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 117 | | `crypto_core_ed25519_sub` | :white_check_mark: | :small_red_triangle: | | |
| 118 | | `crypto_core_ed25519_UNIFORMBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 119 | | `crypto_generichash` | :white_check_mark: | :white_check_mark: | | |
| 120 | | `crypto_generichash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 121 | | `crypto_generichash_BYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 122 | | `crypto_generichash_BYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 123 | | `crypto_generichash_final` | :white_check_mark: | :small_red_triangle: | | |
| 124 | | `crypto_generichash_init` | :white_check_mark: | :small_red_triangle: | | |
| 125 | | `crypto_generichash_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 126 | | `crypto_generichash_KEYBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 127 | | `crypto_generichash_KEYBYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 128 | | `crypto_generichash_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 129 | | `crypto_generichash_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 130 | | `crypto_generichash_update` | :white_check_mark: | :small_red_triangle: | | |
| 131 | | `crypto_hash` | :white_check_mark: | :white_check_mark: | | |
| 132 | | `crypto_hash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 133 | | `crypto_hash_sha256` | :white_check_mark: | :white_check_mark: | | |
| 134 | | `crypto_hash_sha256_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 135 | | `crypto_hash_sha256_final` | :white_check_mark: | :small_red_triangle: | | |
| 136 | | `crypto_hash_sha256_init` | :white_check_mark: | :small_red_triangle: | | |
| 137 | | `crypto_hash_sha256_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 138 | | `crypto_hash_sha256_update` | :white_check_mark: | :small_red_triangle: | | |
| 139 | | `crypto_hash_sha512` | :white_check_mark: | :white_check_mark: | | |
| 140 | | `crypto_hash_sha512_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 141 | | `crypto_hash_sha512_final` | :white_check_mark: | :small_red_triangle: | | |
| 142 | | `crypto_hash_sha512_init` | :white_check_mark: | :small_red_triangle: | | |
| 143 | | `crypto_hash_sha512_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 144 | | `crypto_hash_sha512_update` | :white_check_mark: | :small_red_triangle: | | |
| 145 | | `crypto_kdf_BYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 146 | | `crypto_kdf_BYTES_MIN` | :white_check_mark: | :white_check_mark: | | |
| 147 | | `crypto_kdf_CONTEXTBYTES` | :white_check_mark: | :white_check_mark: | | |
| 148 | | `crypto_kdf_derive_from_key` | :white_check_mark: | :white_check_mark: | | |
| 149 | | `crypto_kdf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 150 | | `crypto_kdf_keygen` | :white_check_mark: | :white_check_mark: | | |
| 151 | | `crypto_kx_client_session_keys` | :white_check_mark: | :small_red_triangle: | | |
| 152 | | `crypto_kx_keypair` | :white_check_mark: | :white_check_mark: | | |
| 153 | | `crypto_kx_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 154 | | `crypto_kx_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 155 | | `crypto_kx_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 156 | | `crypto_kx_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 157 | | `crypto_kx_server_session_keys` | :white_check_mark: | :small_red_triangle: | | |
| 158 | | `crypto_kx_SESSIONKEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 159 | | `crypto_onetimeauth` | :white_check_mark: | :white_check_mark: | | |
| 160 | | `crypto_onetimeauth_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 161 | | `crypto_onetimeauth_final` | :white_check_mark: | :small_red_triangle: | | |
| 162 | | `crypto_onetimeauth_init` | :white_check_mark: | :small_red_triangle: | | |
| 163 | | `crypto_onetimeauth_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 164 | | `crypto_onetimeauth_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 165 | | `crypto_onetimeauth_update` | :white_check_mark: | :small_red_triangle: | | |
| 166 | | `crypto_onetimeauth_verify` | :white_check_mark: | :white_check_mark: | | |
| 167 | | `crypto_pwhash` | :white_check_mark: | :small_red_triangle: | | |
| 168 | | `crypto_pwhash_BYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 169 | | `crypto_pwhash_BYTES_MIN` | :white_check_mark: | :small_red_triangle: | | |
| 170 | | `crypto_pwhash_SALTBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 171 | | `crypto_pwhash_scryptsalsa208sha256` | :white_check_mark: | :small_red_triangle: | | |
| 172 | | `crypto_pwhash_scryptsalsa208sha256_BYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 173 | | `crypto_pwhash_scryptsalsa208sha256_BYTES_MIN` | :white_check_mark: | :small_red_triangle: | | |
| 174 | | `crypto_pwhash_scryptsalsa208sha256_SALTBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 175 | | `crypto_pwhash_scryptsalsa208sha256_str` | :white_check_mark: | :small_red_triangle: | | |
| 176 | | `crypto_pwhash_scryptsalsa208sha256_str_needs_rehash` | :white_check_mark: | :small_red_triangle: | | |
| 177 | | `crypto_pwhash_scryptsalsa208sha256_str_verify` | :white_check_mark: | :small_red_triangle: | | |
| 178 | | `crypto_pwhash_scryptsalsa208sha256_STRBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 179 | | `crypto_pwhash_str` | :white_check_mark: | :small_red_triangle: | | |
| 180 | | `crypto_pwhash_str_needs_rehash` | :white_check_mark: | :small_red_triangle: | | |
| 181 | | `crypto_pwhash_str_verify` | :white_check_mark: | :small_red_triangle: | | |
| 182 | | `crypto_pwhash_STRBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 183 | | `crypto_scalarmult` | :white_check_mark: | :white_check_mark: | | |
| 184 | | `crypto_scalarmult_base` | :white_check_mark: | :white_check_mark: | | |
| 185 | | `crypto_scalarmult_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 186 | | `crypto_scalarmult_ed25519` | :white_check_mark: | :small_red_triangle: | | |
| 187 | | `crypto_scalarmult_ed25519_base` | :white_check_mark: | :small_red_triangle: | | |
| 188 | | `crypto_scalarmult_ed25519_base_noclamp` | :white_check_mark: | :small_red_triangle: | | |
| 189 | | `crypto_scalarmult_ed25519_BYTES` | :white_check_mark: | :small_red_triangle: | | |
| 190 | | `crypto_scalarmult_ed25519_noclamp` | :white_check_mark: | :small_red_triangle: | | |
| 191 | | `crypto_scalarmult_ed25519_SCALARBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 192 | | `crypto_scalarmult_SCALARBYTES` | :white_check_mark: | :white_check_mark: | | |
| 193 | | `crypto_secretbox` | :small_red_triangle: | :white_check_mark: | | |
| 194 | | `crypto_secretbox_BOXZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 195 | | `crypto_secretbox_detached` | :white_check_mark: | :white_check_mark: | | |
| 196 | | `crypto_secretbox_easy` | :white_check_mark: | :white_check_mark: | | |
| 197 | | `crypto_secretbox_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 198 | | `crypto_secretbox_MACBYTES` | :white_check_mark: | :white_check_mark: | | |
| 199 | | `crypto_secretbox_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 200 | | `crypto_secretbox_open` | :small_red_triangle: | :white_check_mark: | | |
| 201 | | `crypto_secretbox_open_detached` | :white_check_mark: | :white_check_mark: | | |
| 202 | | `crypto_secretbox_open_easy` | :white_check_mark: | :white_check_mark: | | |
| 203 | | `crypto_secretbox_ZEROBYTES` | :small_red_triangle: | :white_check_mark: | | |
| 204 | | `crypto_secretstream_xchacha20poly1305_ABYTES` | :white_check_mark: | :small_red_triangle: | | |
| 205 | | `crypto_secretstream_xchacha20poly1305_HEADERBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 206 | | `crypto_secretstream_xchacha20poly1305_init_pull` | :white_check_mark: | :small_red_triangle: | | |
| 207 | | `crypto_secretstream_xchacha20poly1305_init_push` | :white_check_mark: | :small_red_triangle: | | |
| 208 | | `crypto_secretstream_xchacha20poly1305_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 209 | | `crypto_secretstream_xchacha20poly1305_keygen` | :white_check_mark: | :small_red_triangle: | | |
| 210 | | `crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 211 | | `crypto_secretstream_xchacha20poly1305_pull` | :white_check_mark: | :small_red_triangle: | | |
| 212 | | `crypto_secretstream_xchacha20poly1305_push` | :white_check_mark: | :small_red_triangle: | | |
| 213 | | `crypto_secretstream_xchacha20poly1305_rekey` | :white_check_mark: | :small_red_triangle: | | |
| 214 | | `crypto_secretstream_xchacha20poly1305_STATEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 215 | | `crypto_shorthash` | :white_check_mark: | :white_check_mark: | | |
| 216 | | `crypto_shorthash_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 217 | | `crypto_shorthash_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 218 | | `crypto_sign` | :white_check_mark: | :white_check_mark: | | |
| 219 | | `crypto_sign_BYTES` | :white_check_mark: | :white_check_mark: | | |
| 220 | | `crypto_sign_detached` | :white_check_mark: | :white_check_mark: | | |
| 221 | | `crypto_sign_ed25519_pk_to_curve25519` | :white_check_mark: | :small_red_triangle: | | |
| 222 | | `crypto_sign_ed25519_sk_to_curve25519` | :white_check_mark: | :small_red_triangle: | | |
| 223 | | `crypto_sign_ed25519_sk_to_pk` | :white_check_mark: | :small_red_triangle: | | |
| 224 | | `crypto_sign_keypair` | :white_check_mark: | :white_check_mark: | | |
| 225 | | `crypto_sign_open` | :white_check_mark: | :white_check_mark: | | |
| 226 | | `crypto_sign_PUBLICKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 227 | | `crypto_sign_SECRETKEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 228 | | `crypto_sign_seed_keypair` | :white_check_mark: | :white_check_mark: | | |
| 229 | | `crypto_sign_SEEDBYTES` | :white_check_mark: | :white_check_mark: | | |
| 230 | | `crypto_sign_verify_detached` | :white_check_mark: | :white_check_mark: | | |
| 231 | | `crypto_stream` | :white_check_mark: | :white_check_mark: | | |
| 232 | | `crypto_stream_chacha20` | :white_check_mark: | :white_check_mark: | | |
| 233 | | `crypto_stream_chacha20_ietf` | :white_check_mark: | :white_check_mark: | | |
| 234 | | `crypto_stream_chacha20_ietf_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 235 | | `crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 236 | | `crypto_stream_chacha20_ietf_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 237 | | `crypto_stream_chacha20_ietf_xor` | :white_check_mark: | :white_check_mark: | | |
| 238 | | `crypto_stream_chacha20_ietf_xor_ic` | :white_check_mark: | :white_check_mark: | | |
| 239 | | `crypto_stream_chacha20_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 240 | | `crypto_stream_chacha20_MESSAGEBYTES_MAX` | :white_check_mark: | :white_check_mark: | | |
| 241 | | `crypto_stream_chacha20_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 242 | | `crypto_stream_chacha20_xor` | :white_check_mark: | :white_check_mark: | | |
| 243 | | `crypto_stream_chacha20_xor_ic` | :white_check_mark: | :white_check_mark: | | |
| 244 | | `crypto_stream_KEYBYTES` | :white_check_mark: | :white_check_mark: | | |
| 245 | | `crypto_stream_NONCEBYTES` | :white_check_mark: | :white_check_mark: | | |
| 246 | | `crypto_stream_salsa20_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 247 | | `crypto_stream_salsa20_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 248 | | `crypto_stream_salsa20_NONCEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 249 | | `crypto_stream_salsa20_xor` | :white_check_mark: | :small_red_triangle: | | |
| 250 | | `crypto_stream_salsa20_xor_ic` | :white_check_mark: | :small_red_triangle: | | |
| 251 | | `crypto_stream_xchacha20` | :white_check_mark: | :small_red_triangle: | | |
| 252 | | `crypto_stream_xchacha20_KEYBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 253 | | `crypto_stream_xchacha20_MESSAGEBYTES_MAX` | :white_check_mark: | :small_red_triangle: | | |
| 254 | | `crypto_stream_xchacha20_NONCEBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 255 | | `crypto_stream_xchacha20_xor` | :white_check_mark: | :small_red_triangle: | | |
| 256 | | `crypto_stream_xchacha20_xor_ic` | :white_check_mark: | :small_red_triangle: | | |
| 257 | | `crypto_stream_xor` | :white_check_mark: | :white_check_mark: | | |
| 258 | | `crypto_verify_16` | :small_red_triangle: | :white_check_mark: | | |
| 259 | | `crypto_verify_32` | :small_red_triangle: | :white_check_mark: | | |
| 260 | | `randombytes_buf` | :white_check_mark: | :white_check_mark: | | |
| 261 | | `randombytes_buf_deterministic` | :white_check_mark: | :small_red_triangle: | | |
| 262 | | `randombytes_random` | :white_check_mark: | :small_red_triangle: | | |
| 263 | | `randombytes_SEEDBYTES` | :white_check_mark: | :small_red_triangle: | | |
| 264 | | `randombytes_uniform` | :white_check_mark: | :small_red_triangle: | | |
| 265 | | `sodium_add` | :white_check_mark: | :small_red_triangle: | | |
| 266 | | `sodium_compare` | :white_check_mark: | :small_red_triangle: | | |
| 267 | | `sodium_free` | :white_check_mark: | :small_red_triangle: | | |
| 268 | | `sodium_increment` | :white_check_mark: | :small_red_triangle: | | |
| 269 | | `sodium_is_zero` | :white_check_mark: | :white_check_mark: | | |
| 270 | | `sodium_malloc` | :white_check_mark: | :white_check_mark: | | |
| 271 | | `sodium_memcmp` | :white_check_mark: | :white_check_mark: | | |
| 272 | | `sodium_memzero` | :white_check_mark: | :white_check_mark: | | |
| 273 | | `sodium_mlock` | :white_check_mark: | :small_red_triangle: | | |
| 274 | | `sodium_mprotect_noaccess` | :white_check_mark: | :small_red_triangle: | | |
| 275 | | `sodium_mprotect_readonly` | :white_check_mark: | :small_red_triangle: | | |
| 276 | | `sodium_mprotect_readwrite` | :white_check_mark: | :small_red_triangle: | | |
| 277 | | `sodium_munlock` | :white_check_mark: | :small_red_triangle: | | |
| 278 | | `sodium_pad` | :white_check_mark: | :small_red_triangle: | | |
| 279 | | `sodium_sub` | :white_check_mark: | :small_red_triangle: | | |
| 280 | | `sodium_unpad` | :white_check_mark: | :small_red_triangle: | | |
| 281 | ||
| 282 | ### Missing altogether (Ctrl + F friendly) | |
| 283 | ||
| 284 | `crypto_aead_aes256gcm_ABYTES`, `crypto_aead_aes256gcm_beforenm`, `crypto_aead_aes256gcm_decrypt`, `crypto_aead_aes256gcm_decrypt_afternm`, `crypto_aead_aes256gcm_decrypt_detached`, `crypto_aead_aes256gcm_decrypt_detached_afternm`, `crypto_aead_aes256gcm_encrypt`, `crypto_aead_aes256gcm_encrypt_afternm`, `crypto_aead_aes256gcm_encrypt_detached`, `crypto_aead_aes256gcm_encrypt_detached_afternm`, `crypto_aead_aes256gcm_is_available`, `crypto_aead_aes256gcm_KEYBYTES`, `crypto_aead_aes256gcm_keygen`, `crypto_aead_aes256gcm_MESSAGEBYTES_MAX`, `crypto_aead_aes256gcm_NPUBBYTES`, `crypto_aead_aes256gcm_NSECBYTES`, `crypto_aead_aes256gcm_STATEBYTES`, `crypto_aead_chacha20poly1305_ABYTES`, `crypto_aead_chacha20poly1305_decrypt`, `crypto_aead_chacha20poly1305_decrypt_detached`, `crypto_aead_chacha20poly1305_encrypt`, `crypto_aead_chacha20poly1305_encrypt_detached`, `crypto_aead_chacha20poly1305_KEYBYTES`, `crypto_aead_chacha20poly1305_keygen`, `crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX`, `crypto_aead_chacha20poly1305_NPUBBYTES`, `crypto_aead_chacha20poly1305_NSECBYTES`, `crypto_auth_hmacsha256`, `crypto_auth_hmacsha256_BYTES`, `crypto_auth_hmacsha256_final`, `crypto_auth_hmacsha256_init`, `crypto_auth_hmacsha256_KEYBYTES`, `crypto_auth_hmacsha256_keygen`, `crypto_auth_hmacsha256_STATEBYTES`, `crypto_auth_hmacsha256_update`, `crypto_auth_hmacsha256_verify`, `crypto_auth_hmacsha512`, `crypto_auth_hmacsha512256`, `crypto_auth_hmacsha512256_BYTES`, `crypto_auth_hmacsha512256_final`, `crypto_auth_hmacsha512256_init`, `crypto_auth_hmacsha512256_KEYBYTES`, `crypto_auth_hmacsha512256_keygen`, `crypto_auth_hmacsha512256_STATEBYTES`, `crypto_auth_hmacsha512256_update`, `crypto_auth_hmacsha512256_verify`, `crypto_auth_hmacsha512_BYTES`, `crypto_auth_hmacsha512_final`, `crypto_auth_hmacsha512_init`, `crypto_auth_hmacsha512_KEYBYTES`, `crypto_auth_hmacsha512_keygen`, `crypto_auth_hmacsha512_STATEBYTES`, `crypto_auth_hmacsha512_update`, `crypto_auth_hmacsha512_verify`, `crypto_auth_keygen`, `crypto_auth_primitive`, `crypto_box`, `crypto_box_afternm`, `crypto_box_beforenm`, `crypto_box_curve25519xchacha20poly1305_beforenm`, `crypto_box_curve25519xchacha20poly1305_BEFORENMBYTES`, `crypto_box_curve25519xchacha20poly1305_detached`, `crypto_box_curve25519xchacha20poly1305_detached_afternm`, `crypto_box_curve25519xchacha20poly1305_easy`, `crypto_box_curve25519xchacha20poly1305_easy_afternm`, `crypto_box_curve25519xchacha20poly1305_keypair`, `crypto_box_curve25519xchacha20poly1305_MACBYTES`, `crypto_box_curve25519xchacha20poly1305_MESSAGEBYTES_MAX`, `crypto_box_curve25519xchacha20poly1305_NONCEBYTES`, `crypto_box_curve25519xchacha20poly1305_open_detached`, `crypto_box_curve25519xchacha20poly1305_open_detached_afternm`, `crypto_box_curve25519xchacha20poly1305_open_easy`, `crypto_box_curve25519xchacha20poly1305_open_easy_afternm`, `crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES`, `crypto_box_curve25519xchacha20poly1305_seal`, `crypto_box_curve25519xchacha20poly1305_seal_open`, `crypto_box_curve25519xchacha20poly1305_SEALBYTES`, `crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES`, `crypto_box_curve25519xchacha20poly1305_seed_keypair`, `crypto_box_curve25519xchacha20poly1305_SEEDBYTES`, `crypto_box_curve25519xsalsa20poly1305`, `crypto_box_curve25519xsalsa20poly1305_afternm`, `crypto_box_curve25519xsalsa20poly1305_beforenm`, `crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES`, `crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES`, `crypto_box_curve25519xsalsa20poly1305_keypair`, `crypto_box_curve25519xsalsa20poly1305_MACBYTES`, `crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX`, `crypto_box_curve25519xsalsa20poly1305_NONCEBYTES`, `crypto_box_curve25519xsalsa20poly1305_open`, `crypto_box_curve25519xsalsa20poly1305_open_afternm`, `crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES`, `crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES`, `crypto_box_curve25519xsalsa20poly1305_seed_keypair`, `crypto_box_curve25519xsalsa20poly1305_SEEDBYTES`, `crypto_box_curve25519xsalsa20poly1305_ZEROBYTES`, `crypto_box_detached_afternm`, `crypto_box_easy_afternm`, `crypto_box_MESSAGEBYTES_MAX`, `crypto_box_open`, `crypto_box_open_afternm`, `crypto_box_open_detached_afternm`, `crypto_box_open_easy_afternm`, `crypto_box_primitive`, `crypto_core_ed25519_from_hash`, `crypto_core_ed25519_HASHBYTES`, `crypto_core_ed25519_random`, `crypto_core_ed25519_scalar_mul`, `crypto_core_hchacha20`, `crypto_core_hchacha20_CONSTBYTES`, `crypto_core_hchacha20_INPUTBYTES`, `crypto_core_hchacha20_KEYBYTES`, `crypto_core_hchacha20_OUTPUTBYTES`, `crypto_core_hsalsa20`, `crypto_core_hsalsa20_CONSTBYTES`, `crypto_core_hsalsa20_INPUTBYTES`, `crypto_core_hsalsa20_KEYBYTES`, `crypto_core_hsalsa20_OUTPUTBYTES`, `crypto_core_ristretto255_add`, `crypto_core_ristretto255_BYTES`, `crypto_core_ristretto255_from_hash`, `crypto_core_ristretto255_HASHBYTES`, `crypto_core_ristretto255_is_valid_point`, `crypto_core_ristretto255_NONREDUCEDSCALARBYTES`, `crypto_core_ristretto255_random`, `crypto_core_ristretto255_scalar_add`, `crypto_core_ristretto255_scalar_complement`, `crypto_core_ristretto255_scalar_invert`, `crypto_core_ristretto255_scalar_mul`, `crypto_core_ristretto255_scalar_negate`, `crypto_core_ristretto255_scalar_random`, `crypto_core_ristretto255_scalar_reduce`, `crypto_core_ristretto255_scalar_sub`, `crypto_core_ristretto255_SCALARBYTES`, `crypto_core_ristretto255_sub`, `crypto_core_salsa20`, `crypto_core_salsa2012`, `crypto_core_salsa2012_CONSTBYTES`, `crypto_core_salsa2012_INPUTBYTES`, `crypto_core_salsa2012_KEYBYTES`, `crypto_core_salsa2012_OUTPUTBYTES`, `crypto_core_salsa208`, `crypto_core_salsa208_CONSTBYTES`, `crypto_core_salsa208_INPUTBYTES`, `crypto_core_salsa208_KEYBYTES`, `crypto_core_salsa208_OUTPUTBYTES`, `crypto_core_salsa20_CONSTBYTES`, `crypto_core_salsa20_INPUTBYTES`, `crypto_core_salsa20_KEYBYTES`, `crypto_core_salsa20_OUTPUTBYTES`, `crypto_generichash_blake2b`, `crypto_generichash_blake2b_BYTES`, `crypto_generichash_blake2b_BYTES_MAX`, `crypto_generichash_blake2b_BYTES_MIN`, `crypto_generichash_blake2b_final`, `crypto_generichash_blake2b_init`, `crypto_generichash_blake2b_init_salt_personal`, `crypto_generichash_blake2b_KEYBYTES`, `crypto_generichash_blake2b_KEYBYTES_MAX`, `crypto_generichash_blake2b_KEYBYTES_MIN`, `crypto_generichash_blake2b_keygen`, `crypto_generichash_blake2b_PERSONALBYTES`, `crypto_generichash_blake2b_salt_personal`, `crypto_generichash_blake2b_SALTBYTES`, `crypto_generichash_blake2b_STATEBYTES`, `crypto_generichash_blake2b_update`, `crypto_generichash_primitive`, `crypto_hash_primitive`, `crypto_kdf_blake2b_BYTES_MAX`, `crypto_kdf_blake2b_BYTES_MIN`, `crypto_kdf_blake2b_CONTEXTBYTES`, `crypto_kdf_blake2b_derive_from_key`, `crypto_kdf_blake2b_KEYBYTES`, `crypto_kdf_primitive`, `crypto_kx_primitive`, `crypto_onetimeauth_keygen`, `crypto_onetimeauth_poly1305`, `crypto_onetimeauth_poly1305_BYTES`, `crypto_onetimeauth_poly1305_final`, `crypto_onetimeauth_poly1305_init`, `crypto_onetimeauth_poly1305_KEYBYTES`, `crypto_onetimeauth_poly1305_keygen`, `crypto_onetimeauth_poly1305_STATEBYTES`, `crypto_onetimeauth_poly1305_update`, `crypto_onetimeauth_poly1305_verify`, `crypto_onetimeauth_primitive`, `crypto_pwhash_alg_argon2i13`, `crypto_pwhash_alg_argon2id13`, `crypto_pwhash_alg_default`, `crypto_pwhash_argon2i`, `crypto_pwhash_argon2i_alg_argon2i13`, `crypto_pwhash_argon2i_BYTES_MAX`, `crypto_pwhash_argon2i_BYTES_MIN`, `crypto_pwhash_argon2i_memlimit_interactive`, `crypto_pwhash_argon2i_memlimit_max`, `crypto_pwhash_argon2i_memlimit_min`, `crypto_pwhash_argon2i_memlimit_moderate`, `crypto_pwhash_argon2i_memlimit_sensitive`, `crypto_pwhash_argon2i_opslimit_interactive`, `crypto_pwhash_argon2i_opslimit_max`, `crypto_pwhash_argon2i_opslimit_min`, `crypto_pwhash_argon2i_opslimit_moderate`, `crypto_pwhash_argon2i_opslimit_sensitive`, `crypto_pwhash_argon2i_passwd_max`, `crypto_pwhash_argon2i_passwd_min`, `crypto_pwhash_argon2i_SALTBYTES`, `crypto_pwhash_argon2i_str`, `crypto_pwhash_argon2i_str_needs_rehash`, `crypto_pwhash_argon2i_str_verify`, `crypto_pwhash_argon2i_STRBYTES`, `crypto_pwhash_argon2i_strprefix`, `crypto_pwhash_argon2id`, `crypto_pwhash_argon2id_alg_argon2id13`, `crypto_pwhash_argon2id_BYTES_MAX`, `crypto_pwhash_argon2id_BYTES_MIN`, `crypto_pwhash_argon2id_memlimit_interactive`, `crypto_pwhash_argon2id_memlimit_max`, `crypto_pwhash_argon2id_memlimit_min`, `crypto_pwhash_argon2id_memlimit_moderate`, `crypto_pwhash_argon2id_memlimit_sensitive`, `crypto_pwhash_argon2id_opslimit_interactive`, `crypto_pwhash_argon2id_opslimit_max`, `crypto_pwhash_argon2id_opslimit_min`, `crypto_pwhash_argon2id_opslimit_moderate`, `crypto_pwhash_argon2id_opslimit_sensitive`, `crypto_pwhash_argon2id_passwd_max`, `crypto_pwhash_argon2id_passwd_min`, `crypto_pwhash_argon2id_SALTBYTES`, `crypto_pwhash_argon2id_str`, `crypto_pwhash_argon2id_str_needs_rehash`, `crypto_pwhash_argon2id_str_verify`, `crypto_pwhash_argon2id_STRBYTES`, `crypto_pwhash_argon2id_strprefix`, `crypto_pwhash_memlimit_interactive`, `crypto_pwhash_memlimit_max`, `crypto_pwhash_memlimit_min`, `crypto_pwhash_memlimit_moderate`, `crypto_pwhash_memlimit_sensitive`, `crypto_pwhash_opslimit_interactive`, `crypto_pwhash_opslimit_max`, `crypto_pwhash_opslimit_min`, `crypto_pwhash_opslimit_moderate`, `crypto_pwhash_opslimit_sensitive`, `crypto_pwhash_passwd_max`, `crypto_pwhash_passwd_min`, `crypto_pwhash_primitive`, `crypto_pwhash_scryptsalsa208sha256_ll`, `crypto_pwhash_scryptsalsa208sha256_memlimit_interactive`, `crypto_pwhash_scryptsalsa208sha256_memlimit_max`, `crypto_pwhash_scryptsalsa208sha256_memlimit_min`, `crypto_pwhash_scryptsalsa208sha256_memlimit_sensitive`, `crypto_pwhash_scryptsalsa208sha256_opslimit_interactive`, `crypto_pwhash_scryptsalsa208sha256_opslimit_max`, `crypto_pwhash_scryptsalsa208sha256_opslimit_min`, `crypto_pwhash_scryptsalsa208sha256_opslimit_sensitive`, `crypto_pwhash_scryptsalsa208sha256_passwd_max`, `crypto_pwhash_scryptsalsa208sha256_passwd_min`, `crypto_pwhash_scryptsalsa208sha256_strprefix`, `crypto_pwhash_str_alg`, `crypto_pwhash_strprefix`, `crypto_scalarmult_curve25519`, `crypto_scalarmult_curve25519_base`, `crypto_scalarmult_curve25519_BYTES`, `crypto_scalarmult_curve25519_SCALARBYTES`, `crypto_scalarmult_primitive`, `crypto_scalarmult_ristretto255`, `crypto_scalarmult_ristretto255_base`, `crypto_scalarmult_ristretto255_BYTES`, `crypto_scalarmult_ristretto255_SCALARBYTES`, `crypto_secretbox_keygen`, `crypto_secretbox_MESSAGEBYTES_MAX`, `crypto_secretbox_primitive`, `crypto_secretbox_xchacha20poly1305_detached`, `crypto_secretbox_xchacha20poly1305_easy`, `crypto_secretbox_xchacha20poly1305_KEYBYTES`, `crypto_secretbox_xchacha20poly1305_MACBYTES`, `crypto_secretbox_xchacha20poly1305_MESSAGEBYTES_MAX`, `crypto_secretbox_xchacha20poly1305_NONCEBYTES`, `crypto_secretbox_xchacha20poly1305_open_detached`, `crypto_secretbox_xchacha20poly1305_open_easy`, `crypto_secretbox_xsalsa20poly1305`, `crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES`, `crypto_secretbox_xsalsa20poly1305_KEYBYTES`, `crypto_secretbox_xsalsa20poly1305_keygen`, `crypto_secretbox_xsalsa20poly1305_MACBYTES`, `crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX`, `crypto_secretbox_xsalsa20poly1305_NONCEBYTES`, `crypto_secretbox_xsalsa20poly1305_open`, `crypto_secretbox_xsalsa20poly1305_ZEROBYTES`, `crypto_secretstream_xchacha20poly1305_tag_final`, `crypto_secretstream_xchacha20poly1305_tag_message`, `crypto_secretstream_xchacha20poly1305_tag_push`, `crypto_secretstream_xchacha20poly1305_tag_rekey`, `crypto_shorthash_keygen`, `crypto_shorthash_primitive`, `crypto_shorthash_siphash24`, `crypto_shorthash_siphash24_BYTES`, `crypto_shorthash_siphash24_KEYBYTES`, `crypto_shorthash_siphashx24`, `crypto_shorthash_siphashx24_BYTES`, `crypto_shorthash_siphashx24_KEYBYTES`, `crypto_sign_ed25519`, `crypto_sign_ed25519_BYTES`, `crypto_sign_ed25519_detached`, `crypto_sign_ed25519_keypair`, `crypto_sign_ed25519_MESSAGEBYTES_MAX`, `crypto_sign_ed25519_open`, `crypto_sign_ed25519_PUBLICKEYBYTES`, `crypto_sign_ed25519_SECRETKEYBYTES`, `crypto_sign_ed25519_seed_keypair`, `crypto_sign_ed25519_SEEDBYTES`, `crypto_sign_ed25519_sk_to_seed`, `crypto_sign_ed25519_verify_detached`, `crypto_sign_ed25519ph_final_create`, `crypto_sign_ed25519ph_final_verify`, `crypto_sign_ed25519ph_init`, `crypto_sign_ed25519ph_STATEBYTES`, `crypto_sign_ed25519ph_update`, `crypto_sign_edwards25519sha512batch`, `crypto_sign_edwards25519sha512batch_keypair`, `crypto_sign_edwards25519sha512batch_open`, `crypto_sign_final_create`, `crypto_sign_final_verify`, `crypto_sign_init`, `crypto_sign_MESSAGEBYTES_MAX`, `crypto_sign_primitive`, `crypto_sign_STATEBYTES`, `crypto_sign_update`, `crypto_stream_chacha20_ietf_keygen`, `crypto_stream_chacha20_keygen`, `crypto_stream_keygen`, `crypto_stream_MESSAGEBYTES_MAX`, `crypto_stream_primitive`, `crypto_stream_salsa20`, `crypto_stream_salsa2012`, `crypto_stream_salsa2012_KEYBYTES`, `crypto_stream_salsa2012_keygen`, `crypto_stream_salsa2012_MESSAGEBYTES_MAX`, `crypto_stream_salsa2012_NONCEBYTES`, `crypto_stream_salsa2012_xor`, `crypto_stream_salsa208`, `crypto_stream_salsa208_KEYBYTES`, `crypto_stream_salsa208_keygen`, `crypto_stream_salsa208_MESSAGEBYTES_MAX`, `crypto_stream_salsa208_NONCEBYTES`, `crypto_stream_salsa208_xor`, `crypto_stream_salsa20_keygen`, `crypto_stream_xchacha20_keygen`, `crypto_stream_xsalsa20`, `crypto_stream_xsalsa20_KEYBYTES`, `crypto_stream_xsalsa20_keygen`, `crypto_stream_xsalsa20_MESSAGEBYTES_MAX`, `crypto_stream_xsalsa20_NONCEBYTES`, `crypto_stream_xsalsa20_xor`, `crypto_stream_xsalsa20_xor_ic`, `crypto_verify_16_BYTES`, `crypto_verify_32_BYTES`, `crypto_verify_64`, `crypto_verify_64_BYTES`, `randombytes`, `randombytes_close`, `randombytes_implementation_name`, `randombytes_internal_implementation`, `randombytes_set_implementation`, `randombytes_stir`, `randombytes_sysrandom_implementation`, `sodium_allocarray`, `sodium_base642bin`, `sodium_base64_encoded_len`, `sodium_bin2base64`, `sodium_bin2hex`, `sodium_hex2bin`, `sodium_init`, `sodium_library_minimal`, `sodium_library_version_major`, `sodium_library_version_minor`, `sodium_misuse`, `sodium_runtime_has_aesni`, `sodium_runtime_has_avx`, `sodium_runtime_has_avx2`, `sodium_runtime_has_avx512f`, `sodium_runtime_has_neon`, `sodium_runtime_has_pclmul`, `sodium_runtime_has_rdrand`, `sodium_runtime_has_sse2`, `sodium_runtime_has_sse3`, `sodium_runtime_has_sse41`, `sodium_runtime_has_ssse3`, `sodium_set_misuse_handler`, `sodium_stackzero`, `sodium_version_string` | |
| 285 | ||
| 286 | ## License | |
| 287 | ||
| 288 | [MIT](LICENSE.md) |
| … | … | @@ -0,0 +1,33 @@ |
| 1 | #!/usr/bin/env node | |
| 2 | ||
| 3 | const fs = require('fs') | |
| 4 | const path = require('path') | |
| 5 | const root = path.dirname(require.resolve('sodium-javascript/package.json')) | |
| 6 | ||
| 7 | const tmp = "module.exports = require('sodium-native')\n" | |
| 8 | ||
| 9 | function recurse (dir) { | |
| 10 | const ls = fs.readdirSync(dir) | |
| 11 | const subdir = path.relative(root, dir) | |
| 12 | if (subdir) fs.mkdirSync(subdir, { recursive: true }) | |
| 13 | ||
| 14 | for (const file of ls) { | |
| 15 | if (file === 'internal') recurse(path.join(dir, file)) | |
| 16 | if (!/\.js$/i.test(file)) continue | |
| 17 | if (file === 'example.js') continue | |
| 18 | if (file === 'test.js') continue | |
| 19 | ||
| 20 | fs.writeFileSync(path.join(__dirname, '..', subdir, file), tmp) | |
| 21 | } | |
| 22 | } | |
| 23 | ||
| 24 | recurse(root) | |
| 25 | ||
| 26 | const pkg = require('sodium-javascript/package.json') | |
| 27 | const myPkg = require('../package.json') | |
| 28 | ||
| 29 | for (const key of Object.keys(pkg.dependencies)) { | |
| 30 | myPkg.dependencies[key] = pkg.dependencies[key] | |
| 31 | } | |
| 32 | ||
| 33 | fs.writeFileSync(path.join(__dirname, '../package.json'), JSON.stringify(myPkg, null, 2) + '\n') |
| … | … | @@ -0,0 +1,32 @@ |
| 1 | var exec = require('child_process').execSync | |
| 2 | var path = require('path') | |
| 3 | ||
| 4 | var symbols = exec(`nm -gU "${path.join(require.resolve('sodium-native'), '../prebuilds/darwin-x64/libsodium.dylib')}"`).toString().trim().split('\n') | |
| 5 | var native = new Set(Object.keys(require('sodium-native')).sort()) | |
| 6 | var js = new Set(Object.keys(require('sodium-javascript')).sort()) | |
| 7 | ||
| 8 | symbols = symbols.map(l => l.split(' ').pop().replace(/^_/, '').replace(/_[^_]*bytes.*/, (s) => s.toUpperCase())) | |
| 9 | ||
| 10 | var missing = [] | |
| 11 | ||
| 12 | console.log(` | |
| 13 | <!-- Generated by build-scripts/symbols.js with | |
| 14 | sodium-native@${require('sodium-native/package.json').version} | |
| 15 | sodium-javascript@${require('sodium-javascript/package.json').version} -->\ | |
| 16 | `) | |
| 17 | ||
| 18 | const max = symbols.filter(s => native.has(s) || js.has(s)).map(s => s.length).reduce((a, b) => Math.max(a, b)) | |
| 19 | const lines = ':-' + ''.padEnd(max, '--') + '-:' | |
| 20 | const center = s => s.padStart(Math.floor((max + s.length) / 2), ' ').padEnd(max, ' ') | |
| 21 | ||
| 22 | console.log(`| ${center('C Library Symbol')} | \`sodium-native\` | \`sodium-javascript\` |`) | |
| 23 | console.log(`|${lines}|:--------------------:|:--------------------:|`) | |
| 24 | ||
| 25 | for (var i = 0; i < symbols.length; i++) { | |
| 26 | var s = symbols[i] | |
| 27 | if (!(native.has(s) || js.has(s))) missing.push(s) | |
| 28 | else console.log(`| \`${(s + '`').padEnd(max + 1, ' ')} | ${native.has(s) ? ' :white_check_mark: ' : ':small_red_triangle:'} | ${js.has(s) ? ' :white_check_mark: ' : ':small_red_triangle:'} |`) | |
| 29 | } | |
| 30 | ||
| 31 | console.log('\n### Missing altogether (Ctrl + F friendly)\n') | |
| 32 | console.log(missing.map(s => '`' + s + '`').join(', ')) |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | const fs = require('fs') | |
| 2 | const path = require('path') | |
| 3 | const { Transform, PassThrough } = require('stream') | |
| 4 | ||
| 5 | module.exports = function (file, opts) { | |
| 6 | const basedir = path.resolve(__dirname, '..') | |
| 7 | const relname = path.relative(basedir, file) | |
| 8 | ||
| 9 | if (opts._flags.browserField === false) { | |
| 10 | return new PassThrough() | |
| 11 | } | |
| 12 | ||
| 13 | return new Transform({ | |
| 14 | transform (data, enc, cb) { | |
| 15 | cb() | |
| 16 | }, | |
| 17 | flush (cb) { | |
| 18 | const filename = require.resolve('sodium-javascript/' + relname) | |
| 19 | fs.readFile(filename, (err, buf) => { | |
| 20 | if (err) return cb(err) | |
| 21 | this.push(buf) | |
| 22 | cb(null) | |
| 23 | }) | |
| 24 | } | |
| 25 | }) | |
| 26 | } |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1,45 @@ |
| 1 | { | |
| 2 | "name": "sodium-universal", | |
| 3 | "version": "4.0.1", | |
| 4 | "description": "Universal wrapper for sodium-javascript and sodium-native working in Node.js and the Browser", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "sodium-native": "^4.0.0" | |
| 8 | }, | |
| 9 | "peerDependencies": { | |
| 10 | "sodium-javascript": "~0.8.0" | |
| 11 | }, | |
| 12 | "peerDependenciesMeta": { | |
| 13 | "sodium-javascript": { | |
| 14 | "optional": true | |
| 15 | } | |
| 16 | }, | |
| 17 | "scripts": { | |
| 18 | "prepublish": "./build-scripts/generate.js" | |
| 19 | }, | |
| 20 | "browser": { | |
| 21 | "sodium-native": "sodium-javascript" | |
| 22 | }, | |
| 23 | "browserify": { | |
| 24 | "transform": [ | |
| 25 | "./build-scripts/transform.js" | |
| 26 | ] | |
| 27 | }, | |
| 28 | "repository": { | |
| 29 | "type": "git", | |
| 30 | "url": "git+https://github.com/sodium-friends/sodium-universal.git" | |
| 31 | }, | |
| 32 | "keywords": [ | |
| 33 | "libsodium", | |
| 34 | "sodium", | |
| 35 | "sodium-native", | |
| 36 | "sodium-javascript", | |
| 37 | "browserify" | |
| 38 | ], | |
| 39 | "author": "Emil Bay <github@tixz.dk>", | |
| 40 | "license": "MIT", | |
| 41 | "bugs": { | |
| 42 | "url": "https://github.com/sodium-friends/sodium-universal/issues" | |
| 43 | }, | |
| 44 | "homepage": "https://github.com/sodium-friends/sodium-universal#readme" | |
| 45 | } |
| … | … | @@ -0,0 +1 @@ |
| 1 | module.exports = require('sodium-native') |
| … | … | @@ -0,0 +1,20 @@ |
| 1 | Copyright 2013 Mathias Buus | |
| 2 | ||
| 3 | Permission is hereby granted, free of charge, to any person obtaining | |
| 4 | a 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 | |
| 8 | permit persons to whom the Software is furnished to do so, subject to | |
| 9 | the following conditions: | |
| 10 | ||
| 11 | The above copyright notice and this permission notice shall be | |
| 12 | included in all copies or substantial portions of the Software. | |
| 13 | ||
| 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
| 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |
| 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |
| 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
| 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| … | … | @@ -0,0 +1,38 @@ |
| 1 | # speedometer | |
| 2 | ||
| 3 | Speed measurement in Javascript | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install speedometer | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | var speedometer = require('speedometer') | |
| 13 | var fs = require('fs') | |
| 14 | ||
| 15 | // Let's measure how fast we can read from /dev/urandom | |
| 16 | var speed = speedometer() | |
| 17 | var stream = fs.createReadStream('/dev/urandom') | |
| 18 | ||
| 19 | stream.on('data', function(data) { | |
| 20 | // Simply call speed with the amount of bytes transferred | |
| 21 | var bytesPerSecond = speed(data.length) | |
| 22 | ||
| 23 | console.log(bytesPerSecond+' bytes/second') | |
| 24 | }) | |
| 25 | ``` | |
| 26 | ||
| 27 | You can always get the current speed by calling `speed()`. | |
| 28 | ||
| 29 | Per default `speedometer` uses a 5 second buffer. | |
| 30 | To change this simply pass another value to the constructor | |
| 31 | ||
| 32 | ``` js | |
| 33 | var speed = speedometer(20) // uses a 20s buffer instead | |
| 34 | ``` | |
| 35 | ||
| 36 | ## License | |
| 37 | ||
| 38 | MIT |
| … | … | @@ -0,0 +1,39 @@ |
| 1 | var tick = 1 | |
| 2 | var maxTick = 65535 | |
| 3 | var resolution = 4 | |
| 4 | var timer | |
| 5 | var inc = function () { | |
| 6 | tick = (tick + 1) & maxTick | |
| 7 | } | |
| 8 | ||
| 9 | ||
| 10 | module.exports = function (seconds) { | |
| 11 | if (!timer) { | |
| 12 | timer = setInterval(inc, (1000 / resolution) | 0) | |
| 13 | if (timer.unref) timer.unref() | |
| 14 | } | |
| 15 | ||
| 16 | var size = resolution * (seconds || 5) | |
| 17 | var buffer = [0] | |
| 18 | var pointer = 1 | |
| 19 | var last = (tick - 1) & maxTick | |
| 20 | ||
| 21 | return function (delta) { | |
| 22 | var dist = (tick - last) & maxTick | |
| 23 | if (dist > size) dist = size | |
| 24 | last = tick | |
| 25 | ||
| 26 | while (dist--) { | |
| 27 | if (pointer === size) pointer = 0 | |
| 28 | buffer[pointer] = buffer[pointer === 0 ? size - 1 : pointer - 1] | |
| 29 | pointer++ | |
| 30 | } | |
| 31 | ||
| 32 | if (delta) buffer[pointer - 1] += delta | |
| 33 | ||
| 34 | var top = buffer[pointer - 1] | |
| 35 | var btm = buffer.length < size ? 0 : buffer[pointer === size ? 0 : pointer] | |
| 36 | ||
| 37 | return buffer.length < resolution ? top : (top - btm) * resolution / buffer.length | |
| 38 | } | |
| 39 | } |
| … | … | @@ -0,0 +1,15 @@ |
| 1 | { | |
| 2 | "name": "speedometer", | |
| 3 | "version": "1.1.0", | |
| 4 | "repository": "git://github.com/mafintosh/speedometer", | |
| 5 | "description": "simple speed measurement in javascript", | |
| 6 | "license": "MIT", | |
| 7 | "keywords": [ | |
| 8 | "speed", | |
| 9 | "bytes", | |
| 10 | "per", | |
| 11 | "second", | |
| 12 | "transfer" | |
| 13 | ], | |
| 14 | "author": "Mathias Buus Madsen <mathiasbuus@gmail.com>" | |
| 15 | } |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2019 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,490 @@ |
| 1 | # streamx | |
| 2 | ||
| 3 | An iteration of the Node.js core streams with a series of improvements. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install streamx | |
| 7 | ``` | |
| 8 | ||
| 9 | [](https://github.com/streamxorg/streamx/actions?query=workflow%3A%22Build+Status%22) | |
| 10 | ||
| 11 | ## Main improvements from Node.js core stream | |
| 12 | ||
| 13 | #### Proper lifecycle support. | |
| 14 | ||
| 15 | Streams have an `_open` function that is called before any read/write operation and a `_destroy` | |
| 16 | function that is always run as the last part of the stream. | |
| 17 | ||
| 18 | This makes it easy to maintain state. | |
| 19 | ||
| 20 | #### Easy error handling | |
| 21 | ||
| 22 | Fully integrates a `.destroy()` function. When called the stream will wait for any | |
| 23 | pending operation to finish and call the stream destroy logic. | |
| 24 | ||
| 25 | Close is _always_ the last event emitted and `destroy` is always run. | |
| 26 | ||
| 27 | #### `pipe()` error handles | |
| 28 | ||
| 29 | `pipe` accepts a callback that is called when the pipeline is fully drained. | |
| 30 | It also error handles the streams provided and destroys both streams if either | |
| 31 | of them fail. | |
| 32 | ||
| 33 | #### All streams are both binary and object mode streams | |
| 34 | ||
| 35 | A `map` function can be provided to map your input data into buffers | |
| 36 | or other formats. To indicate how much buffer space each data item takes | |
| 37 | an `byteLength` function can be provided as well. | |
| 38 | ||
| 39 | This removes the need for two modes of streams. | |
| 40 | ||
| 41 | #### Simplicity | |
| 42 | ||
| 43 | This is a full rewrite, all contained in one file. | |
| 44 | ||
| 45 | Lots of stream methods are simplified based on how I and devs I work with actually use streams in the wild. | |
| 46 | ||
| 47 | #### Backwards compat | |
| 48 | ||
| 49 | streamx aims to be compatible with Node.js streams whenever it is reasonable to do so. | |
| 50 | ||
| 51 | This means that streamx streams behave a lot like Node.js streams from the outside but still provides the | |
| 52 | improvements above. | |
| 53 | ||
| 54 | #### Smaller browser footprint | |
| 55 | ||
| 56 | streamx has a much smaller footprint when compiled for the browser: | |
| 57 | ||
| 58 | ``` | |
| 59 | $ for x in stream{,x}; do echo $x: $(browserify -r $x | wc -c) bytes; done | |
| 60 | stream: 173844 bytes | |
| 61 | streamx: 46943 bytes | |
| 62 | ``` | |
| 63 | ||
| 64 | With optimizations turned on, the difference is even more stark: | |
| 65 | ||
| 66 | ``` | |
| 67 | $ for x in stream{,x}; do echo $x: $(browserify -r $x -p tinyify | wc -c) bytes; done | |
| 68 | stream: 62649 bytes | |
| 69 | streamx: 8460 bytes | |
| 70 | $ for x in stream{,x}; do echo $x: $(browserify -r $x -p tinyify | gzip | wc -c) "bytes (gzipped)"; done | |
| 71 | stream: 18053 bytes (gzipped) | |
| 72 | streamx: 2806 bytes (gzipped) | |
| 73 | ``` | |
| 74 | ||
| 75 | #### AbortSignal support | |
| 76 | ||
| 77 | To make it easier to integrate streams in a `async/await` flow, all streams support a `signal` option | |
| 78 | that accepts a [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) to as an | |
| 79 | alternative means to `.destroy` streams. | |
| 80 | ||
| 81 | ## Usage | |
| 82 | ||
| 83 | ```js | |
| 84 | const { Readable } = require('streamx') | |
| 85 | ||
| 86 | const rs = new Readable({ | |
| 87 | read(cb) { | |
| 88 | this.push('Cool data') | |
| 89 | cb(null) | |
| 90 | } | |
| 91 | }) | |
| 92 | ||
| 93 | rs.on('data', (data) => console.log('data:', data)) | |
| 94 | ``` | |
| 95 | ||
| 96 | ## API | |
| 97 | ||
| 98 | This streamx package contains 4 streams similar to Node.js core. | |
| 99 | ||
| 100 | ## Readable Stream | |
| 101 | ||
| 102 | #### `rs = new stream.Readable([options])` | |
| 103 | ||
| 104 | Create a new readable stream. | |
| 105 | ||
| 106 | Options include: | |
| 107 | ||
| 108 | ``` | |
| 109 | { | |
| 110 | highWaterMark: 16384, // max buffer size in bytes | |
| 111 | map: (data) => data, // optional function to map input data | |
| 112 | byteLength: (data) => size, // optional function that calculates the byte size of input data | |
| 113 | signal: abortController.signal, // optional AbortSignal that triggers `.destroy` when on `abort` | |
| 114 | eagerOpen: false // eagerly open the stream | |
| 115 | } | |
| 116 | ``` | |
| 117 | ||
| 118 | In addition you can pass the `open`, `read`, and `destroy` functions as shorthands in | |
| 119 | the constructor instead of overwrite the methods below. | |
| 120 | ||
| 121 | The default byteLength function returns the byte length of buffers and `1024` | |
| 122 | for any other object. This means the buffer will contain around 16 non buffers | |
| 123 | or buffers worth 16kb when full if the defaults are used. | |
| 124 | ||
| 125 | If you set highWaterMark to `0` then all read ahead buffering on the stream | |
| 126 | is disabled and it will only call `_read` when a user reads rather than ahead of time. | |
| 127 | ||
| 128 | #### `rs._read(cb)` | |
| 129 | ||
| 130 | This function is called when the stream wants you to push new data. | |
| 131 | Overwrite this and add your own read logic. | |
| 132 | You should call the callback when you are fully done with the read. | |
| 133 | ||
| 134 | Can also be set using `options.read` in the constructor. | |
| 135 | ||
| 136 | Note that this function differs from Node.js streams in that it takes | |
| 137 | the "read finished" callback. | |
| 138 | ||
| 139 | #### `drained = rs.push(data)` | |
| 140 | ||
| 141 | Push new data to the stream. Returns true if the buffer is not full | |
| 142 | and you should push more data if you can. | |
| 143 | ||
| 144 | If you call `rs.push(null)` you signal to the stream that no more | |
| 145 | data will be pushed and that you want to end the stream. | |
| 146 | ||
| 147 | #### `data = rs.read()` | |
| 148 | ||
| 149 | Read a piece of data from the stream buffer. If the buffer is currently empty | |
| 150 | `null` will be returned and you should wait for `readable` to be emitted before | |
| 151 | trying again. If the stream has been ended it will also return `null`. | |
| 152 | ||
| 153 | Note that this method differs from Node.js streams in that it does not accept | |
| 154 | an optional amounts of bytes to consume. | |
| 155 | ||
| 156 | #### `rs.unshift(data)` | |
| 157 | ||
| 158 | Add a piece of data to the front of the buffer. Use this if you read too much | |
| 159 | data using the `rs.read()` function. | |
| 160 | ||
| 161 | #### `rs._open(cb)` | |
| 162 | ||
| 163 | This function is called once before the first read is issued. Use this function | |
| 164 | to implement your own open logic. | |
| 165 | ||
| 166 | Can also be set using `options.open` in the constructor. | |
| 167 | ||
| 168 | #### `rs._destroy(cb)` | |
| 169 | ||
| 170 | This function is called just before the stream is fully destroyed. You should | |
| 171 | use this to implement whatever teardown logic you need. The final part of the | |
| 172 | stream life cycle is always to call destroy itself so this function will always | |
| 173 | be called wheather or not the stream ends gracefully or forcefully. | |
| 174 | ||
| 175 | Can also be set using `options.destroy` in the constructor. | |
| 176 | ||
| 177 | Note that the `_destroy` might be called without the open function being called | |
| 178 | in case no read was ever performed on the stream. | |
| 179 | ||
| 180 | #### `rs._predestroy()` | |
| 181 | ||
| 182 | A simple hook that is called as soon as the first `stream.destroy()` call is invoked. | |
| 183 | ||
| 184 | Use this in case you need to cancel pending reads (if possible) instead of waiting for them to finish. | |
| 185 | ||
| 186 | Can also be set using `options.predestroy` in the constructor. | |
| 187 | ||
| 188 | #### `rs.destroy([error])` | |
| 189 | ||
| 190 | Forcefully destroy the stream. Will call `_destroy` as soon as all pending reads have finished. | |
| 191 | Once the stream is fully destroyed `close` will be emitted. | |
| 192 | ||
| 193 | If you pass an error this error will be emitted just before `close` is, signifying a reason | |
| 194 | as to why this stream was destroyed. | |
| 195 | ||
| 196 | #### `rs.pause()` | |
| 197 | ||
| 198 | Pauses the stream. You will only need to call this if you want to pause a resumed stream. | |
| 199 | ||
| 200 | Returns this stream instance. | |
| 201 | ||
| 202 | #### `rs.resume()` | |
| 203 | ||
| 204 | Will start reading data from the stream as fast as possible. | |
| 205 | ||
| 206 | If you do not call this, you need to use the `read()` method to read data or the `pipe()` method to | |
| 207 | pipe the stream somewhere else or the `data` handler. | |
| 208 | ||
| 209 | If none of these option are used the stream will stay paused. | |
| 210 | ||
| 211 | Returns this stream instance. | |
| 212 | ||
| 213 | #### `bool = Readable.isPaused(rs)` | |
| 214 | ||
| 215 | Returns `true` if the stream is paused, else `false`. | |
| 216 | ||
| 217 | #### `writableStream = rs.pipe(writableStream, [callback])` | |
| 218 | ||
| 219 | Efficently pipe the readable stream to a writable stream (can be Node.js core stream or a stream from this package). | |
| 220 | If you provide a callback the callback is called when the pipeline has fully finished with an optional error in case | |
| 221 | it failed. | |
| 222 | ||
| 223 | To cancel the pipeline destroy either of the streams. | |
| 224 | ||
| 225 | #### `rs.on('readable')` | |
| 226 | ||
| 227 | Emitted when data is pushed to the stream if the buffer was previously empty. | |
| 228 | ||
| 229 | #### `rs.on('data', data)` | |
| 230 | ||
| 231 | Emitted when data is being read from the stream. If you attach a data handler you are implicitly resuming the stream. | |
| 232 | ||
| 233 | #### `rs.on('end')` | |
| 234 | ||
| 235 | Emitted when the readable stream has ended and no data is left in it's buffer. | |
| 236 | ||
| 237 | #### `rs.on('close')` | |
| 238 | ||
| 239 | Emitted when the readable stream has fully closed (i.e. it's destroy function has completed) | |
| 240 | ||
| 241 | #### `rs.on('error', err)` | |
| 242 | ||
| 243 | Emitted if any of the stream operations fail with an error. `close` is always emitted right after this. | |
| 244 | ||
| 245 | #### `rs.on('piping', dest)` | |
| 246 | ||
| 247 | Emitted when the readable stream is pipeing to a destination. | |
| 248 | ||
| 249 | #### `rs.destroyed` | |
| 250 | ||
| 251 | Boolean property indicating wheather or not this stream has been destroyed. | |
| 252 | ||
| 253 | #### `bool = Readable.isBackpressured(rs)` | |
| 254 | ||
| 255 | Static method to check if a readable stream is currently under backpressure. | |
| 256 | ||
| 257 | #### `stream = Readable.from(arrayOrBufferOrStringOrAsyncIterator)` | |
| 258 | ||
| 259 | Static method to turn an array or buffer or string or AsyncIterator into a readable stream. | |
| 260 | ||
| 261 | ## Writable Stream | |
| 262 | ||
| 263 | #### `ws = new stream.Writable([options])` | |
| 264 | ||
| 265 | Create a new writable stream. | |
| 266 | ||
| 267 | Options include: | |
| 268 | ||
| 269 | ``` | |
| 270 | { | |
| 271 | highWaterMark: 16384, // max buffer size in bytes | |
| 272 | map: (data) => data, // optional function to map input data | |
| 273 | byteLength: (data) => size, // optional function that calculates the byte size of input data | |
| 274 | signal: abortController.signal // optional AbortSignal that triggers `.destroy` when on `abort` | |
| 275 | } | |
| 276 | ``` | |
| 277 | ||
| 278 | In addition you can pass the `open`, `write`, `final`, and `destroy` functions as shorthands in | |
| 279 | the constructor instead of overwrite the methods below. | |
| 280 | ||
| 281 | The default byteLength function returns the byte length of buffers and `1024` | |
| 282 | for any other object. This means the buffer will contain around 16 non buffers | |
| 283 | or buffers worth 16kb when full if the defaults are used. | |
| 284 | ||
| 285 | #### `ws._open(cb)` | |
| 286 | ||
| 287 | This function is called once before the first write is issued. Use this function | |
| 288 | to implement your own open logic. | |
| 289 | ||
| 290 | Can also be set using `options.open` in the constructor. | |
| 291 | ||
| 292 | #### `ws._destroy(cb)` | |
| 293 | ||
| 294 | This function is called just before the stream is fully destroyed. You should | |
| 295 | use this to implement whatever teardown logic you need. The final part of the | |
| 296 | stream life cycle is always to call destroy itself so this function will always | |
| 297 | be called wheather or not the stream ends gracefully or forcefully. | |
| 298 | ||
| 299 | Can also be set using `options.destroy` in the constructor. | |
| 300 | ||
| 301 | Note that the `_destroy` might be called without the open function being called | |
| 302 | in case no write was ever performed on the stream. | |
| 303 | ||
| 304 | #### `ws._predestroy()` | |
| 305 | ||
| 306 | A simple hook that is called as soon as the first `stream.destroy()` call is invoked. | |
| 307 | ||
| 308 | Use this in case you need to cancel pending writes (if possible) instead of waiting for them to finish. | |
| 309 | ||
| 310 | Can also be set using `options.predestroy` in the constructor. | |
| 311 | ||
| 312 | #### `ws.destroy([error])` | |
| 313 | ||
| 314 | Forcefully destroy the stream. Will call `_destroy` as soon as all pending reads have finished. | |
| 315 | Once the stream is fully destroyed `close` will be emitted. | |
| 316 | ||
| 317 | If you pass an error this error will be emitted just before `close` is, signifying a reason | |
| 318 | as to why this stream was destroyed. | |
| 319 | ||
| 320 | #### `drained = ws.write(data)` | |
| 321 | ||
| 322 | Write a piece of data to the stream. Returns `true` if the stream buffer is not full and you | |
| 323 | should keep writing to it if you can. If `false` is returned the stream will emit `drain` | |
| 324 | once it's buffer is fully drained. | |
| 325 | ||
| 326 | #### `ws._write(data, callback)` | |
| 327 | ||
| 328 | This function is called when the stream want to write some data. Use this to implement your own | |
| 329 | write logic. When done call the callback and the stream will call it again if more data exists in the buffer. | |
| 330 | ||
| 331 | Can also be set using `options.write` in the constructor. | |
| 332 | ||
| 333 | #### `ws._writev(batch, callback)` | |
| 334 | ||
| 335 | Similar to `_write` but passes an array of all data in the current write buffer instead of the oldest one. | |
| 336 | Useful if the destination you are writing the data to supports batching. | |
| 337 | ||
| 338 | Can also be set using `options.writev` in the constructor. | |
| 339 | ||
| 340 | #### `ws.end()` | |
| 341 | ||
| 342 | Gracefully end the writable stream. Call this when you no longer want to write to the stream. | |
| 343 | ||
| 344 | Once all writes have been fully drained `finish` will be emitted. | |
| 345 | ||
| 346 | Returns this stream instance. | |
| 347 | ||
| 348 | #### `ws._final(callback)` | |
| 349 | ||
| 350 | This function is called just before `finish` is emitted, i.e. when all writes have flushed but `ws.end()` | |
| 351 | have been called. Use this to implement any logic that should happen after all writes but before finish. | |
| 352 | ||
| 353 | Can also be set using `options.final` in the constructor. | |
| 354 | ||
| 355 | #### `ws.on('finish')` | |
| 356 | ||
| 357 | Emitted when the stream has been ended and all writes have been drained. | |
| 358 | ||
| 359 | #### `ws.on('close')` | |
| 360 | ||
| 361 | Emitted when the readable stream has fully closed (i.e. it's destroy function has completed) | |
| 362 | ||
| 363 | #### `ws.on('error', err)` | |
| 364 | ||
| 365 | Emitted if any of the stream operations fail with an error. `close` is always emitted right after this. | |
| 366 | ||
| 367 | #### `ws.on('pipe', src)` | |
| 368 | ||
| 369 | Emitted when a readable stream is being piped to the writable one. | |
| 370 | ||
| 371 | #### `ws.destroyed` | |
| 372 | ||
| 373 | Boolean property indicating wheather or not this stream has been destroyed. | |
| 374 | ||
| 375 | #### `bool = Writable.isBackpressured(ws)` | |
| 376 | ||
| 377 | Static method to check if a writable stream is currently under backpressure. | |
| 378 | ||
| 379 | #### `bool = await Writable.drained(ws)` | |
| 380 | ||
| 381 | Static helper to wait for a stream to drain the currently queued writes. | |
| 382 | Returns true if they were drained and false otherwise if the stream was destroyed. | |
| 383 | ||
| 384 | ## Duplex Stream | |
| 385 | ||
| 386 | #### `s = new stream.Duplex([options])` | |
| 387 | ||
| 388 | A duplex stream is a stream that is both readable and writable. | |
| 389 | ||
| 390 | Since JS does not support multiple inheritance it inherits directly from Readable | |
| 391 | but implements the Writable API as well. | |
| 392 | ||
| 393 | If you want to provide only a map function for the readable side use `mapReadable` instead. | |
| 394 | If you want to provide only a byteLength function for the readable side use `byteLengthReadable` instead. | |
| 395 | ||
| 396 | Same goes for the writable side but using `mapWritable` and `byteLengthWritable` instead. | |
| 397 | ||
| 398 | ## Transform Stream | |
| 399 | ||
| 400 | A Transform stream is a duplex stream with an `._transform` template method that allows to | |
| 401 | asynchronously map the input to a different output. | |
| 402 | ||
| 403 | The transform stream overrides the `_write` and `_read` operations of `Readable` and `Writable` but | |
| 404 | still allows the setting of these options in the constructor. Usually it is unnecessary to pass | |
| 405 | in `read` or `write`/`writev` or to override the corresponding `._read`, `._write` or `._writev` operation. | |
| 406 | ||
| 407 | #### `ts = new stream.Transform([options])` | |
| 408 | ||
| 409 | A transform stream is a duplex stream that maps the data written to it and emits that as readable data. | |
| 410 | ||
| 411 | Has the same options as a duplex stream except you can provide a `transform` function also. | |
| 412 | ||
| 413 | #### `ts._transform(data, callback)` | |
| 414 | ||
| 415 | Transform the incoming data. Call `callback(null, mappedData)` or use `ts.push(mappedData)` to | |
| 416 | return data to the readable side of the stream. | |
| 417 | ||
| 418 | Per default the transform function just remits the incoming data making it act as a pass-through stream. | |
| 419 | ||
| 420 | ## Pipeline | |
| 421 | ||
| 422 | `pipeline` allows to stream form a readable through a set of duplex streams to a writable entry. | |
| 423 | ||
| 424 | ```js | |
| 425 | const { pipeline, Readable, Transform, Writable } = require('streamx') | |
| 426 | const lastStream = pipeline( | |
| 427 | Readable.from([1, 2, 3]), | |
| 428 | new Transform({ | |
| 429 | transform (from, cb) { | |
| 430 | this.push(from.toString()) | |
| 431 | cb() | |
| 432 | } | |
| 433 | }), | |
| 434 | new Writable({ | |
| 435 | write (data, cb) { | |
| 436 | console.log(data) | |
| 437 | cb() | |
| 438 | } | |
| 439 | }) | |
| 440 | error => { | |
| 441 | // Callback once write has finished | |
| 442 | } | |
| 443 | ) | |
| 444 | ``` | |
| 445 | ||
| 446 | #### `lastStream = stream.pipeline(...streams, [done])` | |
| 447 | ||
| 448 | Pipe all streams together and return the last stream piped to. | |
| 449 | When the last stream finishes the pipeline ended succesfully. | |
| 450 | ||
| 451 | If any of the streams error, whether they are Node.js core streams | |
| 452 | or streamx streams, all streams in the pipeline are shutdown. | |
| 453 | ||
| 454 | Optionally you can pass a done callback to know when the pipeline is done. | |
| 455 | ||
| 456 | #### `promise = stream.pipelinePromise(...streams)` | |
| 457 | ||
| 458 | Same as normal pipeline except instead of returning the last stream it returns | |
| 459 | a promise representing the done callback. Note you should error handle this | |
| 460 | promise if you use this version. | |
| 461 | ||
| 462 | ## Helpers | |
| 463 | ||
| 464 | #### `bool = isStream(stream)` | |
| 465 | ||
| 466 | #### `bool = isStreamx(stream)` | |
| 467 | ||
| 468 | #### `err = getStreamError(stream, [options])` | |
| 469 | ||
| 470 | Returns `null` if the stream has no errors. | |
| 471 | ||
| 472 | ## Utilities | |
| 473 | ||
| 474 | Streamx aims to be minimal and stable. It therefore only contains a minimal set of utilities. | |
| 475 | To help discover of other modules that help you build streamx apps, we link some useful utilities here | |
| 476 | ||
| 477 | - [stream-composer](https://github.com/mafintosh/stream-composer) - Compose streams like Node's `stream.compose` and the `duplexify` and `pumpify` modules. | |
| 478 | - [teex](https://github.com/mafintosh/teex) - Clone a readable stream into multiple new readable instances. | |
| 479 | - [merge-readable](https://github.com/holepunchto/merge-readable) - Merge multiple readers into a new readable with optional mapping of data | |
| 480 | ||
| 481 | ## Contributing | |
| 482 | ||
| 483 | If you want to help contribute to streamx a good way to start is to help writing more test | |
| 484 | cases, compatibility tests, documentation, or performance benchmarks. | |
| 485 | ||
| 486 | If in doubt open an issue :) | |
| 487 | ||
| 488 | ## License | |
| 489 | ||
| 490 | MIT |
| … | … | @@ -0,0 +1,1232 @@ |
| 1 | const { EventEmitter } = require('events-universal') | |
| 2 | const STREAM_DESTROYED = new Error('Stream was destroyed') | |
| 3 | const PREMATURE_CLOSE = new Error('Premature close') | |
| 4 | ||
| 5 | const FIFO = require('fast-fifo') | |
| 6 | const TextDecoder = require('text-decoder') | |
| 7 | ||
| 8 | // if we do a future major, expect queue microtask to be there always, for now a bit defensive | |
| 9 | const qmt = | |
| 10 | typeof queueMicrotask === 'undefined' ? (fn) => global.process.nextTick(fn) : queueMicrotask | |
| 11 | ||
| 12 | // 29 bits used total (4 from shared, 14 from read, and 11 from write) | |
| 13 | const MAX = (1 << 29) - 1 | |
| 14 | ||
| 15 | // Shared state | |
| 16 | const OPENING = 0b0001 | |
| 17 | const PREDESTROYING = 0b0010 | |
| 18 | const DESTROYING = 0b0100 | |
| 19 | const DESTROYED = 0b1000 | |
| 20 | ||
| 21 | const NOT_OPENING = MAX ^ OPENING | |
| 22 | const NOT_PREDESTROYING = MAX ^ PREDESTROYING | |
| 23 | ||
| 24 | // Read state (4 bit offset from shared state) | |
| 25 | const READ_ACTIVE = 0b00000000000001 << 4 | |
| 26 | const READ_UPDATING = 0b00000000000010 << 4 | |
| 27 | const READ_PRIMARY = 0b00000000000100 << 4 | |
| 28 | const READ_QUEUED = 0b00000000001000 << 4 | |
| 29 | const READ_RESUMED = 0b00000000010000 << 4 | |
| 30 | const READ_PIPE_DRAINED = 0b00000000100000 << 4 | |
| 31 | const READ_ENDING = 0b00000001000000 << 4 | |
| 32 | const READ_EMIT_DATA = 0b00000010000000 << 4 | |
| 33 | const READ_EMIT_READABLE = 0b00000100000000 << 4 | |
| 34 | const READ_EMITTED_READABLE = 0b00001000000000 << 4 | |
| 35 | const READ_DONE = 0b00010000000000 << 4 | |
| 36 | const READ_NEXT_TICK = 0b00100000000000 << 4 | |
| 37 | const READ_NEEDS_PUSH = 0b01000000000000 << 4 | |
| 38 | const READ_READ_AHEAD = 0b10000000000000 << 4 | |
| 39 | ||
| 40 | // Combined read state | |
| 41 | const READ_FLOWING = READ_RESUMED | READ_PIPE_DRAINED | |
| 42 | const READ_ACTIVE_AND_NEEDS_PUSH = READ_ACTIVE | READ_NEEDS_PUSH | |
| 43 | const READ_PRIMARY_AND_ACTIVE = READ_PRIMARY | READ_ACTIVE | |
| 44 | const READ_EMIT_READABLE_AND_QUEUED = READ_EMIT_READABLE | READ_QUEUED | |
| 45 | const READ_RESUMED_READ_AHEAD = READ_RESUMED | READ_READ_AHEAD | |
| 46 | ||
| 47 | const READ_NOT_ACTIVE = MAX ^ READ_ACTIVE | |
| 48 | const READ_NON_PRIMARY = MAX ^ READ_PRIMARY | |
| 49 | const READ_NON_PRIMARY_AND_PUSHED = MAX ^ (READ_PRIMARY | READ_NEEDS_PUSH) | |
| 50 | const READ_PUSHED = MAX ^ READ_NEEDS_PUSH | |
| 51 | const READ_PAUSED = MAX ^ READ_RESUMED | |
| 52 | const READ_NOT_QUEUED = MAX ^ (READ_QUEUED | READ_EMITTED_READABLE) | |
| 53 | const READ_NOT_ENDING = MAX ^ READ_ENDING | |
| 54 | const READ_PIPE_NOT_DRAINED = MAX ^ READ_FLOWING | |
| 55 | const READ_NOT_NEXT_TICK = MAX ^ READ_NEXT_TICK | |
| 56 | const READ_NOT_UPDATING = MAX ^ READ_UPDATING | |
| 57 | const READ_NO_READ_AHEAD = MAX ^ READ_READ_AHEAD | |
| 58 | const READ_PAUSED_NO_READ_AHEAD = MAX ^ READ_RESUMED_READ_AHEAD | |
| 59 | ||
| 60 | // Write state (18 bit offset, 4 bit offset from shared state and 14 from read state) | |
| 61 | const WRITE_ACTIVE = 0b00000000001 << 18 | |
| 62 | const WRITE_UPDATING = 0b00000000010 << 18 | |
| 63 | const WRITE_PRIMARY = 0b00000000100 << 18 | |
| 64 | const WRITE_QUEUED = 0b00000001000 << 18 | |
| 65 | const WRITE_UNDRAINED = 0b00000010000 << 18 | |
| 66 | const WRITE_DONE = 0b00000100000 << 18 | |
| 67 | const WRITE_EMIT_DRAIN = 0b00001000000 << 18 | |
| 68 | const WRITE_NEXT_TICK = 0b00010000000 << 18 | |
| 69 | const WRITE_WRITING = 0b00100000000 << 18 | |
| 70 | const WRITE_FINISHING = 0b01000000000 << 18 | |
| 71 | const WRITE_CORKED = 0b10000000000 << 18 | |
| 72 | ||
| 73 | const WRITE_NOT_ACTIVE = MAX ^ (WRITE_ACTIVE | WRITE_WRITING) | |
| 74 | const WRITE_NON_PRIMARY = MAX ^ WRITE_PRIMARY | |
| 75 | const WRITE_NOT_FINISHING = MAX ^ (WRITE_ACTIVE | WRITE_FINISHING) | |
| 76 | const WRITE_DRAINED = MAX ^ WRITE_UNDRAINED | |
| 77 | const WRITE_NOT_QUEUED = MAX ^ WRITE_QUEUED | |
| 78 | const WRITE_NOT_NEXT_TICK = MAX ^ WRITE_NEXT_TICK | |
| 79 | const WRITE_NOT_UPDATING = MAX ^ WRITE_UPDATING | |
| 80 | const WRITE_NOT_CORKED = MAX ^ WRITE_CORKED | |
| 81 | ||
| 82 | // Combined shared state | |
| 83 | const ACTIVE = READ_ACTIVE | WRITE_ACTIVE | |
| 84 | const NOT_ACTIVE = MAX ^ ACTIVE | |
| 85 | const DONE = READ_DONE | WRITE_DONE | |
| 86 | const DESTROY_STATUS = DESTROYING | DESTROYED | PREDESTROYING | |
| 87 | const OPEN_STATUS = DESTROY_STATUS | OPENING | |
| 88 | const AUTO_DESTROY = DESTROY_STATUS | DONE | |
| 89 | const NON_PRIMARY = WRITE_NON_PRIMARY & READ_NON_PRIMARY | |
| 90 | const ACTIVE_OR_TICKING = WRITE_NEXT_TICK | READ_NEXT_TICK | |
| 91 | const TICKING = ACTIVE_OR_TICKING & NOT_ACTIVE | |
| 92 | const IS_OPENING = OPEN_STATUS | TICKING | |
| 93 | ||
| 94 | // Combined shared state and read state | |
| 95 | const READ_PRIMARY_STATUS = OPEN_STATUS | READ_ENDING | READ_DONE | |
| 96 | const READ_STATUS = OPEN_STATUS | READ_DONE | READ_QUEUED | |
| 97 | const READ_ENDING_STATUS = OPEN_STATUS | READ_ENDING | READ_QUEUED | |
| 98 | const READ_READABLE_STATUS = OPEN_STATUS | READ_EMIT_READABLE | READ_QUEUED | READ_EMITTED_READABLE | |
| 99 | const SHOULD_NOT_READ = | |
| 100 | OPEN_STATUS | READ_ACTIVE | READ_ENDING | READ_DONE | READ_NEEDS_PUSH | READ_READ_AHEAD | |
| 101 | const READ_BACKPRESSURE_STATUS = DESTROY_STATUS | READ_ENDING | READ_DONE | |
| 102 | const READ_UPDATE_SYNC_STATUS = READ_UPDATING | OPEN_STATUS | READ_NEXT_TICK | READ_PRIMARY | |
| 103 | const READ_NEXT_TICK_OR_OPENING = READ_NEXT_TICK | OPENING | |
| 104 | ||
| 105 | // Combined write state | |
| 106 | const WRITE_PRIMARY_STATUS = OPEN_STATUS | WRITE_FINISHING | WRITE_DONE | |
| 107 | const WRITE_QUEUED_AND_UNDRAINED = WRITE_QUEUED | WRITE_UNDRAINED | |
| 108 | const WRITE_QUEUED_AND_ACTIVE = WRITE_QUEUED | WRITE_ACTIVE | |
| 109 | const WRITE_DRAIN_STATUS = WRITE_QUEUED | WRITE_UNDRAINED | OPEN_STATUS | WRITE_ACTIVE | |
| 110 | const WRITE_STATUS = OPEN_STATUS | WRITE_ACTIVE | WRITE_QUEUED | WRITE_CORKED | |
| 111 | const WRITE_PRIMARY_AND_ACTIVE = WRITE_PRIMARY | WRITE_ACTIVE | |
| 112 | const WRITE_ACTIVE_AND_WRITING = WRITE_ACTIVE | WRITE_WRITING | |
| 113 | const WRITE_FINISHING_STATUS = OPEN_STATUS | WRITE_FINISHING | WRITE_QUEUED_AND_ACTIVE | WRITE_DONE | |
| 114 | const WRITE_BACKPRESSURE_STATUS = WRITE_UNDRAINED | DESTROY_STATUS | WRITE_FINISHING | WRITE_DONE | |
| 115 | const WRITE_UPDATE_SYNC_STATUS = WRITE_UPDATING | OPEN_STATUS | WRITE_NEXT_TICK | WRITE_PRIMARY | |
| 116 | const WRITE_DROP_DATA = WRITE_FINISHING | WRITE_DONE | DESTROY_STATUS | |
| 117 | ||
| 118 | const asyncIterator = Symbol.asyncIterator || Symbol('asyncIterator') | |
| 119 | ||
| 120 | class WritableState { | |
| 121 | constructor( | |
| 122 | stream, | |
| 123 | { highWaterMark = 16384, map = null, mapWritable, byteLength, byteLengthWritable } = {} | |
| 124 | ) { | |
| 125 | this.stream = stream | |
| 126 | this.queue = new FIFO() | |
| 127 | this.highWaterMark = highWaterMark | |
| 128 | this.buffered = 0 | |
| 129 | this.error = null | |
| 130 | this.pipeline = null | |
| 131 | this.drains = null // if we add more seldomly used helpers we might them into a subobject so its a single ptr | |
| 132 | this.byteLength = byteLengthWritable || byteLength || defaultByteLength | |
| 133 | this.map = mapWritable || map | |
| 134 | this.afterWrite = afterWrite.bind(this) | |
| 135 | this.afterUpdateNextTick = updateWriteNT.bind(this) | |
| 136 | } | |
| 137 | ||
| 138 | get ending() { | |
| 139 | return (this.stream._duplexState & WRITE_FINISHING) !== 0 | |
| 140 | } | |
| 141 | ||
| 142 | get ended() { | |
| 143 | return (this.stream._duplexState & WRITE_DONE) !== 0 | |
| 144 | } | |
| 145 | ||
| 146 | push(data) { | |
| 147 | if ((this.stream._duplexState & WRITE_DROP_DATA) !== 0) return false | |
| 148 | if (this.map !== null) data = this.map(data) | |
| 149 | ||
| 150 | this.buffered += this.byteLength(data) | |
| 151 | this.queue.push(data) | |
| 152 | ||
| 153 | if (this.buffered < this.highWaterMark) { | |
| 154 | this.stream._duplexState |= WRITE_QUEUED | |
| 155 | return true | |
| 156 | } | |
| 157 | ||
| 158 | this.stream._duplexState |= WRITE_QUEUED_AND_UNDRAINED | |
| 159 | return false | |
| 160 | } | |
| 161 | ||
| 162 | shift() { | |
| 163 | const data = this.queue.shift() | |
| 164 | ||
| 165 | this.buffered -= this.byteLength(data) | |
| 166 | if (this.buffered === 0) this.stream._duplexState &= WRITE_NOT_QUEUED | |
| 167 | ||
| 168 | return data | |
| 169 | } | |
| 170 | ||
| 171 | end(data) { | |
| 172 | if (typeof data === 'function') this.stream.once('finish', data) | |
| 173 | else if (data !== undefined && data !== null) this.push(data) | |
| 174 | this.stream._duplexState = (this.stream._duplexState | WRITE_FINISHING) & WRITE_NON_PRIMARY | |
| 175 | } | |
| 176 | ||
| 177 | autoBatch(data, cb) { | |
| 178 | const buffer = [] | |
| 179 | const stream = this.stream | |
| 180 | ||
| 181 | buffer.push(data) | |
| 182 | while ((stream._duplexState & WRITE_STATUS) === WRITE_QUEUED_AND_ACTIVE) { | |
| 183 | buffer.push(stream._writableState.shift()) | |
| 184 | } | |
| 185 | ||
| 186 | if ((stream._duplexState & OPEN_STATUS) !== 0) return cb(null) | |
| 187 | stream._writev(buffer, cb) | |
| 188 | } | |
| 189 | ||
| 190 | update() { | |
| 191 | const stream = this.stream | |
| 192 | ||
| 193 | stream._duplexState |= WRITE_UPDATING | |
| 194 | ||
| 195 | do { | |
| 196 | while ((stream._duplexState & WRITE_STATUS) === WRITE_QUEUED) { | |
| 197 | const data = this.shift() | |
| 198 | stream._duplexState |= WRITE_ACTIVE_AND_WRITING | |
| 199 | stream._write(data, this.afterWrite) | |
| 200 | } | |
| 201 | ||
| 202 | if ((stream._duplexState & WRITE_PRIMARY_AND_ACTIVE) === 0) this.updateNonPrimary() | |
| 203 | } while (this.continueUpdate() === true) | |
| 204 | ||
| 205 | stream._duplexState &= WRITE_NOT_UPDATING | |
| 206 | } | |
| 207 | ||
| 208 | updateNonPrimary() { | |
| 209 | const stream = this.stream | |
| 210 | ||
| 211 | if ((stream._duplexState & WRITE_FINISHING_STATUS) === WRITE_FINISHING) { | |
| 212 | stream._duplexState = stream._duplexState | WRITE_ACTIVE | |
| 213 | stream._final(afterFinal.bind(this)) | |
| 214 | return | |
| 215 | } | |
| 216 | ||
| 217 | if ((stream._duplexState & DESTROY_STATUS) === DESTROYING) { | |
| 218 | if ((stream._duplexState & ACTIVE_OR_TICKING) === 0) { | |
| 219 | stream._duplexState |= ACTIVE | |
| 220 | stream._destroy(afterDestroy.bind(this)) | |
| 221 | } | |
| 222 | return | |
| 223 | } | |
| 224 | ||
| 225 | if ((stream._duplexState & IS_OPENING) === OPENING) { | |
| 226 | stream._duplexState = (stream._duplexState | ACTIVE) & NOT_OPENING | |
| 227 | stream._open(afterOpen.bind(this)) | |
| 228 | } | |
| 229 | } | |
| 230 | ||
| 231 | continueUpdate() { | |
| 232 | if ((this.stream._duplexState & WRITE_NEXT_TICK) === 0) return false | |
| 233 | this.stream._duplexState &= WRITE_NOT_NEXT_TICK | |
| 234 | return true | |
| 235 | } | |
| 236 | ||
| 237 | updateCallback() { | |
| 238 | if ((this.stream._duplexState & WRITE_UPDATE_SYNC_STATUS) === WRITE_PRIMARY) this.update() | |
| 239 | else this.updateNextTick() | |
| 240 | } | |
| 241 | ||
| 242 | updateNextTick() { | |
| 243 | if ((this.stream._duplexState & WRITE_NEXT_TICK) !== 0) return | |
| 244 | this.stream._duplexState |= WRITE_NEXT_TICK | |
| 245 | if ((this.stream._duplexState & WRITE_UPDATING) === 0) qmt(this.afterUpdateNextTick) | |
| 246 | } | |
| 247 | } | |
| 248 | ||
| 249 | class ReadableState { | |
| 250 | constructor( | |
| 251 | stream, | |
| 252 | { highWaterMark = 16384, map = null, mapReadable, byteLength, byteLengthReadable } = {} | |
| 253 | ) { | |
| 254 | this.stream = stream | |
| 255 | this.queue = new FIFO() | |
| 256 | this.highWaterMark = highWaterMark === 0 ? 1 : highWaterMark | |
| 257 | this.buffered = 0 | |
| 258 | this.readAhead = highWaterMark > 0 | |
| 259 | this.error = null | |
| 260 | this.pipeline = null | |
| 261 | this.byteLength = byteLengthReadable || byteLength || defaultByteLength | |
| 262 | this.map = mapReadable || map | |
| 263 | this.pipeTo = null | |
| 264 | this.afterRead = afterRead.bind(this) | |
| 265 | this.afterUpdateNextTick = updateReadNT.bind(this) | |
| 266 | } | |
| 267 | ||
| 268 | get ending() { | |
| 269 | return (this.stream._duplexState & READ_ENDING) !== 0 | |
| 270 | } | |
| 271 | ||
| 272 | get ended() { | |
| 273 | return (this.stream._duplexState & READ_DONE) !== 0 | |
| 274 | } | |
| 275 | ||
| 276 | pipe(pipeTo, cb) { | |
| 277 | if (this.pipeTo !== null) throw new Error('Can only pipe to one destination') | |
| 278 | if (typeof cb !== 'function') cb = null | |
| 279 | ||
| 280 | this.stream._duplexState |= READ_PIPE_DRAINED | |
| 281 | this.pipeTo = pipeTo | |
| 282 | this.pipeline = new Pipeline(this.stream, pipeTo, cb) | |
| 283 | ||
| 284 | if (cb) this.stream.on('error', noop) // We already error handle this so supress crashes | |
| 285 | ||
| 286 | if (isStreamx(pipeTo)) { | |
| 287 | pipeTo._writableState.pipeline = this.pipeline | |
| 288 | if (cb) pipeTo.on('error', noop) // We already error handle this so supress crashes | |
| 289 | pipeTo.on('finish', this.pipeline.finished.bind(this.pipeline)) // TODO: just call finished from pipeTo itself | |
| 290 | } else { | |
| 291 | const onerror = this.pipeline.done.bind(this.pipeline, pipeTo) | |
| 292 | const onclose = this.pipeline.done.bind(this.pipeline, pipeTo, null) // onclose has a weird bool arg | |
| 293 | pipeTo.on('error', onerror) | |
| 294 | pipeTo.on('close', onclose) | |
| 295 | pipeTo.on('finish', this.pipeline.finished.bind(this.pipeline)) | |
| 296 | } | |
| 297 | ||
| 298 | pipeTo.on('drain', afterDrain.bind(this)) | |
| 299 | this.stream.emit('piping', pipeTo) | |
| 300 | pipeTo.emit('pipe', this.stream) | |
| 301 | } | |
| 302 | ||
| 303 | push(data) { | |
| 304 | const stream = this.stream | |
| 305 | ||
| 306 | if (data === null) { | |
| 307 | this.highWaterMark = 0 | |
| 308 | stream._duplexState = (stream._duplexState | READ_ENDING) & READ_NON_PRIMARY_AND_PUSHED | |
| 309 | return false | |
| 310 | } | |
| 311 | ||
| 312 | if (this.map !== null) { | |
| 313 | data = this.map(data) | |
| 314 | if (data === null) { | |
| 315 | stream._duplexState &= READ_PUSHED | |
| 316 | return this.buffered < this.highWaterMark | |
| 317 | } | |
| 318 | } | |
| 319 | ||
| 320 | this.buffered += this.byteLength(data) | |
| 321 | this.queue.push(data) | |
| 322 | ||
| 323 | stream._duplexState = (stream._duplexState | READ_QUEUED) & READ_PUSHED | |
| 324 | ||
| 325 | return this.buffered < this.highWaterMark | |
| 326 | } | |
| 327 | ||
| 328 | shift() { | |
| 329 | const data = this.queue.shift() | |
| 330 | ||
| 331 | this.buffered -= this.byteLength(data) | |
| 332 | if (this.buffered === 0) this.stream._duplexState &= READ_NOT_QUEUED | |
| 333 | return data | |
| 334 | } | |
| 335 | ||
| 336 | unshift(data) { | |
| 337 | const pending = [this.map !== null ? this.map(data) : data] | |
| 338 | while (this.buffered > 0) pending.push(this.shift()) | |
| 339 | ||
| 340 | for (let i = 0; i < pending.length - 1; i++) { | |
| 341 | const data = pending[i] | |
| 342 | this.buffered += this.byteLength(data) | |
| 343 | this.queue.push(data) | |
| 344 | } | |
| 345 | ||
| 346 | this.push(pending[pending.length - 1]) | |
| 347 | } | |
| 348 | ||
| 349 | read() { | |
| 350 | const stream = this.stream | |
| 351 | ||
| 352 | if ((stream._duplexState & READ_STATUS) === READ_QUEUED) { | |
| 353 | const data = this.shift() | |
| 354 | if (this.pipeTo !== null && this.pipeTo.write(data) === false) | |
| 355 | stream._duplexState &= READ_PIPE_NOT_DRAINED | |
| 356 | if ((stream._duplexState & READ_EMIT_DATA) !== 0) stream.emit('data', data) | |
| 357 | return data | |
| 358 | } | |
| 359 | ||
| 360 | if (this.readAhead === false) { | |
| 361 | stream._duplexState |= READ_READ_AHEAD | |
| 362 | this.updateNextTick() | |
| 363 | } | |
| 364 | ||
| 365 | return null | |
| 366 | } | |
| 367 | ||
| 368 | drain() { | |
| 369 | const stream = this.stream | |
| 370 | ||
| 371 | while ( | |
| 372 | (stream._duplexState & READ_STATUS) === READ_QUEUED && | |
| 373 | (stream._duplexState & READ_FLOWING) !== 0 | |
| 374 | ) { | |
| 375 | const data = this.shift() | |
| 376 | if (this.pipeTo !== null && this.pipeTo.write(data) === false) | |
| 377 | stream._duplexState &= READ_PIPE_NOT_DRAINED | |
| 378 | if ((stream._duplexState & READ_EMIT_DATA) !== 0) stream.emit('data', data) | |
| 379 | } | |
| 380 | } | |
| 381 | ||
| 382 | update() { | |
| 383 | const stream = this.stream | |
| 384 | ||
| 385 | stream._duplexState |= READ_UPDATING | |
| 386 | ||
| 387 | do { | |
| 388 | this.drain() | |
| 389 | ||
| 390 | while ( | |
| 391 | this.buffered < this.highWaterMark && | |
| 392 | (stream._duplexState & SHOULD_NOT_READ) === READ_READ_AHEAD | |
| 393 | ) { | |
| 394 | stream._duplexState |= READ_ACTIVE_AND_NEEDS_PUSH | |
| 395 | stream._read(this.afterRead) | |
| 396 | this.drain() | |
| 397 | } | |
| 398 | ||
| 399 | if ((stream._duplexState & READ_READABLE_STATUS) === READ_EMIT_READABLE_AND_QUEUED) { | |
| 400 | stream._duplexState |= READ_EMITTED_READABLE | |
| 401 | stream.emit('readable') | |
| 402 | } | |
| 403 | ||
| 404 | if ((stream._duplexState & READ_PRIMARY_AND_ACTIVE) === 0) this.updateNonPrimary() | |
| 405 | } while (this.continueUpdate() === true) | |
| 406 | ||
| 407 | stream._duplexState &= READ_NOT_UPDATING | |
| 408 | } | |
| 409 | ||
| 410 | updateNonPrimary() { | |
| 411 | const stream = this.stream | |
| 412 | ||
| 413 | if ((stream._duplexState & READ_ENDING_STATUS) === READ_ENDING) { | |
| 414 | stream._duplexState = (stream._duplexState | READ_DONE) & READ_NOT_ENDING | |
| 415 | stream.emit('end') | |
| 416 | if ((stream._duplexState & AUTO_DESTROY) === DONE) stream._duplexState |= DESTROYING | |
| 417 | if (this.pipeTo !== null) this.pipeTo.end() | |
| 418 | } | |
| 419 | ||
| 420 | if ((stream._duplexState & DESTROY_STATUS) === DESTROYING) { | |
| 421 | if ((stream._duplexState & ACTIVE_OR_TICKING) === 0) { | |
| 422 | stream._duplexState |= ACTIVE | |
| 423 | stream._destroy(afterDestroy.bind(this)) | |
| 424 | } | |
| 425 | return | |
| 426 | } | |
| 427 | ||
| 428 | if ((stream._duplexState & IS_OPENING) === OPENING) { | |
| 429 | stream._duplexState = (stream._duplexState | ACTIVE) & NOT_OPENING | |
| 430 | stream._open(afterOpen.bind(this)) | |
| 431 | } | |
| 432 | } | |
| 433 | ||
| 434 | continueUpdate() { | |
| 435 | if ((this.stream._duplexState & READ_NEXT_TICK) === 0) return false | |
| 436 | this.stream._duplexState &= READ_NOT_NEXT_TICK | |
| 437 | return true | |
| 438 | } | |
| 439 | ||
| 440 | updateCallback() { | |
| 441 | if ((this.stream._duplexState & READ_UPDATE_SYNC_STATUS) === READ_PRIMARY) this.update() | |
| 442 | else this.updateNextTick() | |
| 443 | } | |
| 444 | ||
| 445 | updateNextTickIfOpen() { | |
| 446 | if ((this.stream._duplexState & READ_NEXT_TICK_OR_OPENING) !== 0) return | |
| 447 | this.stream._duplexState |= READ_NEXT_TICK | |
| 448 | if ((this.stream._duplexState & READ_UPDATING) === 0) qmt(this.afterUpdateNextTick) | |
| 449 | } | |
| 450 | ||
| 451 | updateNextTick() { | |
| 452 | if ((this.stream._duplexState & READ_NEXT_TICK) !== 0) return | |
| 453 | this.stream._duplexState |= READ_NEXT_TICK | |
| 454 | if ((this.stream._duplexState & READ_UPDATING) === 0) qmt(this.afterUpdateNextTick) | |
| 455 | } | |
| 456 | } | |
| 457 | ||
| 458 | class TransformState { | |
| 459 | constructor(stream) { | |
| 460 | this.data = null | |
| 461 | this.afterTransform = afterTransform.bind(stream) | |
| 462 | this.afterFinal = null | |
| 463 | } | |
| 464 | } | |
| 465 | ||
| 466 | class Pipeline { | |
| 467 | constructor(src, dst, cb) { | |
| 468 | this.from = src | |
| 469 | this.to = dst | |
| 470 | this.afterPipe = cb | |
| 471 | this.error = null | |
| 472 | this.pipeToFinished = false | |
| 473 | } | |
| 474 | ||
| 475 | finished() { | |
| 476 | this.pipeToFinished = true | |
| 477 | } | |
| 478 | ||
| 479 | done(stream, err) { | |
| 480 | if (err) this.error = err | |
| 481 | ||
| 482 | if (stream === this.to) { | |
| 483 | this.to = null | |
| 484 | ||
| 485 | if (this.from !== null) { | |
| 486 | if ((this.from._duplexState & READ_DONE) === 0 || !this.pipeToFinished) { | |
| 487 | this.from.destroy(this.error || new Error('Writable stream closed prematurely')) | |
| 488 | } | |
| 489 | return | |
| 490 | } | |
| 491 | } | |
| 492 | ||
| 493 | if (stream === this.from) { | |
| 494 | this.from = null | |
| 495 | ||
| 496 | if (this.to !== null) { | |
| 497 | if ((stream._duplexState & READ_DONE) === 0) { | |
| 498 | this.to.destroy(this.error || new Error('Readable stream closed before ending')) | |
| 499 | } | |
| 500 | return | |
| 501 | } | |
| 502 | } | |
| 503 | ||
| 504 | if (this.afterPipe !== null) this.afterPipe(this.error) | |
| 505 | this.to = this.from = this.afterPipe = null | |
| 506 | } | |
| 507 | } | |
| 508 | ||
| 509 | function afterDrain() { | |
| 510 | this.stream._duplexState |= READ_PIPE_DRAINED | |
| 511 | this.updateCallback() | |
| 512 | } | |
| 513 | ||
| 514 | function afterFinal(err) { | |
| 515 | const stream = this.stream | |
| 516 | if (err) stream.destroy(err) | |
| 517 | if ((stream._duplexState & DESTROY_STATUS) === 0) { | |
| 518 | stream._duplexState |= WRITE_DONE | |
| 519 | stream.emit('finish') | |
| 520 | } | |
| 521 | if ((stream._duplexState & AUTO_DESTROY) === DONE) { | |
| 522 | stream._duplexState |= DESTROYING | |
| 523 | } | |
| 524 | ||
| 525 | stream._duplexState &= WRITE_NOT_FINISHING | |
| 526 | ||
| 527 | // no need to wait the extra tick here, so we short circuit that | |
| 528 | if ((stream._duplexState & WRITE_UPDATING) === 0) this.update() | |
| 529 | else this.updateNextTick() | |
| 530 | } | |
| 531 | ||
| 532 | function afterDestroy(err) { | |
| 533 | const stream = this.stream | |
| 534 | ||
| 535 | if (!err && this.error !== STREAM_DESTROYED) err = this.error | |
| 536 | if (err) stream.emit('error', err) | |
| 537 | stream._duplexState |= DESTROYED | |
| 538 | stream.emit('close') | |
| 539 | ||
| 540 | const rs = stream._readableState | |
| 541 | const ws = stream._writableState | |
| 542 | ||
| 543 | if (rs !== null && rs.pipeline !== null) rs.pipeline.done(stream, err) | |
| 544 | ||
| 545 | if (ws !== null) { | |
| 546 | while (ws.drains !== null && ws.drains.length > 0) ws.drains.shift().resolve(false) | |
| 547 | if (ws.pipeline !== null) ws.pipeline.done(stream, err) | |
| 548 | } | |
| 549 | } | |
| 550 | ||
| 551 | function afterWrite(err) { | |
| 552 | const stream = this.stream | |
| 553 | ||
| 554 | if (err) stream.destroy(err) | |
| 555 | stream._duplexState &= WRITE_NOT_ACTIVE | |
| 556 | ||
| 557 | if (this.drains !== null) tickDrains(this.drains) | |
| 558 | ||
| 559 | if ((stream._duplexState & WRITE_DRAIN_STATUS) === WRITE_UNDRAINED) { | |
| 560 | stream._duplexState &= WRITE_DRAINED | |
| 561 | if ((stream._duplexState & WRITE_EMIT_DRAIN) === WRITE_EMIT_DRAIN) { | |
| 562 | stream.emit('drain') | |
| 563 | } | |
| 564 | } | |
| 565 | ||
| 566 | this.updateCallback() | |
| 567 | } | |
| 568 | ||
| 569 | function afterRead(err) { | |
| 570 | if (err) this.stream.destroy(err) | |
| 571 | this.stream._duplexState &= READ_NOT_ACTIVE | |
| 572 | if (this.readAhead === false && (this.stream._duplexState & READ_RESUMED) === 0) | |
| 573 | this.stream._duplexState &= READ_NO_READ_AHEAD | |
| 574 | this.updateCallback() | |
| 575 | } | |
| 576 | ||
| 577 | function updateReadNT() { | |
| 578 | if ((this.stream._duplexState & READ_UPDATING) === 0) { | |
| 579 | this.stream._duplexState &= READ_NOT_NEXT_TICK | |
| 580 | this.update() | |
| 581 | } | |
| 582 | } | |
| 583 | ||
| 584 | function updateWriteNT() { | |
| 585 | if ((this.stream._duplexState & WRITE_UPDATING) === 0) { | |
| 586 | this.stream._duplexState &= WRITE_NOT_NEXT_TICK | |
| 587 | this.update() | |
| 588 | } | |
| 589 | } | |
| 590 | ||
| 591 | function tickDrains(drains) { | |
| 592 | for (let i = 0; i < drains.length; i++) { | |
| 593 | // drains.writes are monotonic, so if one is 0 its always the first one | |
| 594 | if (--drains[i].writes === 0) { | |
| 595 | drains.shift().resolve(true) | |
| 596 | i-- | |
| 597 | } | |
| 598 | } | |
| 599 | } | |
| 600 | ||
| 601 | function afterOpen(err) { | |
| 602 | const stream = this.stream | |
| 603 | ||
| 604 | if (err) stream.destroy(err) | |
| 605 | ||
| 606 | if ((stream._duplexState & DESTROYING) === 0) { | |
| 607 | if ((stream._duplexState & READ_PRIMARY_STATUS) === 0) stream._duplexState |= READ_PRIMARY | |
| 608 | if ((stream._duplexState & WRITE_PRIMARY_STATUS) === 0) stream._duplexState |= WRITE_PRIMARY | |
| 609 | stream.emit('open') | |
| 610 | } | |
| 611 | ||
| 612 | stream._duplexState &= NOT_ACTIVE | |
| 613 | ||
| 614 | if (stream._writableState !== null) { | |
| 615 | stream._writableState.updateCallback() | |
| 616 | } | |
| 617 | ||
| 618 | if (stream._readableState !== null) { | |
| 619 | stream._readableState.updateCallback() | |
| 620 | } | |
| 621 | } | |
| 622 | ||
| 623 | function afterTransform(err, data) { | |
| 624 | if (data !== undefined && data !== null) this.push(data) | |
| 625 | this._writableState.afterWrite(err) | |
| 626 | } | |
| 627 | ||
| 628 | function newListener(name) { | |
| 629 | if (this._readableState !== null) { | |
| 630 | if (name === 'data') { | |
| 631 | this._duplexState |= READ_EMIT_DATA | READ_RESUMED_READ_AHEAD | |
| 632 | this._readableState.updateNextTick() | |
| 633 | } | |
| 634 | if (name === 'readable') { | |
| 635 | this._duplexState |= READ_EMIT_READABLE | |
| 636 | this._readableState.updateNextTick() | |
| 637 | } | |
| 638 | } | |
| 639 | ||
| 640 | if (this._writableState !== null) { | |
| 641 | if (name === 'drain') { | |
| 642 | this._duplexState |= WRITE_EMIT_DRAIN | |
| 643 | this._writableState.updateNextTick() | |
| 644 | } | |
| 645 | } | |
| 646 | } | |
| 647 | ||
| 648 | class Stream extends EventEmitter { | |
| 649 | constructor(opts) { | |
| 650 | super() | |
| 651 | ||
| 652 | this._duplexState = 0 | |
| 653 | this._readableState = null | |
| 654 | this._writableState = null | |
| 655 | ||
| 656 | if (opts) { | |
| 657 | if (opts.open) this._open = opts.open | |
| 658 | if (opts.destroy) this._destroy = opts.destroy | |
| 659 | if (opts.predestroy) this._predestroy = opts.predestroy | |
| 660 | if (opts.signal) { | |
| 661 | opts.signal.addEventListener('abort', abort.bind(this)) | |
| 662 | } | |
| 663 | } | |
| 664 | ||
| 665 | this.on('newListener', newListener) | |
| 666 | } | |
| 667 | ||
| 668 | _open(cb) { | |
| 669 | cb(null) | |
| 670 | } | |
| 671 | ||
| 672 | _destroy(cb) { | |
| 673 | cb(null) | |
| 674 | } | |
| 675 | ||
| 676 | _predestroy() { | |
| 677 | // does nothing | |
| 678 | } | |
| 679 | ||
| 680 | get readable() { | |
| 681 | return this._readableState !== null ? true : undefined | |
| 682 | } | |
| 683 | ||
| 684 | get writable() { | |
| 685 | return this._writableState !== null ? true : undefined | |
| 686 | } | |
| 687 | ||
| 688 | get destroyed() { | |
| 689 | return (this._duplexState & DESTROYED) !== 0 | |
| 690 | } | |
| 691 | ||
| 692 | get destroying() { | |
| 693 | return (this._duplexState & DESTROY_STATUS) !== 0 | |
| 694 | } | |
| 695 | ||
| 696 | destroy(err) { | |
| 697 | if ((this._duplexState & DESTROY_STATUS) === 0) { | |
| 698 | if (!err) err = STREAM_DESTROYED | |
| 699 | this._duplexState = (this._duplexState | DESTROYING) & NON_PRIMARY | |
| 700 | ||
| 701 | if (this._readableState !== null) { | |
| 702 | this._readableState.highWaterMark = 0 | |
| 703 | this._readableState.error = err | |
| 704 | } | |
| 705 | if (this._writableState !== null) { | |
| 706 | this._writableState.highWaterMark = 0 | |
| 707 | this._writableState.error = err | |
| 708 | } | |
| 709 | ||
| 710 | this._duplexState |= PREDESTROYING | |
| 711 | this._predestroy() | |
| 712 | this._duplexState &= NOT_PREDESTROYING | |
| 713 | ||
| 714 | if (this._readableState !== null) this._readableState.updateNextTick() | |
| 715 | if (this._writableState !== null) this._writableState.updateNextTick() | |
| 716 | } | |
| 717 | } | |
| 718 | } | |
| 719 | ||
| 720 | class Readable extends Stream { | |
| 721 | constructor(opts) { | |
| 722 | super(opts) | |
| 723 | ||
| 724 | this._duplexState |= OPENING | WRITE_DONE | READ_READ_AHEAD | |
| 725 | this._readableState = new ReadableState(this, opts) | |
| 726 | ||
| 727 | if (opts) { | |
| 728 | if (this._readableState.readAhead === false) this._duplexState &= READ_NO_READ_AHEAD | |
| 729 | if (opts.read) this._read = opts.read | |
| 730 | if (opts.eagerOpen) this._readableState.updateNextTick() | |
| 731 | if (opts.encoding) this.setEncoding(opts.encoding) | |
| 732 | } | |
| 733 | } | |
| 734 | ||
| 735 | setEncoding(encoding) { | |
| 736 | const dec = new TextDecoder(encoding) | |
| 737 | const map = this._readableState.map || echo | |
| 738 | this._readableState.map = mapOrSkip | |
| 739 | return this | |
| 740 | ||
| 741 | function mapOrSkip(data) { | |
| 742 | const next = dec.push(data) | |
| 743 | return next === '' && (data.byteLength !== 0 || dec.remaining > 0) ? null : map(next) | |
| 744 | } | |
| 745 | } | |
| 746 | ||
| 747 | _read(cb) { | |
| 748 | cb(null) | |
| 749 | } | |
| 750 | ||
| 751 | pipe(dest, cb) { | |
| 752 | this._readableState.updateNextTick() | |
| 753 | this._readableState.pipe(dest, cb) | |
| 754 | return dest | |
| 755 | } | |
| 756 | ||
| 757 | read() { | |
| 758 | this._readableState.updateNextTick() | |
| 759 | return this._readableState.read() | |
| 760 | } | |
| 761 | ||
| 762 | push(data) { | |
| 763 | this._readableState.updateNextTickIfOpen() | |
| 764 | return this._readableState.push(data) | |
| 765 | } | |
| 766 | ||
| 767 | unshift(data) { | |
| 768 | this._readableState.updateNextTickIfOpen() | |
| 769 | return this._readableState.unshift(data) | |
| 770 | } | |
| 771 | ||
| 772 | resume() { | |
| 773 | this._duplexState |= READ_RESUMED_READ_AHEAD | |
| 774 | this._readableState.updateNextTick() | |
| 775 | return this | |
| 776 | } | |
| 777 | ||
| 778 | pause() { | |
| 779 | this._duplexState &= | |
| 780 | this._readableState.readAhead === false ? READ_PAUSED_NO_READ_AHEAD : READ_PAUSED | |
| 781 | return this | |
| 782 | } | |
| 783 | ||
| 784 | static _fromAsyncIterator(ite, opts) { | |
| 785 | let destroy | |
| 786 | ||
| 787 | const rs = new Readable({ | |
| 788 | ...opts, | |
| 789 | read(cb) { | |
| 790 | ite.next().then(push).then(cb.bind(null, null)).catch(cb) | |
| 791 | }, | |
| 792 | predestroy() { | |
| 793 | destroy = ite.return() | |
| 794 | }, | |
| 795 | destroy(cb) { | |
| 796 | if (!destroy) return cb(null) | |
| 797 | destroy.then(cb.bind(null, null)).catch(cb) | |
| 798 | } | |
| 799 | }) | |
| 800 | ||
| 801 | return rs | |
| 802 | ||
| 803 | function push(data) { | |
| 804 | if (data.done) rs.push(null) | |
| 805 | else rs.push(data.value) | |
| 806 | } | |
| 807 | } | |
| 808 | ||
| 809 | static from(data, opts) { | |
| 810 | if (isReadStreamx(data)) return data | |
| 811 | if (data[asyncIterator]) return this._fromAsyncIterator(data[asyncIterator](), opts) | |
| 812 | if (!Array.isArray(data)) data = data === undefined ? [] : [data] | |
| 813 | ||
| 814 | let i = 0 | |
| 815 | return new Readable({ | |
| 816 | ...opts, | |
| 817 | read(cb) { | |
| 818 | this.push(i === data.length ? null : data[i++]) | |
| 819 | cb(null) | |
| 820 | } | |
| 821 | }) | |
| 822 | } | |
| 823 | ||
| 824 | static isBackpressured(rs) { | |
| 825 | return ( | |
| 826 | (rs._duplexState & READ_BACKPRESSURE_STATUS) !== 0 || | |
| 827 | rs._readableState.buffered >= rs._readableState.highWaterMark | |
| 828 | ) | |
| 829 | } | |
| 830 | ||
| 831 | static isPaused(rs) { | |
| 832 | return (rs._duplexState & READ_RESUMED) === 0 | |
| 833 | } | |
| 834 | ||
| 835 | [asyncIterator]() { | |
| 836 | const stream = this | |
| 837 | ||
| 838 | let error = null | |
| 839 | let promiseResolve = null | |
| 840 | let promiseReject = null | |
| 841 | ||
| 842 | this.on('error', (err) => { | |
| 843 | error = err | |
| 844 | }) | |
| 845 | this.on('readable', onreadable) | |
| 846 | this.on('close', onclose) | |
| 847 | ||
| 848 | return { | |
| 849 | [asyncIterator]() { | |
| 850 | return this | |
| 851 | }, | |
| 852 | next() { | |
| 853 | return new Promise(function (resolve, reject) { | |
| 854 | promiseResolve = resolve | |
| 855 | promiseReject = reject | |
| 856 | const data = stream.read() | |
| 857 | if (data !== null) ondata(data) | |
| 858 | else if ((stream._duplexState & DESTROYED) !== 0) ondata(null) | |
| 859 | }) | |
| 860 | }, | |
| 861 | return() { | |
| 862 | return destroy(null) | |
| 863 | }, | |
| 864 | throw(err) { | |
| 865 | return destroy(err) | |
| 866 | } | |
| 867 | } | |
| 868 | ||
| 869 | function onreadable() { | |
| 870 | if (promiseResolve !== null) ondata(stream.read()) | |
| 871 | } | |
| 872 | ||
| 873 | function onclose() { | |
| 874 | if (promiseResolve !== null) ondata(null) | |
| 875 | } | |
| 876 | ||
| 877 | function ondata(data) { | |
| 878 | if (promiseReject === null) return | |
| 879 | if (error) promiseReject(error) | |
| 880 | else if (data === null && (stream._duplexState & READ_DONE) === 0) | |
| 881 | promiseReject(STREAM_DESTROYED) | |
| 882 | else promiseResolve({ value: data, done: data === null }) | |
| 883 | promiseReject = promiseResolve = null | |
| 884 | } | |
| 885 | ||
| 886 | function destroy(err) { | |
| 887 | stream.destroy(err) | |
| 888 | return new Promise((resolve, reject) => { | |
| 889 | if (stream._duplexState & DESTROYED) return resolve({ value: undefined, done: true }) | |
| 890 | stream.once('close', function () { | |
| 891 | if (err) reject(err) | |
| 892 | else resolve({ value: undefined, done: true }) | |
| 893 | }) | |
| 894 | }) | |
| 895 | } | |
| 896 | } | |
| 897 | } | |
| 898 | ||
| 899 | class Writable extends Stream { | |
| 900 | constructor(opts) { | |
| 901 | super(opts) | |
| 902 | ||
| 903 | this._duplexState |= OPENING | READ_DONE | |
| 904 | this._writableState = new WritableState(this, opts) | |
| 905 | ||
| 906 | if (opts) { | |
| 907 | if (opts.writev) this._writev = opts.writev | |
| 908 | if (opts.write) this._write = opts.write | |
| 909 | if (opts.final) this._final = opts.final | |
| 910 | if (opts.eagerOpen) this._writableState.updateNextTick() | |
| 911 | } | |
| 912 | } | |
| 913 | ||
| 914 | cork() { | |
| 915 | this._duplexState |= WRITE_CORKED | |
| 916 | } | |
| 917 | ||
| 918 | uncork() { | |
| 919 | this._duplexState &= WRITE_NOT_CORKED | |
| 920 | this._writableState.updateNextTick() | |
| 921 | } | |
| 922 | ||
| 923 | _writev(batch, cb) { | |
| 924 | cb(null) | |
| 925 | } | |
| 926 | ||
| 927 | _write(data, cb) { | |
| 928 | this._writableState.autoBatch(data, cb) | |
| 929 | } | |
| 930 | ||
| 931 | _final(cb) { | |
| 932 | cb(null) | |
| 933 | } | |
| 934 | ||
| 935 | static isBackpressured(ws) { | |
| 936 | return (ws._duplexState & WRITE_BACKPRESSURE_STATUS) !== 0 | |
| 937 | } | |
| 938 | ||
| 939 | static drained(ws) { | |
| 940 | if (ws.destroyed) return Promise.resolve(false) | |
| 941 | const state = ws._writableState | |
| 942 | const pending = isWritev(ws) ? Math.min(1, state.queue.length) : state.queue.length | |
| 943 | const writes = pending + (ws._duplexState & WRITE_WRITING ? 1 : 0) | |
| 944 | if (writes === 0) return Promise.resolve(true) | |
| 945 | if (state.drains === null) state.drains = [] | |
| 946 | return new Promise((resolve) => { | |
| 947 | state.drains.push({ writes, resolve }) | |
| 948 | }) | |
| 949 | } | |
| 950 | ||
| 951 | write(data) { | |
| 952 | this._writableState.updateNextTick() | |
| 953 | return this._writableState.push(data) | |
| 954 | } | |
| 955 | ||
| 956 | end(data) { | |
| 957 | this._writableState.updateNextTick() | |
| 958 | this._writableState.end(data) | |
| 959 | return this | |
| 960 | } | |
| 961 | } | |
| 962 | ||
| 963 | class Duplex extends Readable { | |
| 964 | // and Writable | |
| 965 | constructor(opts) { | |
| 966 | super(opts) | |
| 967 | ||
| 968 | this._duplexState = OPENING | (this._duplexState & READ_READ_AHEAD) | |
| 969 | this._writableState = new WritableState(this, opts) | |
| 970 | ||
| 971 | if (opts) { | |
| 972 | if (opts.writev) this._writev = opts.writev | |
| 973 | if (opts.write) this._write = opts.write | |
| 974 | if (opts.final) this._final = opts.final | |
| 975 | } | |
| 976 | } | |
| 977 | ||
| 978 | cork() { | |
| 979 | this._duplexState |= WRITE_CORKED | |
| 980 | } | |
| 981 | ||
| 982 | uncork() { | |
| 983 | this._duplexState &= WRITE_NOT_CORKED | |
| 984 | this._writableState.updateNextTick() | |
| 985 | } | |
| 986 | ||
| 987 | _writev(batch, cb) { | |
| 988 | cb(null) | |
| 989 | } | |
| 990 | ||
| 991 | _write(data, cb) { | |
| 992 | this._writableState.autoBatch(data, cb) | |
| 993 | } | |
| 994 | ||
| 995 | _final(cb) { | |
| 996 | cb(null) | |
| 997 | } | |
| 998 | ||
| 999 | write(data) { | |
| 1000 | this._writableState.updateNextTick() | |
| 1001 | return this._writableState.push(data) | |
| 1002 | } | |
| 1003 | ||
| 1004 | end(data) { | |
| 1005 | this._writableState.updateNextTick() | |
| 1006 | this._writableState.end(data) | |
| 1007 | return this | |
| 1008 | } | |
| 1009 | } | |
| 1010 | ||
| 1011 | class Transform extends Duplex { | |
| 1012 | constructor(opts) { | |
| 1013 | super(opts) | |
| 1014 | this._transformState = new TransformState(this) | |
| 1015 | ||
| 1016 | if (opts) { | |
| 1017 | if (opts.transform) this._transform = opts.transform | |
| 1018 | if (opts.flush) this._flush = opts.flush | |
| 1019 | } | |
| 1020 | } | |
| 1021 | ||
| 1022 | _write(data, cb) { | |
| 1023 | if (this._readableState.buffered >= this._readableState.highWaterMark) { | |
| 1024 | this._transformState.data = data | |
| 1025 | } else { | |
| 1026 | this._transform(data, this._transformState.afterTransform) | |
| 1027 | } | |
| 1028 | } | |
| 1029 | ||
| 1030 | _read(cb) { | |
| 1031 | if (this._transformState.data !== null) { | |
| 1032 | const data = this._transformState.data | |
| 1033 | this._transformState.data = null | |
| 1034 | cb(null) | |
| 1035 | this._transform(data, this._transformState.afterTransform) | |
| 1036 | } else { | |
| 1037 | cb(null) | |
| 1038 | } | |
| 1039 | } | |
| 1040 | ||
| 1041 | destroy(err) { | |
| 1042 | super.destroy(err) | |
| 1043 | if (this._transformState.data !== null) { | |
| 1044 | this._transformState.data = null | |
| 1045 | this._transformState.afterTransform() | |
| 1046 | } | |
| 1047 | } | |
| 1048 | ||
| 1049 | _transform(data, cb) { | |
| 1050 | cb(null, data) | |
| 1051 | } | |
| 1052 | ||
| 1053 | _flush(cb) { | |
| 1054 | cb(null) | |
| 1055 | } | |
| 1056 | ||
| 1057 | _final(cb) { | |
| 1058 | this._transformState.afterFinal = cb | |
| 1059 | this._flush(transformAfterFlush.bind(this)) | |
| 1060 | } | |
| 1061 | } | |
| 1062 | ||
| 1063 | class PassThrough extends Transform {} | |
| 1064 | ||
| 1065 | function transformAfterFlush(err, data) { | |
| 1066 | const cb = this._transformState.afterFinal | |
| 1067 | if (err) return cb(err) | |
| 1068 | if (data !== null && data !== undefined) this.push(data) | |
| 1069 | this.push(null) | |
| 1070 | cb(null) | |
| 1071 | } | |
| 1072 | ||
| 1073 | function pipelinePromise(...streams) { | |
| 1074 | return new Promise((resolve, reject) => { | |
| 1075 | return pipeline(...streams, (err) => { | |
| 1076 | if (err) return reject(err) | |
| 1077 | resolve() | |
| 1078 | }) | |
| 1079 | }) | |
| 1080 | } | |
| 1081 | ||
| 1082 | function pipeline(stream, ...streams) { | |
| 1083 | const all = Array.isArray(stream) ? [...stream, ...streams] : [stream, ...streams] | |
| 1084 | const done = all.length && typeof all[all.length - 1] === 'function' ? all.pop() : null | |
| 1085 | ||
| 1086 | if (all.length < 2) throw new Error('Pipeline requires at least 2 streams') | |
| 1087 | ||
| 1088 | let src = all[0] | |
| 1089 | let dest = null | |
| 1090 | let error = null | |
| 1091 | ||
| 1092 | for (let i = 1; i < all.length; i++) { | |
| 1093 | dest = all[i] | |
| 1094 | ||
| 1095 | if (isStreamx(src)) { | |
| 1096 | src.pipe(dest, onerror) | |
| 1097 | } else { | |
| 1098 | errorHandle(src, true, i > 1, onerror) | |
| 1099 | src.pipe(dest) | |
| 1100 | } | |
| 1101 | ||
| 1102 | src = dest | |
| 1103 | } | |
| 1104 | ||
| 1105 | if (done) { | |
| 1106 | let fin = false | |
| 1107 | ||
| 1108 | const autoDestroy = | |
| 1109 | isStreamx(dest) || !!(dest._writableState && dest._writableState.autoDestroy) | |
| 1110 | ||
| 1111 | dest.on('error', (err) => { | |
| 1112 | if (error === null) error = err | |
| 1113 | }) | |
| 1114 | ||
| 1115 | dest.on('finish', () => { | |
| 1116 | fin = true | |
| 1117 | if (!autoDestroy) done(error) | |
| 1118 | }) | |
| 1119 | ||
| 1120 | if (autoDestroy) { | |
| 1121 | dest.on('close', () => done(error || (fin ? null : PREMATURE_CLOSE))) | |
| 1122 | } | |
| 1123 | } | |
| 1124 | ||
| 1125 | return dest | |
| 1126 | ||
| 1127 | function errorHandle(s, rd, wr, onerror) { | |
| 1128 | s.on('error', onerror) | |
| 1129 | s.on('close', onclose) | |
| 1130 | ||
| 1131 | function onclose() { | |
| 1132 | if (rd && s._readableState && !s._readableState.ended) return onerror(PREMATURE_CLOSE) | |
| 1133 | if (wr && s._writableState && !s._writableState.ended) return onerror(PREMATURE_CLOSE) | |
| 1134 | } | |
| 1135 | } | |
| 1136 | ||
| 1137 | function onerror(err) { | |
| 1138 | if (!err || error) return | |
| 1139 | error = err | |
| 1140 | ||
| 1141 | for (const s of all) { | |
| 1142 | s.destroy(err) | |
| 1143 | } | |
| 1144 | } | |
| 1145 | } | |
| 1146 | ||
| 1147 | function echo(s) { | |
| 1148 | return s | |
| 1149 | } | |
| 1150 | ||
| 1151 | function isStream(stream) { | |
| 1152 | return !!stream._readableState || !!stream._writableState | |
| 1153 | } | |
| 1154 | ||
| 1155 | function isStreamx(stream) { | |
| 1156 | return typeof stream._duplexState === 'number' && isStream(stream) | |
| 1157 | } | |
| 1158 | ||
| 1159 | function isEnding(stream) { | |
| 1160 | return !!stream._readableState && stream._readableState.ending | |
| 1161 | } | |
| 1162 | ||
| 1163 | function isEnded(stream) { | |
| 1164 | return !!stream._readableState && stream._readableState.ended | |
| 1165 | } | |
| 1166 | ||
| 1167 | function isFinishing(stream) { | |
| 1168 | return !!stream._writableState && stream._writableState.ending | |
| 1169 | } | |
| 1170 | ||
| 1171 | function isFinished(stream) { | |
| 1172 | return !!stream._writableState && stream._writableState.ended | |
| 1173 | } | |
| 1174 | ||
| 1175 | function getStreamError(stream, opts = {}) { | |
| 1176 | const err = | |
| 1177 | (stream._readableState && stream._readableState.error) || | |
| 1178 | (stream._writableState && stream._writableState.error) | |
| 1179 | ||
| 1180 | // avoid implicit errors by default | |
| 1181 | return !opts.all && err === STREAM_DESTROYED ? null : err | |
| 1182 | } | |
| 1183 | ||
| 1184 | function isReadStreamx(stream) { | |
| 1185 | return isStreamx(stream) && stream.readable | |
| 1186 | } | |
| 1187 | ||
| 1188 | function isDisturbed(stream) { | |
| 1189 | return ( | |
| 1190 | (stream._duplexState & OPENING) !== OPENING || | |
| 1191 | (stream._duplexState & DESTROYING) === DESTROYING || | |
| 1192 | (stream._duplexState & ACTIVE_OR_TICKING) !== 0 | |
| 1193 | ) | |
| 1194 | } | |
| 1195 | ||
| 1196 | function isTypedArray(data) { | |
| 1197 | return typeof data === 'object' && data !== null && typeof data.byteLength === 'number' | |
| 1198 | } | |
| 1199 | ||
| 1200 | function defaultByteLength(data) { | |
| 1201 | return isTypedArray(data) ? data.byteLength : 1024 | |
| 1202 | } | |
| 1203 | ||
| 1204 | function noop() {} | |
| 1205 | ||
| 1206 | function abort() { | |
| 1207 | this.destroy(new Error('Stream aborted.')) | |
| 1208 | } | |
| 1209 | ||
| 1210 | function isWritev(s) { | |
| 1211 | return s._writev !== Writable.prototype._writev && s._writev !== Duplex.prototype._writev | |
| 1212 | } | |
| 1213 | ||
| 1214 | module.exports = { | |
| 1215 | pipeline, | |
| 1216 | pipelinePromise, | |
| 1217 | isStream, | |
| 1218 | isStreamx, | |
| 1219 | isEnding, | |
| 1220 | isEnded, | |
| 1221 | isFinishing, | |
| 1222 | isFinished, | |
| 1223 | isDisturbed, | |
| 1224 | getStreamError, | |
| 1225 | Stream, | |
| 1226 | Writable, | |
| 1227 | Readable, | |
| 1228 | Duplex, | |
| 1229 | Transform, | |
| 1230 | // Export PassThrough for compatibility with Node.js core's stream module | |
| 1231 | PassThrough | |
| 1232 | } |
| … | … | @@ -0,0 +1,36 @@ |
| 1 | { | |
| 2 | "name": "streamx", | |
| 3 | "version": "2.25.0", | |
| 4 | "description": "An iteration of the Node.js core streams with a series of improvements", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "events-universal": "^1.0.0", | |
| 8 | "fast-fifo": "^1.3.2", | |
| 9 | "text-decoder": "^1.1.0" | |
| 10 | }, | |
| 11 | "devDependencies": { | |
| 12 | "b4a": "^1.6.6", | |
| 13 | "brittle": "^3.1.1", | |
| 14 | "end-of-stream": "^1.4.4", | |
| 15 | "prettier": "^3.6.2", | |
| 16 | "prettier-config-holepunch": "^2.0.0" | |
| 17 | }, | |
| 18 | "files": [ | |
| 19 | "index.js" | |
| 20 | ], | |
| 21 | "scripts": { | |
| 22 | "format": "prettier --write .", | |
| 23 | "test": "prettier --check . && node test/all.js", | |
| 24 | "test:bare": "bare test/all.js" | |
| 25 | }, | |
| 26 | "repository": { | |
| 27 | "type": "git", | |
| 28 | "url": "https://github.com/mafintosh/streamx.git" | |
| 29 | }, | |
| 30 | "author": "Mathias Buus (@mafintosh)", | |
| 31 | "license": "MIT", | |
| 32 | "bugs": { | |
| 33 | "url": "https://github.com/mafintosh/streamx/issues" | |
| 34 | }, | |
| 35 | "homepage": "https://github.com/mafintosh/streamx" | |
| 36 | } |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | name: Build Status | |
| 2 | on: | |
| 3 | push: | |
| 4 | branches: | |
| 5 | - main | |
| 6 | pull_request: | |
| 7 | branches: | |
| 8 | - main | |
| 9 | jobs: | |
| 10 | build: | |
| 11 | strategy: | |
| 12 | matrix: | |
| 13 | node-version: [lts/*] | |
| 14 | os: [ubuntu-latest, macos-latest, windows-latest] | |
| 15 | runs-on: ${{ matrix.os }} | |
| 16 | steps: | |
| 17 | - uses: actions/checkout@v2 | |
| 18 | - name: Use Node.js ${{ matrix.node-version }} | |
| 19 | uses: actions/setup-node@v2 | |
| 20 | with: | |
| 21 | node-version: ${{ matrix.node-version }} | |
| 22 | - run: npm install | |
| 23 | - run: npm test |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,13 @@ |
| 1 | Copyright 2022 Holepunch Inc | |
| 2 | ||
| 3 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 | you may not use this file except in compliance with the License. | |
| 5 | You may obtain a copy of the License at | |
| 6 | ||
| 7 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | ||
| 9 | Unless required by applicable law or agreed to in writing, software | |
| 10 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 | See the License for the specific language governing permissions and | |
| 13 | limitations under the License. |
| … | … | @@ -0,0 +1,56 @@ |
| 1 | # sub-encoder | |
| 2 | Generate sub encodings for key/value stores | |
| 3 | ||
| 4 | ### Usage | |
| 5 | ```js | |
| 6 | const enc = new SubEncoder() | |
| 7 | const subA = enc.sub('sub-a') | |
| 8 | const subB = enc.sub('sub-b', 'binary') // subs support custom key encodings | |
| 9 | ||
| 10 | await bee.put('k1', 'b') | |
| 11 | await bee.put('a1', 'a1', { keyEncoding: subA }) | |
| 12 | await bee.put('b1', 'b1', { keyEncoding: subB }) | |
| 13 | ||
| 14 | // k1 as normal | |
| 15 | await bee.get('k1') | |
| 16 | // a1 from the sub | |
| 17 | await bee.get('a1', { keyEncoding: subA }) | |
| 18 | ||
| 19 | // also supports read streams | |
| 20 | for await (const node of bee.createReadStream({ keyEncoding: subA })) { | |
| 21 | // Iterates everything in the A sub | |
| 22 | } | |
| 23 | ||
| 24 | // The range options will be encoded properly too | |
| 25 | for await (const node of bee.createReadStream({ lt: 'b2' }, { keyEncoding: subB })) { | |
| 26 | } | |
| 27 | ``` | |
| 28 | ||
| 29 | ### API | |
| 30 | ||
| 31 | #### `const enc = new SubEncoder([prefix, encoding])` | |
| 32 | ||
| 33 | Create a new SubEncoder. Optionally set the initial prefix and encoding. | |
| 34 | ||
| 35 | `prefix` can be string or Buffer. If set, acts as an initial sub instead of starting at the Hyperbee's base level. | |
| 36 | ||
| 37 | #### `subEnc = enc.sub(prefix, [encoding])` | |
| 38 | ||
| 39 | Make a new sub. Returns a SubEncoder, so subs can easily be nested. | |
| 40 | ||
| 41 | `prefix` can be string or Buffer. | |
| 42 | ||
| 43 | #### `buf = enc.encode(key)` | |
| 44 | ||
| 45 | Encode a key. | |
| 46 | ||
| 47 | #### `key = enc.decode(buf)` | |
| 48 | ||
| 49 | Decode a key. | |
| 50 | ||
| 51 | #### `encodedRange = enc.encodeRange(range)` | |
| 52 | ||
| 53 | Encode a range. | |
| 54 | ||
| 55 | ### License | |
| 56 | Apache-2.0 |
| … | … | @@ -0,0 +1,70 @@ |
| 1 | const codecs = require('codecs') | |
| 2 | const b = require('b4a') | |
| 3 | ||
| 4 | const SEP = b.alloc(1) | |
| 5 | const SEP_BUMPED = b.from([0x1]) | |
| 6 | const EMPTY = b.alloc(0) | |
| 7 | ||
| 8 | module.exports = class SubEncoder { | |
| 9 | constructor (prefix, encoding, parent = null) { | |
| 10 | this.userEncoding = codecs(encoding) | |
| 11 | this.prefix = prefix != null ? createPrefix(prefix, parent) : null | |
| 12 | this.lt = this.prefix && b.concat([this.prefix.subarray(0, this.prefix.byteLength - 1), SEP_BUMPED]) | |
| 13 | } | |
| 14 | ||
| 15 | _encodeRangeUser (r) { | |
| 16 | if (this.userEncoding.encodeRange) return this.userEncoding.encodeRange(r) | |
| 17 | ||
| 18 | const res = {} | |
| 19 | if (r.gt != null) res.gt = this.userEncoding.encode(r.gt) | |
| 20 | if (r.gte != null) res.gte = this.userEncoding.encode(r.gte) | |
| 21 | if (r.lte != null) res.lte = this.userEncoding.encode(r.lte) | |
| 22 | if (r.lt != null) res.lt = this.userEncoding.encode(r.lt) | |
| 23 | ||
| 24 | return res | |
| 25 | } | |
| 26 | ||
| 27 | _addPrefix (key) { | |
| 28 | return this.prefix ? b.concat([this.prefix, key]) : key | |
| 29 | } | |
| 30 | ||
| 31 | encode (key) { | |
| 32 | return this._addPrefix(this.userEncoding.encode(key)) | |
| 33 | } | |
| 34 | ||
| 35 | encodeRange (range) { | |
| 36 | const r = this._encodeRangeUser(range) | |
| 37 | ||
| 38 | if (r.gt) r.gt = this._addPrefix(r.gt) | |
| 39 | else if (r.gte) r.gte = this._addPrefix(r.gte) | |
| 40 | else if (this.prefix) r.gte = this.prefix | |
| 41 | ||
| 42 | if (r.lt) r.lt = this._addPrefix(r.lt) | |
| 43 | else if (r.lte) r.lte = this._addPrefix(r.lte) | |
| 44 | else if (this.prefix) r.lt = this.lt | |
| 45 | ||
| 46 | return r | |
| 47 | } | |
| 48 | ||
| 49 | decode (key) { | |
| 50 | return this.userEncoding.decode(this.prefix ? key.subarray(this.prefix.byteLength) : key) | |
| 51 | } | |
| 52 | ||
| 53 | sub (prefix, encoding) { | |
| 54 | return new SubEncoder(prefix || EMPTY, compat(encoding), this.prefix) | |
| 55 | } | |
| 56 | } | |
| 57 | ||
| 58 | function createPrefix (prefix, parent) { | |
| 59 | prefix = typeof prefix === 'string' ? b.from(prefix) : prefix | |
| 60 | ||
| 61 | if (prefix && parent) return b.concat([parent, prefix, SEP]) | |
| 62 | if (prefix) return b.concat([prefix, SEP]) | |
| 63 | if (parent) return b.concat([parent, SEP]) | |
| 64 | return SEP | |
| 65 | } | |
| 66 | ||
| 67 | function compat (enc) { | |
| 68 | if (enc && enc.keyEncoding) return enc.keyEncoding | |
| 69 | return enc | |
| 70 | } |
| … | … | @@ -0,0 +1,37 @@ |
| 1 | { | |
| 2 | "name": "sub-encoder", | |
| 3 | "version": "2.1.3", | |
| 4 | "description": "Generate sub encodings for key/value stores", | |
| 5 | "main": "index.js", | |
| 6 | "scripts": { | |
| 7 | "test": "standard && brittle test/*.js" | |
| 8 | }, | |
| 9 | "repository": { | |
| 10 | "type": "git", | |
| 11 | "url": "git+https://github.com/holepunchto/sub-encoder.git" | |
| 12 | }, | |
| 13 | "keywords": [ | |
| 14 | "kv-store", | |
| 15 | "encoding", | |
| 16 | "hyperbee" | |
| 17 | ], | |
| 18 | "author": "Andrew Osheroff <andrewosh@gmail.com>", | |
| 19 | "license": "Apache-2.0", | |
| 20 | "bugs": { | |
| 21 | "url": "https://github.com/holepunchto/sub-encoder/issues" | |
| 22 | }, | |
| 23 | "homepage": "https://github.com/holepunchto/sub-encoder#readme", | |
| 24 | "devDependencies": { | |
| 25 | "brittle": "^3.1.1", | |
| 26 | "compact-encoding": "^2.12.0", | |
| 27 | "hyperbee": "^2.11.0", | |
| 28 | "hypercore": "^10.3.2", | |
| 29 | "index-encoder": "^3.0.0", | |
| 30 | "random-access-memory": "^6.0.0", | |
| 31 | "standard": "^17.0.0" | |
| 32 | }, | |
| 33 | "dependencies": { | |
| 34 | "b4a": "^1.6.0", | |
| 35 | "codecs": "^3.1.0" | |
| 36 | } | |
| 37 | } |
| … | … | @@ -0,0 +1,281 @@ |
| 1 | const test = require('brittle') | |
| 2 | ||
| 3 | const Hypercore = require('hypercore') | |
| 4 | const Hyperbee = require('hyperbee') | |
| 5 | const IndexEncoder = require('index-encoder') | |
| 6 | const ram = require('random-access-memory') | |
| 7 | const b = require('b4a') | |
| 8 | const cenc = require('compact-encoding') | |
| 9 | ||
| 10 | const SEP = b.alloc(1) | |
| 11 | ||
| 12 | const SubEncoder = require('..') | |
| 13 | ||
| 14 | test('simple sub key encoding', t => { | |
| 15 | const enc = new SubEncoder() | |
| 16 | const sub1 = enc.sub('a') | |
| 17 | const sub2 = sub1.sub('b') | |
| 18 | ||
| 19 | const key = b.alloc(1).fill(1) | |
| 20 | const k1 = enc.encode(key) | |
| 21 | const k2 = sub1.encode(key) | |
| 22 | const k3 = sub2.encode(key) | |
| 23 | ||
| 24 | t.alike(k1, key) | |
| 25 | t.alike(k2, b.concat([b.from('a'), SEP, key])) | |
| 26 | t.alike(k3, b.concat([b.from('a'), SEP, b.from('b'), SEP, key])) | |
| 27 | ||
| 28 | t.alike(key, enc.decode(k1)) | |
| 29 | t.alike(key, sub1.decode(k2)) | |
| 30 | t.alike(key, sub2.decode(k3)) | |
| 31 | }) | |
| 32 | ||
| 33 | test('sub key encoding with hyperbee', async t => { | |
| 34 | const bee = new Hyperbee(new Hypercore(ram)) | |
| 35 | const enc = new SubEncoder() | |
| 36 | ||
| 37 | const sub1 = enc.sub('hello', { keyEncoding: 'utf-8' }) | |
| 38 | const sub2 = enc.sub('world') | |
| 39 | ||
| 40 | await bee.put('a', b.from('a'), { keyEncoding: sub1 }) | |
| 41 | await bee.put('b', b.from('b'), { keyEncoding: sub2 }) | |
| 42 | ||
| 43 | const n1 = await bee.get('a', { keyEncoding: sub1 }) | |
| 44 | const n2 = await bee.get('b', { keyEncoding: sub2 }) | |
| 45 | ||
| 46 | t.is(n1.key, 'a') | |
| 47 | t.alike(n1.value, b.from('a')) | |
| 48 | t.alike(n2.key, b.from('b')) | |
| 49 | t.alike(n2.value, b.from('b')) | |
| 50 | }) | |
| 51 | ||
| 52 | test('sub range encoding with hyperbee', async t => { | |
| 53 | const bee = new Hyperbee(new Hypercore(ram), { valueEncoding: 'utf-8' }) | |
| 54 | ||
| 55 | const enc = new SubEncoder(null, 'utf-8') | |
| 56 | const subA = enc.sub('sub-a', 'utf-8') | |
| 57 | const subB = enc.sub('sub-b', 'utf-8') | |
| 58 | const subC = enc.sub('sub-c', 'utf-8') | |
| 59 | ||
| 60 | await bee.put(enc.encode('d1'), 'd2') | |
| 61 | await bee.put(subA.encode('a1'), 'a1') | |
| 62 | await bee.put(subB.encode('b1'), 'b1') | |
| 63 | await bee.put(subB.encode('b2'), 'b2') | |
| 64 | await bee.put(subB.encode('b3'), 'b3') | |
| 65 | await bee.put(subC.encode('c1'), 'c1') | |
| 66 | ||
| 67 | { | |
| 68 | const range = { lt: 'sub' } | |
| 69 | const nodes = await collect(bee.createReadStream(range, { keyEncoding: enc })) | |
| 70 | t.is(nodes.length, 1) | |
| 71 | t.is(nodes[0].key, 'd1') | |
| 72 | } | |
| 73 | ||
| 74 | { | |
| 75 | const range = {} | |
| 76 | const nodes = await collect(bee.createReadStream(range, { keyEncoding: subA })) | |
| 77 | t.is(nodes.length, 1) | |
| 78 | t.is(nodes[0].key, 'a1') | |
| 79 | } | |
| 80 | ||
| 81 | { | |
| 82 | const range = { gt: 'b1', lt: 'b3' } | |
| 83 | const nodes = await collect(bee.createReadStream(range, { keyEncoding: subB })) | |
| 84 | t.is(nodes.length, 1) | |
| 85 | t.is(nodes[0].key, 'b2') | |
| 86 | } | |
| 87 | }) | |
| 88 | ||
| 89 | test('sub range encoding encodes non-undefined falsy values', async t => { | |
| 90 | const bee = new Hyperbee(new Hypercore(ram), { valueEncoding: 'utf-8' }) | |
| 91 | ||
| 92 | const enc = new SubEncoder() | |
| 93 | ||
| 94 | const preSub = enc.sub('sub-a') | |
| 95 | const keyEncoding = enc.sub('sub-b', cenc.lexint) | |
| 96 | const postSub = enc.sub('sub-c') | |
| 97 | ||
| 98 | await bee.put('what', 'ever', { keyEncoding: preSub }) | |
| 99 | await bee.put('what', 'ever', { keyEncoding: postSub }) | |
| 100 | ||
| 101 | await bee.put(0, 'entry0', { keyEncoding }) | |
| 102 | await bee.put(1, 'entry1', { keyEncoding }) | |
| 103 | ||
| 104 | { | |
| 105 | const range = { gt: 0 } | |
| 106 | const nodes = await collect(bee.createReadStream(range, { keyEncoding })) | |
| 107 | t.is(nodes.length, 1, 'gt works') | |
| 108 | t.is(nodes[0].key, 1) | |
| 109 | } | |
| 110 | ||
| 111 | { | |
| 112 | const range = { lt: 0 } | |
| 113 | const nodes = await collect(bee.createReadStream(range, { keyEncoding })) | |
| 114 | t.is(nodes.length, 0, 'lt works') | |
| 115 | } | |
| 116 | ||
| 117 | { | |
| 118 | const range = { lte: 0 } | |
| 119 | const nodes = await collect(bee.createReadStream(range, { keyEncoding })) | |
| 120 | t.is(nodes.length, 1, 'lte works') | |
| 121 | t.is(nodes[0].key, 0) | |
| 122 | } | |
| 123 | ||
| 124 | { | |
| 125 | // Note: also passes when processed as falsy value | |
| 126 | // would ideally have a value below 0 in the bee, but lexint does not support that | |
| 127 | const range = { gte: 0 } | |
| 128 | const nodes = await collect(bee.createReadStream(range, { keyEncoding })) | |
| 129 | t.is(nodes.length, 2, 'gte works') | |
| 130 | t.is(nodes[0].key, 0) | |
| 131 | } | |
| 132 | }) | |
| 133 | ||
| 134 | test('sub range diff encoding with hyperbee', async t => { | |
| 135 | const bee = new Hyperbee(new Hypercore(ram), { valueEncoding: 'utf-8' }) | |
| 136 | ||
| 137 | const enc = new SubEncoder(null, 'utf-8') | |
| 138 | const subA = enc.sub('sub-a', 'utf-8') | |
| 139 | const subB = enc.sub('sub-b', 'utf-8') | |
| 140 | const subC = enc.sub('sub-c', 'utf-8') | |
| 141 | ||
| 142 | await bee.put(enc.encode('d1'), 'd2') | |
| 143 | await bee.put(subA.encode('a1'), 'a1') | |
| 144 | await bee.put(subB.encode('b1'), 'b1') | |
| 145 | await bee.put(subB.encode('b2'), 'b2') | |
| 146 | await bee.put(subB.encode('b3'), 'b3') | |
| 147 | await bee.put(subC.encode('c1'), 'c1') | |
| 148 | ||
| 149 | { | |
| 150 | const range = { lt: 'sub' } | |
| 151 | const nodes = await collect(bee.createDiffStream(0, range, { keyEncoding: enc })) | |
| 152 | t.is(nodes.length, 1) | |
| 153 | t.is(nodes[0].left.key, 'd1') | |
| 154 | } | |
| 155 | ||
| 156 | { | |
| 157 | const range = {} | |
| 158 | const nodes = await collect(bee.createDiffStream(0, range, { keyEncoding: subA })) | |
| 159 | t.is(nodes.length, 1) | |
| 160 | t.is(nodes[0].left.key, 'a1') | |
| 161 | } | |
| 162 | ||
| 163 | { | |
| 164 | const range = { gt: 'b1', lt: 'b3' } | |
| 165 | const nodes = await collect(bee.createDiffStream(0, range, { keyEncoding: subB })) | |
| 166 | t.is(nodes.length, 1) | |
| 167 | t.is(nodes[0].left.key, 'b2') | |
| 168 | } | |
| 169 | }) | |
| 170 | ||
| 171 | test('supports the empty sub', async t => { | |
| 172 | const bee = new Hyperbee(new Hypercore(ram)) | |
| 173 | const enc = new SubEncoder() | |
| 174 | ||
| 175 | const sub1 = enc.sub('1', 'utf-8') | |
| 176 | const sub2 = enc.sub('2', 'utf-8') | |
| 177 | const sub3 = enc.sub() | |
| 178 | ||
| 179 | await bee.put('', b.from('a'), { keyEncoding: sub1 }) | |
| 180 | await bee.put('', b.from('b'), { keyEncoding: sub2 }) | |
| 181 | await bee.put(b.alloc(1), b.from('c'), { keyEncoding: sub3 }) | |
| 182 | ||
| 183 | const n3 = await collect(bee.createReadStream({ keyEncoding: sub3 })) | |
| 184 | ||
| 185 | t.is(n3.length, 1) | |
| 186 | t.alike(n3[0].key, b.alloc(1)) | |
| 187 | }) | |
| 188 | ||
| 189 | test('supports the empty sub on top of another sub', async t => { | |
| 190 | const bee = new Hyperbee(new Hypercore(ram)) | |
| 191 | const enc = new SubEncoder() | |
| 192 | ||
| 193 | const sub1 = enc.sub('1', 'utf-8') | |
| 194 | const subSubEmpty = sub1.sub() | |
| 195 | const subSub1 = sub1.sub('not empty', 'utf-8') | |
| 196 | ||
| 197 | await bee.put('', 'sub1 Entry', { keyEncoding: sub1 }) | |
| 198 | await bee.put('', 'subSub1 Entry', { keyEncoding: subSub1 }) | |
| 199 | await bee.put(b.alloc(1), 'EmptySubsub entry', { keyEncoding: subSubEmpty }) | |
| 200 | ||
| 201 | const n3 = await collect(bee.createReadStream({ keyEncoding: subSubEmpty })) | |
| 202 | ||
| 203 | t.is(n3.length, 1) | |
| 204 | t.alike(n3[0].key, b.alloc(1)) | |
| 205 | }) | |
| 206 | ||
| 207 | test('empty str is a valid prefix, causing no overlap', async t => { | |
| 208 | const bee = new Hyperbee(new Hypercore(ram)) | |
| 209 | const enc = new SubEncoder() | |
| 210 | ||
| 211 | const sub1 = enc.sub('', 'utf-8') | |
| 212 | const sub2 = enc.sub('other', 'utf-8') | |
| 213 | ||
| 214 | await bee.put('hey', 'ho', { keyEncoding: sub1 }) | |
| 215 | await bee.put('not', 'a part', { keyEncoding: sub2 }) | |
| 216 | const res = await collect(bee.createReadStream({ keyEncoding: sub1 })) | |
| 217 | t.is(res.length, 1) | |
| 218 | t.alike(res[0].key, 'hey') | |
| 219 | }) | |
| 220 | ||
| 221 | test('can read out the empty key in subs', async t => { | |
| 222 | const bee = new Hyperbee(new Hypercore(ram)) | |
| 223 | const enc = new SubEncoder() | |
| 224 | ||
| 225 | const sub1 = enc.sub('1', 'utf-8') | |
| 226 | const sub2 = enc.sub('2', 'utf-8') | |
| 227 | const sub3 = enc.sub('3', 'binary') | |
| 228 | ||
| 229 | await bee.put('', b.from('a'), { keyEncoding: sub1 }) | |
| 230 | await bee.put('', b.from('b'), { keyEncoding: sub2 }) | |
| 231 | await bee.put(b.alloc(1), b.from('c'), { keyEncoding: sub3 }) | |
| 232 | ||
| 233 | const n1 = await collect(bee.createReadStream({ keyEncoding: sub1 })) | |
| 234 | const n2 = await collect(bee.createReadStream({ keyEncoding: sub2 })) | |
| 235 | const n3 = await collect(bee.createReadStream({ keyEncoding: sub3 })) | |
| 236 | ||
| 237 | t.is(n1[0].key, '') | |
| 238 | t.is(n2[0].key, '') | |
| 239 | t.alike(n3[0].key, b.alloc(1)) | |
| 240 | }) | |
| 241 | ||
| 242 | test('sub + index + hyperbee combo', async t => { | |
| 243 | const root = new SubEncoder() | |
| 244 | const enc = { | |
| 245 | keyEncoding: root.sub(b.from([1]), { | |
| 246 | keyEncoding: new IndexEncoder([ | |
| 247 | IndexEncoder.UINT, | |
| 248 | IndexEncoder.STRING | |
| 249 | ]) | |
| 250 | }), | |
| 251 | valueEncoding: 'utf-8' | |
| 252 | } | |
| 253 | const bee = new Hyperbee(new Hypercore(ram)) | |
| 254 | ||
| 255 | await bee.put([1, 'a'], 'a', enc) | |
| 256 | await bee.put([1, 'b'], 'b', enc) | |
| 257 | await bee.put([2, 'aa'], 'aa', enc) | |
| 258 | await bee.put([2, 'bb'], 'bb', enc) | |
| 259 | await bee.put([3, 'aaa'], 'aaa', enc) | |
| 260 | await bee.put([3, 'bbb'], 'bbb', enc) | |
| 261 | ||
| 262 | const expectedKeys = [[2, 'aa'], [2, 'bb']] | |
| 263 | for await (const node of bee.createReadStream({ gt: [1], lt: [3] }, enc)) { | |
| 264 | t.alike(node.key, expectedKeys.shift()) | |
| 265 | } | |
| 266 | t.is(expectedKeys.length, 0) | |
| 267 | }) | |
| 268 | ||
| 269 | test('constructor-specified sub equivalent to calling .sub()', async t => { | |
| 270 | const directSub = new SubEncoder('mysub', 'utf-8') | |
| 271 | const roundaboutSub = (new SubEncoder()).sub('mysub', 'utf-8') | |
| 272 | t.alike(directSub, roundaboutSub) | |
| 273 | }) | |
| 274 | ||
| 275 | async function collect (ite) { | |
| 276 | const res = [] | |
| 277 | for await (const node of ite) { | |
| 278 | res.push(node) | |
| 279 | } | |
| 280 | return res | |
| 281 | } |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2020 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,31 @@ |
| 1 | # teex | |
| 2 | ||
| 3 | Turn a readable stream into multiple readable [streamx streams](https://github.com/mafintosh/streamx). | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install teex | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const tee = require('teex') | |
| 13 | ||
| 14 | // a, b are readable streams that are "clones" | |
| 15 | // of someReadableStream. both streams need to | |
| 16 | // be flowing, for someReadableStream to flow | |
| 17 | ||
| 18 | const [a, b] = tee(someReadableStream) | |
| 19 | ``` | |
| 20 | ||
| 21 | ## API | |
| 22 | ||
| 23 | #### `const streams = tee(s, [howMany])` | |
| 24 | ||
| 25 | Split a readable stream into multiple streams. | |
| 26 | The `howMany` argument indicates how many streams | |
| 27 | to split into and defaults to 2. | |
| 28 | ||
| 29 | ## License | |
| 30 | ||
| 31 | MIT |
| … | … | @@ -0,0 +1,16 @@ |
| 1 | const tee = require('./') | |
| 2 | const { Readable } = require('streamx') | |
| 3 | ||
| 4 | const s = new Readable() | |
| 5 | ||
| 6 | for (let i = 0; i < 1000; i++) { | |
| 7 | s.push(Buffer.alloc(1024)) | |
| 8 | } | |
| 9 | ||
| 10 | const [a, b] = tee(s) | |
| 11 | ||
| 12 | a.on('data', console.log) | |
| 13 | ||
| 14 | setTimeout(function () { | |
| 15 | b.read() | |
| 16 | }, 1000) |
| … | … | @@ -0,0 +1,52 @@ |
| 1 | const { Readable } = require('streamx') | |
| 2 | ||
| 3 | module.exports = function (s, forks = 2) { | |
| 4 | const streams = new Array(forks) | |
| 5 | const status = new Array(forks).fill(true) | |
| 6 | ||
| 7 | let ended = false | |
| 8 | ||
| 9 | for (let i = 0; i < forks; i++) { | |
| 10 | streams[i] = new Readable({ | |
| 11 | read (cb) { | |
| 12 | const check = !status[i] | |
| 13 | status[i] = true | |
| 14 | if (check && allReadable()) s.resume() | |
| 15 | cb(null) | |
| 16 | } | |
| 17 | }) | |
| 18 | } | |
| 19 | ||
| 20 | s.on('end', function () { | |
| 21 | ended = true | |
| 22 | for (const stream of streams) stream.push(null) | |
| 23 | }) | |
| 24 | ||
| 25 | s.on('error', function (err) { | |
| 26 | for (const stream of streams) stream.destroy(err) | |
| 27 | }) | |
| 28 | ||
| 29 | s.on('close', function () { | |
| 30 | if (ended) return | |
| 31 | for (const stream of streams) stream.destroy() | |
| 32 | }) | |
| 33 | ||
| 34 | s.on('data', function (data) { | |
| 35 | let needsPause = false | |
| 36 | for (let i = 0; i < streams.length; i++) { | |
| 37 | if (!(status[i] = streams[i].push(data))) { | |
| 38 | needsPause = true | |
| 39 | } | |
| 40 | } | |
| 41 | if (needsPause) s.pause() | |
| 42 | }) | |
| 43 | ||
| 44 | return streams | |
| 45 | ||
| 46 | function allReadable () { | |
| 47 | for (let j = 0; j < status.length; j++) { | |
| 48 | if (!status[j]) return false | |
| 49 | } | |
| 50 | return true | |
| 51 | } | |
| 52 | } |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | { | |
| 2 | "name": "teex", | |
| 3 | "version": "1.0.1", | |
| 4 | "description": "Turn a readable stream into multiple readable streamx streams", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "streamx": "^2.12.5" | |
| 8 | }, | |
| 9 | "devDependencies": { | |
| 10 | "standard": "^14.3.4", | |
| 11 | "tape": "^5.0.1" | |
| 12 | }, | |
| 13 | "scripts": { | |
| 14 | "test": "standard && tape test.js" | |
| 15 | }, | |
| 16 | "repository": { | |
| 17 | "type": "git", | |
| 18 | "url": "https://github.com/mafintosh/teex.git" | |
| 19 | }, | |
| 20 | "author": "Mathias Buus (@mafintosh)", | |
| 21 | "license": "MIT", | |
| 22 | "bugs": { | |
| 23 | "url": "https://github.com/mafintosh/teex/issues" | |
| 24 | }, | |
| 25 | "homepage": "https://github.com/mafintosh/teex" | |
| 26 | } |
| … | … | @@ -0,0 +1,68 @@ |
| 1 | const tape = require('tape') | |
| 2 | const tee = require('./') | |
| 3 | const { Readable } = require('streamx') | |
| 4 | ||
| 5 | tape('throttled by eachother', function (t) { | |
| 6 | const r = new Readable() | |
| 7 | ||
| 8 | for (let i = 0; i < 1000; i++) { | |
| 9 | r.push(Buffer.alloc(1000)) | |
| 10 | } | |
| 11 | ||
| 12 | const [a, b] = tee(r) | |
| 13 | ||
| 14 | let aTicks = 0 | |
| 15 | ||
| 16 | a.on('data', function (data) { | |
| 17 | aTicks++ | |
| 18 | }) | |
| 19 | ||
| 20 | setTimeout(() => b.read(), 100) | |
| 21 | ||
| 22 | setTimeout(() => { | |
| 23 | t.same(aTicks, 18) | |
| 24 | t.end() | |
| 25 | }, 200) | |
| 26 | }) | |
| 27 | ||
| 28 | tape('does not premature destroy', function (t) { | |
| 29 | const r = new Readable() | |
| 30 | ||
| 31 | const [a, b] = tee(r) | |
| 32 | ||
| 33 | r.push('a') | |
| 34 | r.push('b') | |
| 35 | r.push('c') | |
| 36 | r.push(null) | |
| 37 | ||
| 38 | setTimeout(() => { | |
| 39 | const aSeen = [] | |
| 40 | const bSeen = [] | |
| 41 | ||
| 42 | a.on('data', function (data) { | |
| 43 | aSeen.push(data) | |
| 44 | }) | |
| 45 | a.on('end', function () { | |
| 46 | aSeen.push(null) | |
| 47 | }) | |
| 48 | ||
| 49 | b.on('data', function (data) { | |
| 50 | bSeen.push(data) | |
| 51 | }) | |
| 52 | b.on('end', function () { | |
| 53 | bSeen.push(null) | |
| 54 | }) | |
| 55 | ||
| 56 | let missing = 2 | |
| 57 | a.on('close', onclose) | |
| 58 | b.on('close', onclose) | |
| 59 | ||
| 60 | function onclose () { | |
| 61 | if (--missing === 0) { | |
| 62 | t.same(aSeen, ['a', 'b', 'c', null]) | |
| 63 | t.same(bSeen, ['a', 'b', 'c', null]) | |
| 64 | t.end() | |
| 65 | } | |
| 66 | } | |
| 67 | }, 200) | |
| 68 | }) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,36 @@ |
| 1 | # text-decoder | |
| 2 | ||
| 3 | Streaming text decoder that preserves multibyte Unicode characters. | |
| 4 | ||
| 5 | ``` | |
| 6 | npm i text-decoder | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ```js | |
| 12 | const TextDecoder = require('text-decoder') | |
| 13 | ||
| 14 | const td = new TextDecoder() | |
| 15 | ||
| 16 | td.push(Buffer.of(0xf0, 0x9f, 0x92)) // returns '' | |
| 17 | td.push(Buffer.of(0xa9)) // returns '💩' | |
| 18 | ``` | |
| 19 | ||
| 20 | ## API | |
| 21 | ||
| 22 | #### `const td = new TextDecoder([encoding])` | |
| 23 | ||
| 24 | Create a text decoder. `encoding` defaults to `utf8`. | |
| 25 | ||
| 26 | #### `const string = td.push(data)` | |
| 27 | ||
| 28 | Push either a `Buffer` or a `string` to the text decoder. Returns the decoded string, buffering any partial data. | |
| 29 | ||
| 30 | #### `const string = td.end([data])` | |
| 31 | ||
| 32 | End the decoder, optionally pushing a final piece of data. Returns the decoded string. | |
| 33 | ||
| 34 | ## License | |
| 35 | ||
| 36 | Apache-2.0 |
| … | … | @@ -0,0 +1,64 @@ |
| 1 | const PassThroughDecoder = require('./lib/pass-through-decoder') | |
| 2 | const UTF8Decoder = require('./lib/utf8-decoder') | |
| 3 | ||
| 4 | module.exports = class TextDecoder { | |
| 5 | constructor(encoding = 'utf8') { | |
| 6 | this.encoding = normalizeEncoding(encoding) | |
| 7 | ||
| 8 | switch (this.encoding) { | |
| 9 | case 'utf8': | |
| 10 | this.decoder = new UTF8Decoder() | |
| 11 | break | |
| 12 | case 'utf16le': | |
| 13 | case 'base64': | |
| 14 | throw new Error('Unsupported encoding: ' + this.encoding) | |
| 15 | default: | |
| 16 | this.decoder = new PassThroughDecoder(this.encoding) | |
| 17 | } | |
| 18 | } | |
| 19 | ||
| 20 | get remaining() { | |
| 21 | return this.decoder.remaining | |
| 22 | } | |
| 23 | ||
| 24 | push(data) { | |
| 25 | if (typeof data === 'string') return data | |
| 26 | return this.decoder.decode(data) | |
| 27 | } | |
| 28 | ||
| 29 | // For Node.js compatibility | |
| 30 | write(data) { | |
| 31 | return this.push(data) | |
| 32 | } | |
| 33 | ||
| 34 | end(data) { | |
| 35 | let result = '' | |
| 36 | if (data) result = this.push(data) | |
| 37 | result += this.decoder.flush() | |
| 38 | return result | |
| 39 | } | |
| 40 | } | |
| 41 | ||
| 42 | function normalizeEncoding(encoding) { | |
| 43 | encoding = encoding.toLowerCase() | |
| 44 | ||
| 45 | switch (encoding) { | |
| 46 | case 'utf8': | |
| 47 | case 'utf-8': | |
| 48 | return 'utf8' | |
| 49 | case 'ucs2': | |
| 50 | case 'ucs-2': | |
| 51 | case 'utf16le': | |
| 52 | case 'utf-16le': | |
| 53 | return 'utf16le' | |
| 54 | case 'latin1': | |
| 55 | case 'binary': | |
| 56 | return 'latin1' | |
| 57 | case 'base64': | |
| 58 | case 'ascii': | |
| 59 | case 'hex': | |
| 60 | return encoding | |
| 61 | default: | |
| 62 | throw new Error('Unknown encoding: ' + encoding) | |
| 63 | } | |
| 64 | } |
| … | … | @@ -0,0 +1,19 @@ |
| 1 | const b4a = require('b4a') | |
| 2 | ||
| 3 | module.exports = class PassThroughDecoder { | |
| 4 | constructor(encoding) { | |
| 5 | this.encoding = encoding | |
| 6 | } | |
| 7 | ||
| 8 | get remaining() { | |
| 9 | return 0 | |
| 10 | } | |
| 11 | ||
| 12 | decode(data) { | |
| 13 | return b4a.toString(data, this.encoding) | |
| 14 | } | |
| 15 | ||
| 16 | flush() { | |
| 17 | return '' | |
| 18 | } | |
| 19 | } |
| … | … | @@ -0,0 +1,185 @@ |
| 1 | const b4a = require('b4a') | |
| 2 | ||
| 3 | /** | |
| 4 | * https://encoding.spec.whatwg.org/#utf-8-decoder | |
| 5 | */ | |
| 6 | module.exports = class UTF8Decoder { | |
| 7 | constructor() { | |
| 8 | this._reset() | |
| 9 | } | |
| 10 | ||
| 11 | get remaining() { | |
| 12 | return this.bytesSeen | |
| 13 | } | |
| 14 | ||
| 15 | decode(data) { | |
| 16 | if (data.byteLength === 0) return '' | |
| 17 | ||
| 18 | if (this.bytesNeeded === 0 && trailingIncomplete(data, 0) === 0) { | |
| 19 | this.bytesSeen = trailingBytesSeen(data) | |
| 20 | return b4a.toString(data, 'utf8') | |
| 21 | } | |
| 22 | ||
| 23 | let result = '' | |
| 24 | let start = 0 | |
| 25 | ||
| 26 | if (this.bytesNeeded > 0) { | |
| 27 | while (start < data.byteLength) { | |
| 28 | const byte = data[start] | |
| 29 | ||
| 30 | if (byte < this.lowerBoundary || byte > this.upperBoundary) { | |
| 31 | result += '\ufffd' | |
| 32 | this._reset() | |
| 33 | break | |
| 34 | } | |
| 35 | ||
| 36 | this.lowerBoundary = 0x80 | |
| 37 | this.upperBoundary = 0xbf | |
| 38 | this.codePoint = (this.codePoint << 6) | (byte & 0x3f) | |
| 39 | this.bytesSeen++ | |
| 40 | start++ | |
| 41 | ||
| 42 | if (this.bytesSeen === this.bytesNeeded) { | |
| 43 | result += String.fromCodePoint(this.codePoint) | |
| 44 | this._reset() | |
| 45 | break | |
| 46 | } | |
| 47 | } | |
| 48 | ||
| 49 | if (this.bytesNeeded > 0) return result | |
| 50 | } | |
| 51 | ||
| 52 | const trailing = trailingIncomplete(data, start) | |
| 53 | const end = data.byteLength - trailing | |
| 54 | ||
| 55 | if (end > start) result += b4a.toString(data, 'utf8', start, end) | |
| 56 | ||
| 57 | for (let i = end; i < data.byteLength; i++) { | |
| 58 | const byte = data[i] | |
| 59 | ||
| 60 | if (this.bytesNeeded === 0) { | |
| 61 | if (byte <= 0x7f) { | |
| 62 | this.bytesSeen = 0 | |
| 63 | result += String.fromCharCode(byte) | |
| 64 | } else if (byte >= 0xc2 && byte <= 0xdf) { | |
| 65 | this.bytesNeeded = 2 | |
| 66 | this.bytesSeen = 1 | |
| 67 | this.codePoint = byte & 0x1f | |
| 68 | } else if (byte >= 0xe0 && byte <= 0xef) { | |
| 69 | if (byte === 0xe0) this.lowerBoundary = 0xa0 | |
| 70 | else if (byte === 0xed) this.upperBoundary = 0x9f | |
| 71 | this.bytesNeeded = 3 | |
| 72 | this.bytesSeen = 1 | |
| 73 | this.codePoint = byte & 0xf | |
| 74 | } else if (byte >= 0xf0 && byte <= 0xf4) { | |
| 75 | if (byte === 0xf0) this.lowerBoundary = 0x90 | |
| 76 | else if (byte === 0xf4) this.upperBoundary = 0x8f | |
| 77 | this.bytesNeeded = 4 | |
| 78 | this.bytesSeen = 1 | |
| 79 | this.codePoint = byte & 0x7 | |
| 80 | } else { | |
| 81 | this.bytesSeen = 1 | |
| 82 | result += '\ufffd' | |
| 83 | } | |
| 84 | ||
| 85 | continue | |
| 86 | } | |
| 87 | ||
| 88 | if (byte < this.lowerBoundary || byte > this.upperBoundary) { | |
| 89 | result += '\ufffd' | |
| 90 | i-- | |
| 91 | this._reset() | |
| 92 | ||
| 93 | continue | |
| 94 | } | |
| 95 | ||
| 96 | this.lowerBoundary = 0x80 | |
| 97 | this.upperBoundary = 0xbf | |
| 98 | ||
| 99 | this.codePoint = (this.codePoint << 6) | (byte & 0x3f) | |
| 100 | this.bytesSeen++ | |
| 101 | ||
| 102 | if (this.bytesSeen === this.bytesNeeded) { | |
| 103 | result += String.fromCodePoint(this.codePoint) | |
| 104 | this._reset() | |
| 105 | } | |
| 106 | } | |
| 107 | ||
| 108 | return result | |
| 109 | } | |
| 110 | ||
| 111 | flush() { | |
| 112 | const result = this.bytesNeeded > 0 ? '\ufffd' : '' | |
| 113 | this._reset() | |
| 114 | return result | |
| 115 | } | |
| 116 | ||
| 117 | _reset() { | |
| 118 | this.codePoint = 0 | |
| 119 | this.bytesNeeded = 0 | |
| 120 | this.bytesSeen = 0 | |
| 121 | this.lowerBoundary = 0x80 | |
| 122 | this.upperBoundary = 0xbf | |
| 123 | } | |
| 124 | } | |
| 125 | ||
| 126 | function trailingIncomplete(data, start) { | |
| 127 | const len = data.byteLength | |
| 128 | if (len <= start) return 0 | |
| 129 | ||
| 130 | const limit = Math.max(start, len - 4) | |
| 131 | ||
| 132 | let i = len - 1 | |
| 133 | while (i > limit && (data[i] & 0xc0) === 0x80) i-- | |
| 134 | ||
| 135 | if (i < start) return 0 | |
| 136 | ||
| 137 | const byte = data[i] | |
| 138 | ||
| 139 | let needed | |
| 140 | if (byte <= 0x7f) return 0 | |
| 141 | if (byte >= 0xc2 && byte <= 0xdf) needed = 2 | |
| 142 | else if (byte >= 0xe0 && byte <= 0xef) needed = 3 | |
| 143 | else if (byte >= 0xf0 && byte <= 0xf4) needed = 4 | |
| 144 | else return 0 | |
| 145 | ||
| 146 | const available = len - i | |
| 147 | return available < needed ? available : 0 | |
| 148 | } | |
| 149 | ||
| 150 | function trailingBytesSeen(data) { | |
| 151 | const len = data.byteLength | |
| 152 | if (len === 0) return 0 | |
| 153 | ||
| 154 | const last = data[len - 1] | |
| 155 | ||
| 156 | if (last <= 0x7f) return 0 | |
| 157 | if ((last & 0xc0) !== 0x80) return 1 | |
| 158 | ||
| 159 | const limit = Math.max(0, len - 4) | |
| 160 | ||
| 161 | let i = len - 2 | |
| 162 | while (i >= limit && (data[i] & 0xc0) === 0x80) i-- | |
| 163 | ||
| 164 | if (i < 0) return 1 | |
| 165 | ||
| 166 | const first = data[i] | |
| 167 | ||
| 168 | let needed | |
| 169 | if (first >= 0xc2 && first <= 0xdf) needed = 2 | |
| 170 | else if (first >= 0xe0 && first <= 0xef) needed = 3 | |
| 171 | else if (first >= 0xf0 && first <= 0xf4) needed = 4 | |
| 172 | else return 1 | |
| 173 | ||
| 174 | if (len - i !== needed) return 1 | |
| 175 | ||
| 176 | if (needed >= 3) { | |
| 177 | const second = data[i + 1] | |
| 178 | if (first === 0xe0 && second < 0xa0) return 1 | |
| 179 | if (first === 0xed && second > 0x9f) return 1 | |
| 180 | if (first === 0xf0 && second < 0x90) return 1 | |
| 181 | if (first === 0xf4 && second > 0x8f) return 1 | |
| 182 | } | |
| 183 | ||
| 184 | return 0 | |
| 185 | } |
| … | … | @@ -0,0 +1,39 @@ |
| 1 | { | |
| 2 | "name": "text-decoder", | |
| 3 | "version": "1.2.7", | |
| 4 | "description": "Streaming text decoder that preserves multibyte Unicode characters", | |
| 5 | "exports": { | |
| 6 | "./package": "./package.json", | |
| 7 | ".": "./index.js" | |
| 8 | }, | |
| 9 | "files": [ | |
| 10 | "index.js", | |
| 11 | "lib" | |
| 12 | ], | |
| 13 | "scripts": { | |
| 14 | "format": "prettier --write .", | |
| 15 | "lint": "prettier --check . && lunte", | |
| 16 | "test": "npm run test:node && npm run test:bare", | |
| 17 | "test:node": "brittle-node test.js", | |
| 18 | "test:bare": "brittle-bare test.js" | |
| 19 | }, | |
| 20 | "repository": { | |
| 21 | "type": "git", | |
| 22 | "url": "git+https://github.com/holepunchto/text-decoder.git" | |
| 23 | }, | |
| 24 | "author": "Holepunch", | |
| 25 | "license": "Apache-2.0", | |
| 26 | "bugs": { | |
| 27 | "url": "https://github.com/holepunchto/text-decoder/issues" | |
| 28 | }, | |
| 29 | "homepage": "https://github.com/holepunchto/text-decoder#readme", | |
| 30 | "dependencies": { | |
| 31 | "b4a": "^1.6.4" | |
| 32 | }, | |
| 33 | "devDependencies": { | |
| 34 | "brittle": "^3.3.2", | |
| 35 | "lunte": "^1.3.0", | |
| 36 | "prettier": "^3.6.2", | |
| 37 | "prettier-config-holepunch": "^1.0.0" | |
| 38 | } | |
| 39 | } |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | name: Build Status | |
| 2 | on: | |
| 3 | push: | |
| 4 | branches: | |
| 5 | - master | |
| 6 | pull_request: | |
| 7 | branches: | |
| 8 | - master | |
| 9 | jobs: | |
| 10 | build: | |
| 11 | strategy: | |
| 12 | matrix: | |
| 13 | node-version: [lts/*] | |
| 14 | os: [ubuntu-latest] | |
| 15 | runs-on: ${{ matrix.os }} | |
| 16 | steps: | |
| 17 | - uses: actions/checkout@v2 | |
| 18 | - name: Use Node.js ${{ matrix.node-version }} | |
| 19 | uses: actions/setup-node@v2 | |
| 20 | with: | |
| 21 | node-version: ${{ matrix.node-version }} | |
| 22 | - run: npm install | |
| 23 | - run: npm test |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2018 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,67 @@ |
| 1 | # timeout-refresh | |
| 2 | ||
| 3 | Efficiently refresh a timer | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install timeout-refresh | |
| 7 | ``` | |
| 8 | ||
| 9 | Uses `timeout.refresh` in Node. | |
| 10 | In the browser a basic `clearTimeout + setTimeout` is used since no other method exists | |
| 11 | ||
| 12 | ## Usage | |
| 13 | ||
| 14 | ``` js | |
| 15 | const Timeout = require('timeout-refresh') | |
| 16 | ||
| 17 | const to = Timeout.once(100, function () { | |
| 18 | console.log('Timed out!') | |
| 19 | }) | |
| 20 | ||
| 21 | const i = setInterval(function () { | |
| 22 | // refresh every 50ms | |
| 23 | to.refresh() | |
| 24 | }, 50) | |
| 25 | ||
| 26 | setTimeout(function () { | |
| 27 | // cancel the refresh after 500ms | |
| 28 | clearInterval(i) | |
| 29 | setTimeout(function () { | |
| 30 | console.log('Should have timed out now') | |
| 31 | }, 200) | |
| 32 | }, 500) | |
| 33 | ``` | |
| 34 | ||
| 35 | ## API | |
| 36 | ||
| 37 | #### `to = Timeout.once(ms, ontimeout, [context])` | |
| 38 | ||
| 39 | Make a new refreshable timeout that fires once. | |
| 40 | ||
| 41 | If you pass `context`, it will be set as `this` when calling `ontimeout`. | |
| 42 | ||
| 43 | #### `to = Timeout.on(ms, ontimeout, [context])` | |
| 44 | ||
| 45 | Make a new refreshable timeout that fires every `ms`. | |
| 46 | ||
| 47 | If you pass `context`, it will be set as `this` when calling `ontimeout`. | |
| 48 | ||
| 49 | #### `to.unref()` | |
| 50 | ||
| 51 | Unref the timer. | |
| 52 | ||
| 53 | #### `to.ref()` | |
| 54 | ||
| 55 | Ref the timer. | |
| 56 | ||
| 57 | #### `to.refresh()` | |
| 58 | ||
| 59 | Refresh the timeout. | |
| 60 | ||
| 61 | #### `to.destroy()` | |
| 62 | ||
| 63 | Destroy the timeout. | |
| 64 | ||
| 65 | ## License | |
| 66 | ||
| 67 | MIT |
| … | … | @@ -0,0 +1,54 @@ |
| 1 | module.exports = class TimerBrowser { | |
| 2 | constructor (ms, fn, ctx = null, interval = false) { | |
| 3 | this.ms = ms | |
| 4 | this.ontimeout = fn | |
| 5 | this.context = ctx || null | |
| 6 | this.interval = interval | |
| 7 | this.done = false | |
| 8 | ||
| 9 | this._timer = interval | |
| 10 | ? setInterval(callInterval, ms, this) | |
| 11 | : setTimeout(callTimeout, ms, this) | |
| 12 | } | |
| 13 | ||
| 14 | unref () {} | |
| 15 | ||
| 16 | ref () {} | |
| 17 | ||
| 18 | refresh () { | |
| 19 | if (this.done) return | |
| 20 | ||
| 21 | if (this.interval) { | |
| 22 | clearInterval(this._timer) | |
| 23 | this._timer = setInterval(callInterval, this.ms, this) | |
| 24 | } else { | |
| 25 | clearTimeout(this._timer) | |
| 26 | this._timer = setTimeout(callTimeout, this.ms, this) | |
| 27 | } | |
| 28 | } | |
| 29 | ||
| 30 | destroy () { | |
| 31 | this.done = true | |
| 32 | this.ontimeout = null | |
| 33 | ||
| 34 | if (this.interval) clearInterval(this._timer) | |
| 35 | else clearTimeout(this._timer) | |
| 36 | } | |
| 37 | ||
| 38 | static once (ms, fn, ctx) { | |
| 39 | return new this(ms, fn, ctx, false) | |
| 40 | } | |
| 41 | ||
| 42 | static on (ms, fn, ctx) { | |
| 43 | return new this(ms, fn, ctx, true) | |
| 44 | } | |
| 45 | } | |
| 46 | ||
| 47 | function callTimeout (self) { | |
| 48 | self.done = true | |
| 49 | self.ontimeout.call(self.context) | |
| 50 | } | |
| 51 | ||
| 52 | function callInterval (self) { | |
| 53 | self.ontimeout.call(self.context) | |
| 54 | } |
| … | … | @@ -0,0 +1,19 @@ |
| 1 | const Timeout = require('./') | |
| 2 | ||
| 3 | const to = Timeout.once(100, function () { | |
| 4 | console.log('Timed out!') | |
| 5 | }) | |
| 6 | ||
| 7 | const i = setInterval(function () { | |
| 8 | // refresh every 50ms | |
| 9 | to.refresh() | |
| 10 | }, 50) | |
| 11 | ||
| 12 | setTimeout(function () { | |
| 13 | // cancel the refresh after 500ms | |
| 14 | clearInterval(i) | |
| 15 | console.log('Stopping refresh') | |
| 16 | setTimeout(function () { | |
| 17 | console.log('Should have timed out now') | |
| 18 | }, 200) | |
| 19 | }, 500) |
| … | … | @@ -0,0 +1,9 @@ |
| 1 | module.exports = isNode() | |
| 2 | ? require('./node') | |
| 3 | : require('./browser') | |
| 4 | ||
| 5 | function isNode () { | |
| 6 | const to = setTimeout(function () {}, 1000) | |
| 7 | clearTimeout(to) | |
| 8 | return !!to.refresh | |
| 9 | } |
| … | … | @@ -0,0 +1,49 @@ |
| 1 | module.exports = class Timer { | |
| 2 | constructor (ms, fn, ctx = null, interval = false) { | |
| 3 | this.ms = ms | |
| 4 | this.ontimeout = fn | |
| 5 | this.context = ctx | |
| 6 | this.interval = interval | |
| 7 | this.done = false | |
| 8 | ||
| 9 | this._timer = interval | |
| 10 | ? setInterval(callInterval, ms, this) | |
| 11 | : setTimeout(callTimeout, ms, this) | |
| 12 | } | |
| 13 | ||
| 14 | unref () { | |
| 15 | this._timer.unref() | |
| 16 | } | |
| 17 | ||
| 18 | ref () { | |
| 19 | this._timer.ref() | |
| 20 | } | |
| 21 | ||
| 22 | refresh () { | |
| 23 | if (this.done !== true) this._timer.refresh() | |
| 24 | } | |
| 25 | ||
| 26 | destroy () { | |
| 27 | this.done = true | |
| 28 | this.ontimeout = null | |
| 29 | if (this.interval) clearInterval(this._timer) | |
| 30 | else clearTimeout(this._timer) | |
| 31 | } | |
| 32 | ||
| 33 | static once (ms, fn, ctx) { | |
| 34 | return new this(ms, fn, ctx, false) | |
| 35 | } | |
| 36 | ||
| 37 | static on (ms, fn, ctx) { | |
| 38 | return new this(ms, fn, ctx, true) | |
| 39 | } | |
| 40 | } | |
| 41 | ||
| 42 | function callTimeout (self) { | |
| 43 | self.done = true | |
| 44 | self.ontimeout.call(self.context) | |
| 45 | } | |
| 46 | ||
| 47 | function callInterval (self) { | |
| 48 | self.ontimeout.call(self.context) | |
| 49 | } |
| … | … | @@ -0,0 +1,25 @@ |
| 1 | { | |
| 2 | "name": "timeout-refresh", | |
| 3 | "version": "2.0.1", | |
| 4 | "description": "Efficiently refresh a timer", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": {}, | |
| 7 | "devDependencies": { | |
| 8 | "standard": "^16.0.4", | |
| 9 | "tape": "^5.5.2" | |
| 10 | }, | |
| 11 | "browser": "./browser.js", | |
| 12 | "scripts": { | |
| 13 | "test": "standard && tape test.js" | |
| 14 | }, | |
| 15 | "repository": { | |
| 16 | "type": "git", | |
| 17 | "url": "https://github.com/mafintosh/timeout-refresh.git" | |
| 18 | }, | |
| 19 | "author": "Mathias Buus (@mafintosh)", | |
| 20 | "license": "MIT", | |
| 21 | "bugs": { | |
| 22 | "url": "https://github.com/mafintosh/timeout-refresh/issues" | |
| 23 | }, | |
| 24 | "homepage": "https://github.com/mafintosh/timeout-refresh" | |
| 25 | } |
| … | … | @@ -0,0 +1,102 @@ |
| 1 | const tape = require('tape') | |
| 2 | ||
| 3 | run('', require('./')) | |
| 4 | run('browser: ', require('./browser')) | |
| 5 | ||
| 6 | function run (prefix, Timeout) { | |
| 7 | tape(prefix + 'refresh', function (t) { | |
| 8 | let refreshing = true | |
| 9 | let timedout = false | |
| 10 | ||
| 11 | const ctx = {} | |
| 12 | const to = Timeout.once(100, function () { | |
| 13 | t.ok(ctx === this) | |
| 14 | t.ok(!refreshing) | |
| 15 | timedout = true | |
| 16 | }, ctx) | |
| 17 | ||
| 18 | const i = setInterval(function () { | |
| 19 | to.refresh() | |
| 20 | }, 50) | |
| 21 | ||
| 22 | setTimeout(function () { | |
| 23 | refreshing = false | |
| 24 | clearInterval(i) | |
| 25 | setTimeout(function () { | |
| 26 | t.ok(timedout) | |
| 27 | t.end() | |
| 28 | }, 100) | |
| 29 | }, 500) | |
| 30 | }) | |
| 31 | ||
| 32 | tape(prefix + 'destroy', function (t) { | |
| 33 | let timedout = false | |
| 34 | ||
| 35 | const to = Timeout.once(100, function () { | |
| 36 | t.fail('should be destroyed') | |
| 37 | timedout = true | |
| 38 | }) | |
| 39 | ||
| 40 | const i = setInterval(function () { | |
| 41 | to.refresh() | |
| 42 | }, 50) | |
| 43 | ||
| 44 | setTimeout(function () { | |
| 45 | clearInterval(i) | |
| 46 | to.destroy() | |
| 47 | setTimeout(function () { | |
| 48 | t.ok(!timedout) | |
| 49 | t.end() | |
| 50 | }, 100) | |
| 51 | }, 500) | |
| 52 | }) | |
| 53 | ||
| 54 | tape(prefix + 'cannot be refreshed after call', function (t) { | |
| 55 | t.plan(2) | |
| 56 | ||
| 57 | let timedout = false | |
| 58 | ||
| 59 | const to = Timeout.once(50, function () { | |
| 60 | t.notOk(timedout, 'did not already timeout') | |
| 61 | t.pass('should be destroyed') | |
| 62 | to.refresh() | |
| 63 | timedout = true | |
| 64 | }) | |
| 65 | ||
| 66 | setTimeout(function () { | |
| 67 | t.end() | |
| 68 | }, 500) | |
| 69 | }) | |
| 70 | ||
| 71 | tape(prefix + 'setInterval', function (t) { | |
| 72 | t.plan(1) | |
| 73 | ||
| 74 | const actual = [] | |
| 75 | let tick = 0 | |
| 76 | ||
| 77 | const i = setInterval(function () { | |
| 78 | tick++ | |
| 79 | }, 100) | |
| 80 | ||
| 81 | const to = Timeout.on(500, function () { | |
| 82 | actual.push(tick) | |
| 83 | ||
| 84 | if (actual.length === 2) { | |
| 85 | setTimeout(() => { | |
| 86 | to.refresh() | |
| 87 | setTimeout(() => { | |
| 88 | to.refresh() | |
| 89 | }, 250) | |
| 90 | }, 250) | |
| 91 | } | |
| 92 | ||
| 93 | if (actual.length === 4) { | |
| 94 | to.destroy() | |
| 95 | clearInterval(i) | |
| 96 | ||
| 97 | t.same(actual.map(n => Math.round(n / 5)), [1, 2, 4, 5]) | |
| 98 | t.end() | |
| 99 | } | |
| 100 | }) | |
| 101 | }) | |
| 102 | } |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | name: Build Status | |
| 2 | on: | |
| 3 | push: | |
| 4 | branches: | |
| 5 | - master | |
| 6 | pull_request: | |
| 7 | branches: | |
| 8 | - master | |
| 9 | jobs: | |
| 10 | build: | |
| 11 | strategy: | |
| 12 | matrix: | |
| 13 | node-version: [lts/*] | |
| 14 | os: [ubuntu-latest, macos-latest, windows-latest] | |
| 15 | runs-on: ${{ matrix.os }} | |
| 16 | steps: | |
| 17 | - uses: actions/checkout@v2 | |
| 18 | - name: Use Node.js ${{ matrix.node-version }} | |
| 19 | uses: actions/setup-node@v2 | |
| 20 | with: | |
| 21 | node-version: ${{ matrix.node-version }} | |
| 22 | - run: npm install | |
| 23 | - run: npm test |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2021 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,27 @@ |
| 1 | # unix-path-resolve | |
| 2 | ||
| 3 | Cross platform resolve that always returns a UNIX style `/` seperated path | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install unix-path-resolve | |
| 7 | ``` | |
| 8 | ||
| 9 | Mostly useful for resolving modules cross platform | |
| 10 | ||
| 11 | ## Usage | |
| 12 | ||
| 13 | ``` js | |
| 14 | const resolve = require('unix-path-resolve') | |
| 15 | ||
| 16 | resolve('/foo/bar', '../baz') // /foo/baz | |
| 17 | resolve('/foo/bar', '/baz/foo') // /baz/foo | |
| 18 | resolve('/a/b/c', '../../../../d') // throws since its out of bounds | |
| 19 | resolve('a', 'b') // throws since none of them are absolute | |
| 20 | resolve('/a/b/c', '..\\d') // /a/b/d | |
| 21 | resolve('/a/b/c', 'c:\\foo\\bar') // /foo/bar | |
| 22 | resolve('file:///a/b', './c') // /a/b/c | |
| 23 | ``` | |
| 24 | ||
| 25 | ## License | |
| 26 | ||
| 27 | MIT |
| … | … | @@ -0,0 +1,68 @@ |
| 1 | module.exports = resolve | |
| 2 | ||
| 3 | function parse (addr) { | |
| 4 | const names = addr.split(/[/\\]/) | |
| 5 | ||
| 6 | const r = { | |
| 7 | isAbsolute: false, | |
| 8 | names | |
| 9 | } | |
| 10 | ||
| 11 | // don't think this ever happens, but whatevs | |
| 12 | if (names.length === 0) return r | |
| 13 | ||
| 14 | if (names.length > 1 && names[0].endsWith(':')) { | |
| 15 | r.isAbsolute = true | |
| 16 | ||
| 17 | if (names[0].length === 2) { // windows | |
| 18 | r.names = names.slice(1) | |
| 19 | return r | |
| 20 | } | |
| 21 | ||
| 22 | if (names[0] === 'file:') { | |
| 23 | r.names = names.slice(1) | |
| 24 | return r | |
| 25 | } | |
| 26 | ||
| 27 | r.names = names.slice(3) | |
| 28 | return r | |
| 29 | } | |
| 30 | ||
| 31 | r.isAbsolute = addr.startsWith('/') || addr.startsWith('\\') | |
| 32 | ||
| 33 | return r | |
| 34 | } | |
| 35 | ||
| 36 | function resolve (a, b = '') { | |
| 37 | const ap = parse(a) | |
| 38 | const bp = parse(b) | |
| 39 | ||
| 40 | if (bp.isAbsolute) { | |
| 41 | return resolveNames([], bp.names) | |
| 42 | } | |
| 43 | ||
| 44 | if (!ap.isAbsolute) { | |
| 45 | throw new Error('One of the two paths must be absolute') | |
| 46 | } | |
| 47 | ||
| 48 | return resolveNames(ap.names, bp.names) | |
| 49 | } | |
| 50 | ||
| 51 | function toString (p, names) { | |
| 52 | for (let i = 0; i < names.length; i++) { | |
| 53 | if (names[i] === '') continue | |
| 54 | if (names[i] === '.') continue | |
| 55 | if (names[i] === '..') { | |
| 56 | if (p.length === 1) throw new Error('Path cannot be resolved, too many \'..\'') | |
| 57 | p = p.slice(0, p.lastIndexOf('/')) || '/' | |
| 58 | continue | |
| 59 | } | |
| 60 | p += (p.length === 1 ? names[i] : '/' + names[i]) | |
| 61 | } | |
| 62 | ||
| 63 | return p | |
| 64 | } | |
| 65 | ||
| 66 | function resolveNames (a, b) { | |
| 67 | return toString(toString('/', a), b) | |
| 68 | } |
| … | … | @@ -0,0 +1,24 @@ |
| 1 | { | |
| 2 | "name": "unix-path-resolve", | |
| 3 | "version": "1.0.2", | |
| 4 | "description": "Cross platform resolve that always returns a UNIX style `/` seperated path", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": {}, | |
| 7 | "devDependencies": { | |
| 8 | "brittle": "^2.0.1", | |
| 9 | "standard": "^16.0.4" | |
| 10 | }, | |
| 11 | "scripts": { | |
| 12 | "test": "standard && brittle test.js" | |
| 13 | }, | |
| 14 | "repository": { | |
| 15 | "type": "git", | |
| 16 | "url": "https://github.com/mafintosh/unix-path-resolve.git" | |
| 17 | }, | |
| 18 | "author": "Mathias Buus (@mafintosh)", | |
| 19 | "license": "MIT", | |
| 20 | "bugs": { | |
| 21 | "url": "https://github.com/mafintosh/unix-path-resolve/issues" | |
| 22 | }, | |
| 23 | "homepage": "https://github.com/mafintosh/unix-path-resolve" | |
| 24 | } |
| … | … | @@ -0,0 +1,25 @@ |
| 1 | const test = require('brittle') | |
| 2 | const resolve = require('./') | |
| 3 | ||
| 4 | test('normalises', function (t) { | |
| 5 | t.is(resolve('/a/b'), '/a/b') | |
| 6 | t.is(resolve('/a/b/..'), '/a') | |
| 7 | t.is(resolve('/a/b/../..'), '/') | |
| 8 | t.is(resolve('/a/b/././////c'), '/a/b/c') | |
| 9 | t.is(resolve('/a/b/.\\.\\//\\/c/..///d'), '/a/b/d') | |
| 10 | t.is(resolve('file:/a/b/../../c'), '/c') | |
| 11 | t.is(resolve('file:///a/b/../../c'), '/c') | |
| 12 | t.is(resolve('file:///a/b/../../c'), '/c') | |
| 13 | t.is(resolve('proto://host/a/b/../../c'), '/c') | |
| 14 | t.is(resolve('c:\\dir\\file'), '/dir/file') | |
| 15 | t.is(resolve('\\dir'), '/dir') | |
| 16 | t.exception(() => resolve('/..')) | |
| 17 | }) | |
| 18 | ||
| 19 | test('resolve two paths', function (t) { | |
| 20 | t.is(resolve('/a/b', 'c/d'), '/a/b/c/d') | |
| 21 | t.is(resolve('/a/b', '../c/d'), '/a/c/d') | |
| 22 | t.is(resolve('/', '\\c\\d/e'), '/c/d/e') | |
| 23 | t.is(resolve('/a', '/b'), '/b') | |
| 24 | t.exception(() => resolve('a', 'b')) | |
| 25 | }) |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | # unslab | |
| 2 | ||
| 3 | Unslab some slab'ed buffers | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install unslab | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const unslab = require('unslab') | |
| 13 | ||
| 14 | // the buf returned unpools the buffer passed from the memory slab | |
| 15 | const buf = unslab(Buffer.from('hello world')) | |
| 16 | ||
| 17 | // can do multiple at the same time if you use them together. only does a single alloc in this case | |
| 18 | const [a, b] = unslab([Buffer.from('hello'), Buffer.from('world')]) | |
| 19 | ``` | |
| 20 | ||
| 21 | ## License | |
| 22 | ||
| 23 | Apache-2.0 |
| … | … | @@ -0,0 +1,42 @@ |
| 1 | const b4a = require('b4a') | |
| 2 | ||
| 3 | unslab.all = all | |
| 4 | unslab.is = is | |
| 5 | ||
| 6 | module.exports = unslab | |
| 7 | ||
| 8 | function unslab (buf) { | |
| 9 | if (buf === null || buf.buffer.byteLength === buf.byteLength) return buf | |
| 10 | const copy = b4a.allocUnsafeSlow(buf.byteLength) | |
| 11 | copy.set(buf, 0) | |
| 12 | return copy | |
| 13 | } | |
| 14 | ||
| 15 | function is (buf) { | |
| 16 | return buf.buffer.byteLength !== buf.byteLength | |
| 17 | } | |
| 18 | ||
| 19 | function all (list) { | |
| 20 | let size = 0 | |
| 21 | for (let i = 0; i < list.length; i++) { | |
| 22 | const buf = list[i] | |
| 23 | size += buf === null || buf.buffer.byteLength === buf.byteLength ? 0 : buf.byteLength | |
| 24 | } | |
| 25 | ||
| 26 | const copy = b4a.allocUnsafeSlow(size) | |
| 27 | const result = new Array(list.length) | |
| 28 | ||
| 29 | let offset = 0 | |
| 30 | for (let i = 0; i < list.length; i++) { | |
| 31 | let buf = list[i] | |
| 32 | ||
| 33 | if (buf !== null && buf.buffer.byteLength !== buf.byteLength) { | |
| 34 | copy.set(buf, offset) | |
| 35 | buf = copy.subarray(offset, offset += buf.byteLength) | |
| 36 | } | |
| 37 | ||
| 38 | result[i] = buf | |
| 39 | } | |
| 40 | ||
| 41 | return result | |
| 42 | } |
| … | … | @@ -0,0 +1,29 @@ |
| 1 | { | |
| 2 | "name": "unslab", | |
| 3 | "version": "1.3.0", | |
| 4 | "description": "Unslab some slab'ed buffers", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js" | |
| 8 | ], | |
| 9 | "dependencies": { | |
| 10 | "b4a": "^1.6.6" | |
| 11 | }, | |
| 12 | "devDependencies": { | |
| 13 | "brittle": "^3.5.2", | |
| 14 | "standard": "^17.1.0" | |
| 15 | }, | |
| 16 | "scripts": { | |
| 17 | "test": "standard && brittle test.js" | |
| 18 | }, | |
| 19 | "repository": { | |
| 20 | "type": "git", | |
| 21 | "url": "https://github.com/holepunchto/unslab.git" | |
| 22 | }, | |
| 23 | "author": "Holepunch", | |
| 24 | "license": "Apache-2.0", | |
| 25 | "bugs": { | |
| 26 | "url": "https://github.com/holepunchto/unslab/issues" | |
| 27 | }, | |
| 28 | "homepage": "https://github.com/holepunchto/unslab" | |
| 29 | } |
| … | … | @@ -0,0 +1,53 @@ |
| 1 | # varint | |
| 2 | ||
| 3 | encode whole numbers to an array of [protobuf-style varint bytes](https://developers.google.com/protocol-buffers/docs/encoding#varints) and also decode them. | |
| 4 | ||
| 5 | ```javascript | |
| 6 | var varint = require('varint') | |
| 7 | ||
| 8 | var bytes = varint.encode(300) // === [0xAC, 0x02] | |
| 9 | varint.decode(bytes) // 300 | |
| 10 | varint.decode.bytes // 2 (the last decode() call required 2 bytes) | |
| 11 | ``` | |
| 12 | ||
| 13 | ## api | |
| 14 | ||
| 15 | ### varint = require('varint') | |
| 16 | ||
| 17 | ### varint.encode(num[, buffer=[], offset=0]) -> buffer | |
| 18 | ||
| 19 | Encodes `num` into `buffer` starting at `offset`. returns `buffer`, with the encoded varint written into it. If `buffer` is not provided, it will default to a new array. | |
| 20 | ||
| 21 | `varint.encode.bytes` will now be set to the number of bytes | |
| 22 | modified. | |
| 23 | ||
| 24 | ### varint.decode(data[, offset=0]) -> number | |
| 25 | ||
| 26 | decodes `data`, which can be either a buffer or array of integers, from position `offset` or default 0 and returns the decoded original integer. | |
| 27 | ||
| 28 | Throws a `RangeError` when `data` does not represent a valid encoding. | |
| 29 | ||
| 30 | ### varint.decode.bytes | |
| 31 | ||
| 32 | if you also require the length (number of bytes) that were required to decode the integer you can access it via `varint.decode.bytes`. this is an integer property that will tell you the number of bytes that the last .decode() call had to use to decode. | |
| 33 | ||
| 34 | ### varint.encode.bytes | |
| 35 | ||
| 36 | similar to `decode.bytes` when encoding a number it can be useful to know how many bytes where written (especially if you pass an output array). you can access this via `varint.encode.bytes` which holds the number of bytes written in the last encode. | |
| 37 | ||
| 38 | ||
| 39 | ### varint.encodingLength(num) | |
| 40 | ||
| 41 | returns the number of bytes this number will be encoded as, up to a maximum of 8. | |
| 42 | ||
| 43 | ## usage notes | |
| 44 | ||
| 45 | If varint is passed a buffer that does not contain a valid end | |
| 46 | byte, then `decode` will throw `RangeError`, and `decode.bytes` | |
| 47 | will be set to 0. If you are reading from a streaming source, | |
| 48 | it's okay to pass an incomplete buffer into `decode`, detect this | |
| 49 | case, and then concatenate the next buffer. | |
| 50 | ||
| 51 | # License | |
| 52 | ||
| 53 | MIT |
| … | … | @@ -0,0 +1,57 @@ |
| 1 | var N = 1e7 | |
| 2 | var M = 10 | |
| 3 | /* | |
| 4 | benchmark encoding and decoding N random integers. | |
| 5 | ||
| 6 | A number is encoded into a buffer, (the buffer is reused so | |
| 7 | that allocation does not affect the benchmark) | |
| 8 | ||
| 9 | to test the effect on performance of invalid records | |
| 10 | (i.e. too short, with the Most Significant Byte missing) | |
| 11 | every M items, attempt to decode from a shorter slice of the buffer. | |
| 12 | This will probably be produce an invalid result. We do not | |
| 13 | need to write into that buffer - because it refurs to the same memory as | |
| 14 | the full size buffer. | |
| 15 | ||
| 16 | run with INVALID=1 to include N/M invalid decodes. | |
| 17 | ||
| 18 | results: | |
| 19 | with no invalid decodes, I get about 2428 decodes/ms | |
| 20 | with invalid decodes: | |
| 21 | old code that overruns buffer: 1122 decodes/ms | |
| 22 | check length & return undefined: 2439 decodecs/ms | |
| 23 | check length & return NaN: 2434 d/ms | |
| 24 | check length & return -1: 2400 d/ms | |
| 25 | ||
| 26 | conclusion, it doesn't make a significant difference whether | |
| 27 | what is returned to show an invalid read, | |
| 28 | but if you overrun the buffer the cost is considerable. | |
| 29 | ||
| 30 | recomendation: return undefined | |
| 31 | */ | |
| 32 | ||
| 33 | var buffer = new Buffer(8) | |
| 34 | var _buffer = buffer.slice(0, 4) | |
| 35 | var varint = require('./') | |
| 36 | var l = N | |
| 37 | var invalid = 0 | |
| 38 | ||
| 39 | includeInvalid = !!process.env.INVALID | |
| 40 | ||
| 41 | var start = Date.now() | |
| 42 | while (l--) { | |
| 43 | var int = Math.floor(Math.random()*0x01fffffffffffff) | |
| 44 | varint.encode(int, buffer, 0) | |
| 45 | //console.log(int, varint.decode(buffer, 0)) | |
| 46 | //every 1000 varints, do one that will be too short, | |
| 47 | //measure | |
| 48 | if(includeInvalid && !(l%M)) { | |
| 49 | if(undefined == varint.decode(_buffer, 0)) | |
| 50 | invalid ++ | |
| 51 | } else | |
| 52 | if(int !== varint.decode(buffer, 0)) | |
| 53 | throw new Error('decode was incorrect') | |
| 54 | } | |
| 55 | ||
| 56 | console.log('decode&encode/ms, invalidDecodes') | |
| 57 | console.log(N/(Date.now() - start) + ',', invalid) |
| … | … | @@ -0,0 +1,29 @@ |
| 1 | module.exports = read | |
| 2 | ||
| 3 | var MSB = 0x80 | |
| 4 | , REST = 0x7F | |
| 5 | ||
| 6 | function read(buf, offset) { | |
| 7 | var res = 0 | |
| 8 | , offset = offset || 0 | |
| 9 | , shift = 0 | |
| 10 | , counter = offset | |
| 11 | , b | |
| 12 | , l = buf.length | |
| 13 | ||
| 14 | do { | |
| 15 | if (counter >= l) { | |
| 16 | read.bytes = 0 | |
| 17 | throw new RangeError('Could not decode varint') | |
| 18 | } | |
| 19 | b = buf[counter++] | |
| 20 | res += shift < 28 | |
| 21 | ? (b & REST) << shift | |
| 22 | : (b & REST) * Math.pow(2, shift) | |
| 23 | shift += 7 | |
| 24 | } while (b >= MSB) | |
| 25 | ||
| 26 | read.bytes = counter - offset | |
| 27 | ||
| 28 | return res | |
| 29 | } |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | module.exports = encode | |
| 2 | ||
| 3 | var MSB = 0x80 | |
| 4 | , REST = 0x7F | |
| 5 | , MSBALL = ~REST | |
| 6 | , INT = Math.pow(2, 31) | |
| 7 | ||
| 8 | function encode(num, out, offset) { | |
| 9 | out = out || [] | |
| 10 | offset = offset || 0 | |
| 11 | var oldOffset = offset | |
| 12 | ||
| 13 | while(num >= INT) { | |
| 14 | out[offset++] = (num & 0xFF) | MSB | |
| 15 | num /= 128 | |
| 16 | } | |
| 17 | while(num & MSBALL) { | |
| 18 | out[offset++] = (num & 0xFF) | MSB | |
| 19 | num >>>= 7 | |
| 20 | } | |
| 21 | out[offset] = num | 0 | |
| 22 | ||
| 23 | encode.bytes = offset - oldOffset + 1 | |
| 24 | ||
| 25 | return out | |
| 26 | } |
| … | … | @@ -0,0 +1,5 @@ |
| 1 | module.exports = { | |
| 2 | encode: require('./encode.js') | |
| 3 | , decode: require('./decode.js') | |
| 4 | , encodingLength: require('./length.js') | |
| 5 | } |
| … | … | @@ -0,0 +1,25 @@ |
| 1 | ||
| 2 | var N1 = Math.pow(2, 7) | |
| 3 | var N2 = Math.pow(2, 14) | |
| 4 | var N3 = Math.pow(2, 21) | |
| 5 | var N4 = Math.pow(2, 28) | |
| 6 | var N5 = Math.pow(2, 35) | |
| 7 | var N6 = Math.pow(2, 42) | |
| 8 | var N7 = Math.pow(2, 49) | |
| 9 | var N8 = Math.pow(2, 56) | |
| 10 | var N9 = Math.pow(2, 63) | |
| 11 | ||
| 12 | module.exports = function (value) { | |
| 13 | return ( | |
| 14 | value < N1 ? 1 | |
| 15 | : value < N2 ? 2 | |
| 16 | : value < N3 ? 3 | |
| 17 | : value < N4 ? 4 | |
| 18 | : value < N5 ? 5 | |
| 19 | : value < N6 ? 6 | |
| 20 | : value < N7 ? 7 | |
| 21 | : value < N8 ? 8 | |
| 22 | : value < N9 ? 9 | |
| 23 | : 10 | |
| 24 | ) | |
| 25 | } |
| … | … | @@ -0,0 +1,24 @@ |
| 1 | { | |
| 2 | "name": "varint", | |
| 3 | "version": "5.0.0", | |
| 4 | "description": "protobuf-style varint bytes - use msb to create integer values of varying sizes", | |
| 5 | "main": "index.js", | |
| 6 | "scripts": { | |
| 7 | "test": "node test.js" | |
| 8 | }, | |
| 9 | "repository": { | |
| 10 | "type": "git", | |
| 11 | "url": "git://github.com/chrisdickinson/varint.git" | |
| 12 | }, | |
| 13 | "keywords": [ | |
| 14 | "varint", | |
| 15 | "protobuf", | |
| 16 | "encode", | |
| 17 | "decode" | |
| 18 | ], | |
| 19 | "author": "Chris Dickinson <chris@neversaw.us>", | |
| 20 | "license": "MIT", | |
| 21 | "devDependencies": { | |
| 22 | "tape": "~2.12.3" | |
| 23 | } | |
| 24 | } |
| … | … | @@ -0,0 +1,139 @@ |
| 1 | var varint = require('./index') | |
| 2 | , test = require('tape') | |
| 3 | , decode = varint.decode | |
| 4 | , encode = varint.encode | |
| 5 | , encodingLength = varint.encodingLength | |
| 6 | ||
| 7 | test('fuzz test', function(assert) { | |
| 8 | var expect | |
| 9 | , encoded | |
| 10 | ||
| 11 | for(var i = 0, len = 100; i < len; ++i) { | |
| 12 | expect = randint(0x7FFFFFFF) | |
| 13 | encoded = encode(expect) | |
| 14 | var data = decode(encoded) | |
| 15 | assert.equal(expect, data, 'fuzz test: ' + expect.toString()) | |
| 16 | assert.equal(decode.bytes, encoded.length) | |
| 17 | } | |
| 18 | ||
| 19 | assert.end() | |
| 20 | }) | |
| 21 | ||
| 22 | test('test single byte works as expected', function(assert) { | |
| 23 | var buf = new Uint8Array(2) | |
| 24 | buf[0] = 172 | |
| 25 | buf[1] = 2 | |
| 26 | var data = decode(buf) | |
| 27 | assert.equal(data, 300, 'should equal 300') | |
| 28 | assert.equal(decode.bytes, 2) | |
| 29 | assert.end() | |
| 30 | }) | |
| 31 | ||
| 32 | test('test encode works as expected', function(assert) { | |
| 33 | var out = [] | |
| 34 | ||
| 35 | assert.deepEqual(encode(300), [0xAC, 0x02]) | |
| 36 | ||
| 37 | assert.end() | |
| 38 | }) | |
| 39 | ||
| 40 | test('test decode single bytes', function(assert) { | |
| 41 | var expected = randint(parseInt('1111111', '2')) | |
| 42 | var buf = new Uint8Array(1) | |
| 43 | buf[0] = expected | |
| 44 | var data = decode(buf) | |
| 45 | assert.equal(data, expected) | |
| 46 | assert.equal(decode.bytes, 1) | |
| 47 | assert.end() | |
| 48 | }) | |
| 49 | ||
| 50 | test('test decode multiple bytes with zero', function(assert) { | |
| 51 | var expected = randint(parseInt('1111111', '2')) | |
| 52 | var buf = new Uint8Array(2) | |
| 53 | buf[0] = 128 | |
| 54 | buf[1] = expected | |
| 55 | var data = decode(buf) | |
| 56 | assert.equal(data, expected << 7) | |
| 57 | assert.equal(decode.bytes, 2) | |
| 58 | assert.end() | |
| 59 | }) | |
| 60 | ||
| 61 | test('encode single byte', function(assert) { | |
| 62 | var expected = randint(parseInt('1111111', '2')) | |
| 63 | assert.deepEqual(encode(expected), [expected]) | |
| 64 | assert.equal(encode.bytes, 1) | |
| 65 | assert.end() | |
| 66 | }) | |
| 67 | ||
| 68 | test('encode multiple byte with zero first byte', function(assert) { | |
| 69 | var expected = 0x0F00 | |
| 70 | assert.deepEqual(encode(expected), [0x80, 0x1E]) | |
| 71 | assert.equal(encode.bytes, 2) | |
| 72 | assert.end() | |
| 73 | }) | |
| 74 | ||
| 75 | test('big integers', function (assert) { | |
| 76 | ||
| 77 | var bigs = [] | |
| 78 | for(var i = 32; i <= 53; i++) (function (i) { | |
| 79 | bigs.push(Math.pow(2, i) - 1) | |
| 80 | bigs.push(Math.pow(2, i)) | |
| 81 | })(i) | |
| 82 | ||
| 83 | bigs.forEach(function (n) { | |
| 84 | var data = encode(n) | |
| 85 | console.error(n, '->', data) | |
| 86 | assert.equal(decode(data), n) | |
| 87 | assert.notEqual(decode(data), n - 1) | |
| 88 | }) | |
| 89 | assert.end() | |
| 90 | }) | |
| 91 | ||
| 92 | test('fuzz test - big', function(assert) { | |
| 93 | var expect | |
| 94 | , encoded | |
| 95 | ||
| 96 | var MAX_INTD = Math.pow(2, 55) | |
| 97 | var MAX_INT = Math.pow(2, 31) | |
| 98 | ||
| 99 | for(var i = 0, len = 100; i < len; ++i) { | |
| 100 | expect = randint(MAX_INTD - MAX_INT) + MAX_INT | |
| 101 | encoded = encode(expect) | |
| 102 | var data = decode(encoded) | |
| 103 | assert.equal(expect, data, 'fuzz test: ' + expect.toString()) | |
| 104 | assert.equal(decode.bytes, encoded.length) | |
| 105 | } | |
| 106 | ||
| 107 | assert.end() | |
| 108 | }) | |
| 109 | ||
| 110 | test('encodingLength', function (assert) { | |
| 111 | ||
| 112 | for(var i = 0; i <= 53; i++) { | |
| 113 | var n = Math.pow(2, i) | |
| 114 | assert.equal(encode(n).length, encodingLength(n)) | |
| 115 | } | |
| 116 | ||
| 117 | assert.end() | |
| 118 | }) | |
| 119 | ||
| 120 | test('buffer too short', function (assert) { | |
| 121 | ||
| 122 | var value = encode(9812938912312) | |
| 123 | var buffer = encode(value) | |
| 124 | ||
| 125 | var l = buffer.length | |
| 126 | while(l--) { | |
| 127 | try { | |
| 128 | var val = decode(buffer.slice(0, l)) | |
| 129 | } catch (err) { | |
| 130 | assert.equal(err.constructor, RangeError) | |
| 131 | assert.equal(decode.bytes, 0) | |
| 132 | } | |
| 133 | } | |
| 134 | assert.end() | |
| 135 | }) | |
| 136 | ||
| 137 | function randint(range) { | |
| 138 | return Math.floor(Math.random() * range) | |
| 139 | } |
| … | … | @@ -0,0 +1,201 @@ |
| 1 | Apache License | |
| 2 | Version 2.0, January 2004 | |
| 3 | http://www.apache.org/licenses/ | |
| 4 | ||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
| 6 | ||
| 7 | 1. Definitions. | |
| 8 | ||
| 9 | "License" shall mean the terms and conditions for use, reproduction, | |
| 10 | and distribution as defined by Sections 1 through 9 of this document. | |
| 11 | ||
| 12 | "Licensor" shall mean the copyright owner or entity authorized by | |
| 13 | the copyright owner that is granting the License. | |
| 14 | ||
| 15 | "Legal Entity" shall mean the union of the acting entity and all | |
| 16 | other entities that control, are controlled by, or are under common | |
| 17 | control with that entity. For the purposes of this definition, | |
| 18 | "control" means (i) the power, direct or indirect, to cause the | |
| 19 | direction or management of such entity, whether by contract or | |
| 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
| 21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
| 22 | ||
| 23 | "You" (or "Your") shall mean an individual or Legal Entity | |
| 24 | exercising permissions granted by this License. | |
| 25 | ||
| 26 | "Source" form shall mean the preferred form for making modifications, | |
| 27 | including but not limited to software source code, documentation | |
| 28 | source, and configuration files. | |
| 29 | ||
| 30 | "Object" form shall mean any form resulting from mechanical | |
| 31 | transformation or translation of a Source form, including but | |
| 32 | not limited to compiled object code, generated documentation, | |
| 33 | and conversions to other media types. | |
| 34 | ||
| 35 | "Work" shall mean the work of authorship, whether in Source or | |
| 36 | Object form, made available under the License, as indicated by a | |
| 37 | copyright notice that is included in or attached to the work | |
| 38 | (an example is provided in the Appendix below). | |
| 39 | ||
| 40 | "Derivative Works" shall mean any work, whether in Source or Object | |
| 41 | form, that is based on (or derived from) the Work and for which the | |
| 42 | editorial revisions, annotations, elaborations, or other modifications | |
| 43 | represent, as a whole, an original work of authorship. For the purposes | |
| 44 | of this License, Derivative Works shall not include works that remain | |
| 45 | separable from, or merely link (or bind by name) to the interfaces of, | |
| 46 | the Work and Derivative Works thereof. | |
| 47 | ||
| 48 | "Contribution" shall mean any work of authorship, including | |
| 49 | the original version of the Work and any modifications or additions | |
| 50 | to that Work or Derivative Works thereof, that is intentionally | |
| 51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
| 52 | or by an individual or Legal Entity authorized to submit on behalf of | |
| 53 | the copyright owner. For the purposes of this definition, "submitted" | |
| 54 | means any form of electronic, verbal, or written communication sent | |
| 55 | to the Licensor or its representatives, including but not limited to | |
| 56 | communication on electronic mailing lists, source code control systems, | |
| 57 | and issue tracking systems that are managed by, or on behalf of, the | |
| 58 | Licensor for the purpose of discussing and improving the Work, but | |
| 59 | excluding communication that is conspicuously marked or otherwise | |
| 60 | designated in writing by the copyright owner as "Not a Contribution." | |
| 61 | ||
| 62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
| 63 | on behalf of whom a Contribution has been received by Licensor and | |
| 64 | subsequently incorporated within the Work. | |
| 65 | ||
| 66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
| 67 | this License, each Contributor hereby grants to You a perpetual, | |
| 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 69 | copyright license to reproduce, prepare Derivative Works of, | |
| 70 | publicly display, publicly perform, sublicense, and distribute the | |
| 71 | Work and such Derivative Works in Source or Object form. | |
| 72 | ||
| 73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
| 74 | this License, each Contributor hereby grants to You a perpetual, | |
| 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
| 76 | (except as stated in this section) patent license to make, have made, | |
| 77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
| 78 | where such license applies only to those patent claims licensable | |
| 79 | by such Contributor that are necessarily infringed by their | |
| 80 | Contribution(s) alone or by combination of their Contribution(s) | |
| 81 | with the Work to which such Contribution(s) was submitted. If You | |
| 82 | institute patent litigation against any entity (including a | |
| 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
| 84 | or a Contribution incorporated within the Work constitutes direct | |
| 85 | or contributory patent infringement, then any patent licenses | |
| 86 | granted to You under this License for that Work shall terminate | |
| 87 | as of the date such litigation is filed. | |
| 88 | ||
| 89 | 4. Redistribution. You may reproduce and distribute copies of the | |
| 90 | Work or Derivative Works thereof in any medium, with or without | |
| 91 | modifications, and in Source or Object form, provided that You | |
| 92 | meet the following conditions: | |
| 93 | ||
| 94 | (a) You must give any other recipients of the Work or | |
| 95 | Derivative Works a copy of this License; and | |
| 96 | ||
| 97 | (b) You must cause any modified files to carry prominent notices | |
| 98 | stating that You changed the files; and | |
| 99 | ||
| 100 | (c) You must retain, in the Source form of any Derivative Works | |
| 101 | that You distribute, all copyright, patent, trademark, and | |
| 102 | attribution notices from the Source form of the Work, | |
| 103 | excluding those notices that do not pertain to any part of | |
| 104 | the Derivative Works; and | |
| 105 | ||
| 106 | (d) If the Work includes a "NOTICE" text file as part of its | |
| 107 | distribution, then any Derivative Works that You distribute must | |
| 108 | include a readable copy of the attribution notices contained | |
| 109 | within such NOTICE file, excluding those notices that do not | |
| 110 | pertain to any part of the Derivative Works, in at least one | |
| 111 | of the following places: within a NOTICE text file distributed | |
| 112 | as part of the Derivative Works; within the Source form or | |
| 113 | documentation, if provided along with the Derivative Works; or, | |
| 114 | within a display generated by the Derivative Works, if and | |
| 115 | wherever such third-party notices normally appear. The contents | |
| 116 | of the NOTICE file are for informational purposes only and | |
| 117 | do not modify the License. You may add Your own attribution | |
| 118 | notices within Derivative Works that You distribute, alongside | |
| 119 | or as an addendum to the NOTICE text from the Work, provided | |
| 120 | that such additional attribution notices cannot be construed | |
| 121 | as modifying the License. | |
| 122 | ||
| 123 | You may add Your own copyright statement to Your modifications and | |
| 124 | may provide additional or different license terms and conditions | |
| 125 | for use, reproduction, or distribution of Your modifications, or | |
| 126 | for any such Derivative Works as a whole, provided Your use, | |
| 127 | reproduction, and distribution of the Work otherwise complies with | |
| 128 | the conditions stated in this License. | |
| 129 | ||
| 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
| 131 | any Contribution intentionally submitted for inclusion in the Work | |
| 132 | by You to the Licensor shall be under the terms and conditions of | |
| 133 | this License, without any additional terms or conditions. | |
| 134 | Notwithstanding the above, nothing herein shall supersede or modify | |
| 135 | the terms of any separate license agreement you may have executed | |
| 136 | with Licensor regarding such Contributions. | |
| 137 | ||
| 138 | 6. Trademarks. This License does not grant permission to use the trade | |
| 139 | names, trademarks, service marks, or product names of the Licensor, | |
| 140 | except as required for reasonable and customary use in describing the | |
| 141 | origin of the Work and reproducing the content of the NOTICE file. | |
| 142 | ||
| 143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
| 144 | agreed to in writing, Licensor provides the Work (and each | |
| 145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
| 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
| 147 | implied, including, without limitation, any warranties or conditions | |
| 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
| 149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
| 150 | appropriateness of using or redistributing the Work and assume any | |
| 151 | risks associated with Your exercise of permissions under this License. | |
| 152 | ||
| 153 | 8. Limitation of Liability. In no event and under no legal theory, | |
| 154 | whether in tort (including negligence), contract, or otherwise, | |
| 155 | unless required by applicable law (such as deliberate and grossly | |
| 156 | negligent acts) or agreed to in writing, shall any Contributor be | |
| 157 | liable to You for damages, including any direct, indirect, special, | |
| 158 | incidental, or consequential damages of any character arising as a | |
| 159 | result of this License or out of the use or inability to use the | |
| 160 | Work (including but not limited to damages for loss of goodwill, | |
| 161 | work stoppage, computer failure or malfunction, or any and all | |
| 162 | other commercial damages or losses), even if such Contributor | |
| 163 | has been advised of the possibility of such damages. | |
| 164 | ||
| 165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
| 166 | the Work or Derivative Works thereof, You may choose to offer, | |
| 167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
| 168 | or other liability obligations and/or rights consistent with this | |
| 169 | License. However, in accepting such obligations, You may act only | |
| 170 | on Your own behalf and on Your sole responsibility, not on behalf | |
| 171 | of any other Contributor, and only if You agree to indemnify, | |
| 172 | defend, and hold each Contributor harmless for any liability | |
| 173 | incurred by, or claims asserted against, such Contributor by reason | |
| 174 | of your accepting any such warranty or additional liability. | |
| 175 | ||
| 176 | END OF TERMS AND CONDITIONS | |
| 177 | ||
| 178 | APPENDIX: How to apply the Apache License to your work. | |
| 179 | ||
| 180 | To apply the Apache License to your work, attach the following | |
| 181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
| 182 | replaced with your own identifying information. (Don't include | |
| 183 | the brackets!) The text should be enclosed in the appropriate | |
| 184 | comment syntax for the file format. We also recommend that a | |
| 185 | file or class name and description of purpose be included on the | |
| 186 | same "printed page" as the copyright notice for easier | |
| 187 | identification within third-party archives. | |
| 188 | ||
| 189 | Copyright [yyyy] [name of copyright owner] | |
| 190 | ||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
| 192 | you may not use this file except in compliance with the License. | |
| 193 | You may obtain a copy of the License at | |
| 194 | ||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | |
| 196 | ||
| 197 | Unless required by applicable law or agreed to in writing, software | |
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | |
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 200 | See the License for the specific language governing permissions and | |
| 201 | limitations under the License. |
| … | … | @@ -0,0 +1,29 @@ |
| 1 | # which-runtime | |
| 2 | ||
| 3 | Detect if you are in Bare or Node and which os etc | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install which-runtime | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | import { | |
| 13 | runtime, // bare, node, or browser | |
| 14 | platform, // the platform string, ie darwin, win32, etc | |
| 15 | arch, // which arch, arm64, ia32, x64 etc | |
| 16 | isBrowser, | |
| 17 | isBare, | |
| 18 | isNode, | |
| 19 | isLinux, | |
| 20 | isWindows, | |
| 21 | isMac, | |
| 22 | isIOS, | |
| 23 | isAndroid | |
| 24 | } from 'which-runtime' | |
| 25 | ``` | |
| 26 | ||
| 27 | ## License | |
| 28 | ||
| 29 | Apache-2.0 |
| … | … | @@ -0,0 +1,24 @@ |
| 1 | const { runtime, platform, arch } = typeof Bare !== 'undefined' | |
| 2 | ? { runtime: 'bare', platform: global.Bare.platform, arch: global.Bare.arch } | |
| 3 | : typeof process !== 'undefined' | |
| 4 | ? { runtime: 'node', platform: global.process.platform, arch: global.process.arch } | |
| 5 | : typeof Window !== 'undefined' | |
| 6 | ? { runtime: 'browser', platform: 'unknown', arch: 'unknown' } | |
| 7 | : { runtime: 'unknown', platform: 'unknown', arch: 'unknown' } | |
| 8 | ||
| 9 | exports.runtime = runtime | |
| 10 | exports.platform = platform | |
| 11 | exports.arch = arch | |
| 12 | exports.isBare = runtime === 'bare' | |
| 13 | exports.isBareKit = exports.isBare && typeof BareKit !== 'undefined' | |
| 14 | exports.isPear = typeof Pear !== 'undefined' | |
| 15 | exports.isNode = runtime === 'node' | |
| 16 | exports.isBrowser = runtime === 'browser' | |
| 17 | exports.isWindows = platform === 'win32' | |
| 18 | exports.isLinux = platform === 'linux' | |
| 19 | exports.isMac = platform === 'darwin' | |
| 20 | exports.isIOS = platform === 'ios' || platform === 'ios-simulator' | |
| 21 | exports.isAndroid = platform === 'android' | |
| 22 | exports.isElectron = typeof process !== 'undefined' && !!global.process.versions?.electron | |
| 23 | exports.isElectronRenderer = exports.isElectron && global.process.type === 'renderer' | |
| 24 | exports.isElectronWorker = exports.isElectron && global.process.type === 'worker' |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | { | |
| 2 | "name": "which-runtime", | |
| 3 | "version": "1.3.2", | |
| 4 | "description": "Detect if you are in Bare or Node and which os etc", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js" | |
| 8 | ], | |
| 9 | "dependencies": {}, | |
| 10 | "devDependencies": { | |
| 11 | "standard": "^17.0.0" | |
| 12 | }, | |
| 13 | "scripts": { | |
| 14 | "test": "standard" | |
| 15 | }, | |
| 16 | "repository": { | |
| 17 | "type": "git", | |
| 18 | "url": "https://github.com/holepunchto/which-runtime.git" | |
| 19 | }, | |
| 20 | "author": "Holepunch Inc.", | |
| 21 | "license": "Apache-2.0", | |
| 22 | "bugs": { | |
| 23 | "url": "https://github.com/holepunchto/which-runtime/issues" | |
| 24 | }, | |
| 25 | "homepage": "https://github.com/holepunchto/which-runtime" | |
| 26 | } |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2021 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,35 @@ |
| 1 | # xache | |
| 2 | ||
| 3 | Yet another auto expiring, max sizable cache | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install xache | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const Xache = require('xache') | |
| 13 | ||
| 14 | const cache = new Xache({ | |
| 15 | maxSize: 10, // at max (ish) have 10 entries | |
| 16 | maxAge: 100, // auto expire entries after (ish) 100ms | |
| 17 | createMap () { // optional function to create backing storage | |
| 18 | return new Map() | |
| 19 | } | |
| 20 | }) | |
| 21 | ||
| 22 | // When maxSize is hit, the oldest entries are removed | |
| 23 | // cache has the same api as a map | |
| 24 | ||
| 25 | cache.set('hello', 'world') | |
| 26 | console.log(cache.get('hello')) | |
| 27 | ||
| 28 | console.log(...cache) // iterable! | |
| 29 | ``` | |
| 30 | ||
| 31 | That's it, nice and simple. | |
| 32 | ||
| 33 | ## License | |
| 34 | ||
| 35 | MIT |
| … | … | @@ -0,0 +1,108 @@ |
| 1 | module.exports = class MaxCache { | |
| 2 | constructor ({ maxSize, maxAge, createMap, ongc }) { | |
| 3 | this.maxSize = maxSize | |
| 4 | this.maxAge = maxAge | |
| 5 | this.ongc = ongc || null | |
| 6 | ||
| 7 | this._createMap = createMap || defaultCreateMap | |
| 8 | this._latest = this._createMap() | |
| 9 | this._oldest = this._createMap() | |
| 10 | this._retained = this._createMap() | |
| 11 | this._gced = false | |
| 12 | this._interval = null | |
| 13 | ||
| 14 | if (this.maxAge > 0 && this.maxAge < Infinity) { | |
| 15 | const tick = Math.ceil(2 / 3 * this.maxAge) | |
| 16 | this._interval = setInterval(this._gcAuto.bind(this), tick) | |
| 17 | if (this._interval.unref) this._interval.unref() | |
| 18 | } | |
| 19 | } | |
| 20 | ||
| 21 | * [Symbol.iterator] () { | |
| 22 | for (const it of [this._latest, this._oldest, this._retained]) { | |
| 23 | yield * it | |
| 24 | } | |
| 25 | } | |
| 26 | ||
| 27 | * keys () { | |
| 28 | for (const it of [this._latest, this._oldest, this._retained]) { | |
| 29 | yield * it.keys() | |
| 30 | } | |
| 31 | } | |
| 32 | ||
| 33 | * values () { | |
| 34 | for (const it of [this._latest, this._oldest, this._retained]) { | |
| 35 | yield * it.values() | |
| 36 | } | |
| 37 | } | |
| 38 | ||
| 39 | destroy () { | |
| 40 | this.clear() | |
| 41 | clearInterval(this._interval) | |
| 42 | this._interval = null | |
| 43 | } | |
| 44 | ||
| 45 | clear () { | |
| 46 | this._gced = true | |
| 47 | this._latest.clear() | |
| 48 | this._oldest.clear() | |
| 49 | this._retained.clear() | |
| 50 | } | |
| 51 | ||
| 52 | set (k, v) { | |
| 53 | if (this._retained.has(k)) return this | |
| 54 | this._latest.set(k, v) | |
| 55 | this._oldest.delete(k) || this._retained.delete(k) | |
| 56 | if (this._latest.size >= this.maxSize) this._gc() | |
| 57 | return this | |
| 58 | } | |
| 59 | ||
| 60 | retain (k, v) { | |
| 61 | this._retained.set(k, v) | |
| 62 | this._latest.delete(k) || this._oldest.delete(k) | |
| 63 | return this | |
| 64 | } | |
| 65 | ||
| 66 | delete (k) { | |
| 67 | return this._latest.delete(k) || this._oldest.delete(k) || this._retained.delete(k) | |
| 68 | } | |
| 69 | ||
| 70 | has (k) { | |
| 71 | return this._latest.has(k) || this._oldest.has(k) || this._retained.has(k) | |
| 72 | } | |
| 73 | ||
| 74 | get (k) { | |
| 75 | if (this._latest.has(k)) { | |
| 76 | return this._latest.get(k) | |
| 77 | } | |
| 78 | ||
| 79 | if (this._oldest.has(k)) { | |
| 80 | const v = this._oldest.get(k) | |
| 81 | this._latest.set(k, v) | |
| 82 | this._oldest.delete(k) | |
| 83 | return v | |
| 84 | } | |
| 85 | ||
| 86 | if (this._retained.has(k)) { | |
| 87 | return this._retained.get(k) | |
| 88 | } | |
| 89 | ||
| 90 | return null | |
| 91 | } | |
| 92 | ||
| 93 | _gcAuto () { | |
| 94 | if (!this._gced) this._gc() | |
| 95 | this._gced = false | |
| 96 | } | |
| 97 | ||
| 98 | _gc () { | |
| 99 | this._gced = true | |
| 100 | if (this.ongc !== null && this._oldest.size > 0) this.ongc(this._oldest) | |
| 101 | this._oldest = this._latest | |
| 102 | this._latest = this._createMap() | |
| 103 | } | |
| 104 | } | |
| 105 | ||
| 106 | function defaultCreateMap () { | |
| 107 | return new Map() | |
| 108 | } |
| … | … | @@ -0,0 +1,26 @@ |
| 1 | { | |
| 2 | "name": "xache", | |
| 3 | "version": "1.2.1", | |
| 4 | "description": "Yet another auto expiring, max sizable cache", | |
| 5 | "main": "index.js", | |
| 6 | "files": [ | |
| 7 | "index.js" | |
| 8 | ], | |
| 9 | "devDependencies": { | |
| 10 | "brittle": "^3.3.2", | |
| 11 | "standard": "^17.1.0" | |
| 12 | }, | |
| 13 | "scripts": { | |
| 14 | "test": "standard && brittle test.js" | |
| 15 | }, | |
| 16 | "repository": { | |
| 17 | "type": "git", | |
| 18 | "url": "https://github.com/mafintosh/xache.git" | |
| 19 | }, | |
| 20 | "author": "Mathias Buus (@mafintosh)", | |
| 21 | "license": "MIT", | |
| 22 | "bugs": { | |
| 23 | "url": "https://github.com/mafintosh/xache/issues" | |
| 24 | }, | |
| 25 | "homepage": "https://github.com/mafintosh/xache" | |
| 26 | } |
| … | … | @@ -0,0 +1,23 @@ |
| 1 | name: Build Status | |
| 2 | on: | |
| 3 | push: | |
| 4 | branches: | |
| 5 | - master | |
| 6 | pull_request: | |
| 7 | branches: | |
| 8 | - master | |
| 9 | jobs: | |
| 10 | build: | |
| 11 | strategy: | |
| 12 | matrix: | |
| 13 | node-version: [lts/*] | |
| 14 | os: [ubuntu-latest, macos-latest, windows-latest] | |
| 15 | runs-on: ${{ matrix.os }} | |
| 16 | steps: | |
| 17 | - uses: actions/checkout@v2 | |
| 18 | - name: Use Node.js ${{ matrix.node-version }} | |
| 19 | uses: actions/setup-node@v2 | |
| 20 | with: | |
| 21 | node-version: ${{ matrix.node-version }} | |
| 22 | - run: npm install | |
| 23 | - run: npm test |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | The MIT License (MIT) | |
| 2 | ||
| 3 | Copyright (c) 2022 Mathias Buus | |
| 4 | ||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | of this software and associated documentation files (the "Software"), to deal | |
| 7 | in the Software without restriction, including without limitation the rights | |
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | copies of the Software, and to permit persons to whom the Software is | |
| 10 | furnished to do so, subject to the following conditions: | |
| 11 | ||
| 12 | The above copyright notice and this permission notice shall be included in | |
| 13 | all copies or substantial portions of the Software. | |
| 14 | ||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 21 | THE SOFTWARE. |
| … | … | @@ -0,0 +1,35 @@ |
| 1 | # z32 | |
| 2 | ||
| 3 | z-base-32 encoder & decoder | |
| 4 | ||
| 5 | ``` | |
| 6 | npm install z32 | |
| 7 | ``` | |
| 8 | ||
| 9 | ## Usage | |
| 10 | ||
| 11 | ``` js | |
| 12 | const z32 = require('z32') | |
| 13 | ||
| 14 | const s = z32.encode('Hello, World!') | |
| 15 | ||
| 16 | console.log(s) // jb1sa5dxfoofq551pt1nn | |
| 17 | ||
| 18 | const b = z32.decode(s) | |
| 19 | ||
| 20 | console.log(b) // Buffer('Hello, World!') | |
| 21 | ``` | |
| 22 | ||
| 23 | ## API | |
| 24 | ||
| 25 | #### `const s = z32.encode(stringOrBuffer)` | |
| 26 | ||
| 27 | Encode a string or buffer to z-base-32. | |
| 28 | ||
| 29 | #### `const buf = z32.decode(s)` | |
| 30 | ||
| 31 | Returns a decoded buffer. Throws if the string is invalid. | |
| 32 | ||
| 33 | ## License | |
| 34 | ||
| 35 | MIT |
| … | … | @@ -0,0 +1,133 @@ |
| 1 | const bench = require('nanobench') | |
| 2 | const { randomBytes } = require('node:crypto') | |
| 3 | ||
| 4 | const buffers = Array(1e4) | |
| 5 | .fill(null) | |
| 6 | .map(() => { | |
| 7 | return randomBytes(Math.round(Math.random() * 100)) | |
| 8 | }) | |
| 9 | ||
| 10 | bench('z32 encode 100 times', function (b) { | |
| 11 | const z32 = require('.') | |
| 12 | b.start() | |
| 13 | for (let i = 0; i < 100; i++) { | |
| 14 | for (const buf of buffers) { | |
| 15 | z32.encode(buf) | |
| 16 | } | |
| 17 | } | |
| 18 | ||
| 19 | b.end() | |
| 20 | }) | |
| 21 | ||
| 22 | bench('z32 decode 100 times', function (b) { | |
| 23 | const z32 = require('.') | |
| 24 | const encoded = buffers.map(buf => z32.encode(buf)) | |
| 25 | b.start() | |
| 26 | for (let i = 0; i < 100; i++) { | |
| 27 | for (const s of encoded) { | |
| 28 | z32.decode(s) | |
| 29 | } | |
| 30 | } | |
| 31 | ||
| 32 | b.end() | |
| 33 | }) | |
| 34 | ||
| 35 | bench("buf.toString('hex') 100 times", function (b) { | |
| 36 | b.start() | |
| 37 | for (let i = 0; i < 100; i++) { | |
| 38 | for (const buf of buffers) { | |
| 39 | buf.toString('hex') | |
| 40 | } | |
| 41 | } | |
| 42 | ||
| 43 | b.end() | |
| 44 | }) | |
| 45 | ||
| 46 | bench("Buffer.from(s, 'hex') 100 times", function (b) { | |
| 47 | const encoded = buffers.map(buf => buf.toString('hex')) | |
| 48 | b.start() | |
| 49 | for (let i = 0; i < 100; i++) { | |
| 50 | for (const s of encoded) { | |
| 51 | Buffer.from(s, 'hex') | |
| 52 | } | |
| 53 | } | |
| 54 | ||
| 55 | b.end() | |
| 56 | }) | |
| 57 | ||
| 58 | bench('base32 encode 100 times', function (b) { | |
| 59 | const base32 = require('base32') | |
| 60 | b.start() | |
| 61 | for (let i = 0; i < 100; i++) { | |
| 62 | for (const buf of buffers) { | |
| 63 | base32.encode(buf) | |
| 64 | } | |
| 65 | } | |
| 66 | ||
| 67 | b.end() | |
| 68 | }) | |
| 69 | ||
| 70 | bench('base32 decode 100 times', function (b) { | |
| 71 | const base32 = require('base32') | |
| 72 | const encoded = buffers.map(buf => base32.encode(buf)) | |
| 73 | b.start() | |
| 74 | for (let i = 0; i < 100; i++) { | |
| 75 | for (const s of encoded) { | |
| 76 | base32.decode(s) | |
| 77 | } | |
| 78 | } | |
| 79 | ||
| 80 | b.end() | |
| 81 | }) | |
| 82 | ||
| 83 | bench('rfc4648.base32 encode 100 times', function (b) { | |
| 84 | const { base32 } = require('rfc4648') | |
| 85 | b.start() | |
| 86 | for (let i = 0; i < 100; i++) { | |
| 87 | for (const buf of buffers) { | |
| 88 | base32.stringify(buf) | |
| 89 | } | |
| 90 | } | |
| 91 | ||
| 92 | b.end() | |
| 93 | }) | |
| 94 | ||
| 95 | bench('rfc4648.base32 decode 100 times', function (b) { | |
| 96 | const { base32 } = require('rfc4648') | |
| 97 | const encoded = buffers.map(buf => base32.stringify(buf)) | |
| 98 | b.start() | |
| 99 | for (let i = 0; i < 100; i++) { | |
| 100 | for (const s of encoded) { | |
| 101 | base32.parse(s, { out: Buffer.allocUnsafe }) | |
| 102 | } | |
| 103 | } | |
| 104 | ||
| 105 | b.end() | |
| 106 | }) | |
| 107 | ||
| 108 | bench('base-x z-base-32 encode 100 times', function (b) { | |
| 109 | const ZBASE32 = 'ybndrfg8ejkmcpqxot1uwisza345h769' | |
| 110 | const base32 = require('base-x')(ZBASE32) | |
| 111 | b.start() | |
| 112 | for (let i = 0; i < 100; i++) { | |
| 113 | for (const buf of buffers) { | |
| 114 | base32.encode(buf) | |
| 115 | } | |
| 116 | } | |
| 117 | ||
| 118 | b.end() | |
| 119 | }) | |
| 120 | ||
| 121 | bench('base-x z-base-32 decode 100 times', function (b) { | |
| 122 | const ZBASE32 = 'ybndrfg8ejkmcpqxot1uwisza345h769' | |
| 123 | const base32 = require('base-x')(ZBASE32) | |
| 124 | const encoded = buffers.map(buf => base32.encode(buf)) | |
| 125 | b.start() | |
| 126 | for (let i = 0; i < 100; i++) { | |
| 127 | for (const s of encoded) { | |
| 128 | base32.decode(s) | |
| 129 | } | |
| 130 | } | |
| 131 | ||
| 132 | b.end() | |
| 133 | }) |
| … | … | @@ -0,0 +1,126 @@ |
| 1 | const b4a = require('b4a') | |
| 2 | ||
| 3 | const ALPHABET = 'ybndrfg8ejkmcpqxot1uwisza345h769' | |
| 4 | const MIN = 0x31 // 1 | |
| 5 | const MAX = 0x7a // z | |
| 6 | const REVERSE = new Int8Array(1 + MAX - MIN) | |
| 7 | ||
| 8 | REVERSE.fill(-1) | |
| 9 | ||
| 10 | for (let i = 0; i < ALPHABET.length; i++) { | |
| 11 | const v = ALPHABET.charCodeAt(i) - MIN | |
| 12 | REVERSE[v] = i | |
| 13 | } | |
| 14 | ||
| 15 | exports.encode = encode | |
| 16 | exports.decode = decode | |
| 17 | exports.ALPHABET = ALPHABET | |
| 18 | ||
| 19 | function decode (s, out) { | |
| 20 | let pb = 0 | |
| 21 | let ps = 0 | |
| 22 | ||
| 23 | const r = s.length & 7 | |
| 24 | const q = (s.length - r) / 8 | |
| 25 | ||
| 26 | if (!out) out = b4a.allocUnsafe(Math.ceil(s.length * 5 / 8)) | |
| 27 | ||
| 28 | // 0 5 2 7 4 1 6 3 (+5 mod 8) | |
| 29 | for (let i = 0; i < q; i++) { | |
| 30 | const a = quintet(s, ps++) | |
| 31 | const b = quintet(s, ps++) | |
| 32 | const c = quintet(s, ps++) | |
| 33 | const d = quintet(s, ps++) | |
| 34 | const e = quintet(s, ps++) | |
| 35 | const f = quintet(s, ps++) | |
| 36 | const g = quintet(s, ps++) | |
| 37 | const h = quintet(s, ps++) | |
| 38 | ||
| 39 | out[pb++] = (a << 3) | (b >>> 2) | |
| 40 | out[pb++] = ((b & 0b11) << 6) | (c << 1) | (d >>> 4) | |
| 41 | out[pb++] = ((d & 0b1111) << 4) | (e >>> 1) | |
| 42 | out[pb++] = ((e & 0b1) << 7) | (f << 2) | (g >>> 3) | |
| 43 | out[pb++] = ((g & 0b111) << 5) | h | |
| 44 | } | |
| 45 | ||
| 46 | if (r === 0) return out.subarray(0, pb) | |
| 47 | ||
| 48 | const a = quintet(s, ps++) | |
| 49 | const b = quintet(s, ps++) | |
| 50 | ||
| 51 | out[pb++] = (a << 3) | (b >>> 2) | |
| 52 | ||
| 53 | if (r <= 2) return out.subarray(0, pb) | |
| 54 | ||
| 55 | const c = quintet(s, ps++) | |
| 56 | const d = quintet(s, ps++) | |
| 57 | ||
| 58 | out[pb++] = ((b & 0b11) << 6) | (c << 1) | (d >>> 4) | |
| 59 | ||
| 60 | if (r <= 4) return out.subarray(0, pb) | |
| 61 | ||
| 62 | const e = quintet(s, ps++) | |
| 63 | ||
| 64 | out[pb++] = ((d & 0b1111) << 4) | (e >>> 1) | |
| 65 | ||
| 66 | if (r <= 5) return out.subarray(0, pb) | |
| 67 | ||
| 68 | const f = quintet(s, ps++) | |
| 69 | const g = quintet(s, ps++) | |
| 70 | ||
| 71 | out[pb++] = ((e & 0b1) << 7) | (f << 2) | (g >>> 3) | |
| 72 | ||
| 73 | if (r <= 7) return out.subarray(0, pb) | |
| 74 | ||
| 75 | const h = quintet(s, ps++) | |
| 76 | ||
| 77 | out[pb++] = ((g & 0b111) << 5) | h | |
| 78 | ||
| 79 | return out.subarray(0, pb) | |
| 80 | } | |
| 81 | ||
| 82 | function encode (buf) { | |
| 83 | if (typeof buf === 'string') buf = b4a.from(buf) | |
| 84 | ||
| 85 | const max = buf.byteLength * 8 | |
| 86 | ||
| 87 | let s = '' | |
| 88 | ||
| 89 | for (let p = 0; p < max; p += 5) { | |
| 90 | const i = p >>> 3 | |
| 91 | const j = p & 7 | |
| 92 | ||
| 93 | if (j <= 3) { | |
| 94 | s += ALPHABET[(buf[i] >>> (3 - j)) & 0b11111] | |
| 95 | continue | |
| 96 | } | |
| 97 | ||
| 98 | const of = j - 3 | |
| 99 | const h = (buf[i] << of) & 0b11111 | |
| 100 | const l = (i >= buf.byteLength ? 0 : buf[i + 1]) >>> (8 - of) | |
| 101 | ||
| 102 | s += ALPHABET[h | l] | |
| 103 | } | |
| 104 | ||
| 105 | return s | |
| 106 | } | |
| 107 | ||
| 108 | function quintet (s, i) { | |
| 109 | if (i > s.length) { | |
| 110 | return 0 | |
| 111 | } | |
| 112 | ||
| 113 | const v = s.charCodeAt(i) | |
| 114 | ||
| 115 | if (v < MIN || v > MAX) { | |
| 116 | throw Error('Invalid character in base32 input: "' + s[i] + '" at position ' + i) | |
| 117 | } | |
| 118 | ||
| 119 | const bits = REVERSE[v - MIN] | |
| 120 | ||
| 121 | if (bits === -1) { | |
| 122 | throw Error('Invalid character in base32 input: "' + s[i] + '" at position ' + i) | |
| 123 | } | |
| 124 | ||
| 125 | return bits | |
| 126 | } |
| … | … | @@ -0,0 +1,31 @@ |
| 1 | { | |
| 2 | "name": "z32", | |
| 3 | "version": "1.1.0", | |
| 4 | "description": "Encode & decode z-base32", | |
| 5 | "main": "index.js", | |
| 6 | "dependencies": { | |
| 7 | "b4a": "^1.5.3" | |
| 8 | }, | |
| 9 | "devDependencies": { | |
| 10 | "base-x": "^4.0.0", | |
| 11 | "base32": "0.0.7", | |
| 12 | "brittle": "^3.1.3", | |
| 13 | "nanobench": "^3.0.0", | |
| 14 | "rfc4648": "^1.5.2", | |
| 15 | "standard": "^17.0.0" | |
| 16 | }, | |
| 17 | "repository": { | |
| 18 | "type": "git", | |
| 19 | "url": "https://github.com/mafintosh/z32.git" | |
| 20 | }, | |
| 21 | "scripts": { | |
| 22 | "test": "standard && brittle test.js", | |
| 23 | "bench": "node benchmark.js" | |
| 24 | }, | |
| 25 | "author": "Mathias Buus (@mafintosh)", | |
| 26 | "license": "MIT", | |
| 27 | "bugs": { | |
| 28 | "url": "https://github.com/mafintosh/z32/issues" | |
| 29 | }, | |
| 30 | "homepage": "https://github.com/mafintosh/z32" | |
| 31 | } |
| … | … | @@ -0,0 +1,49 @@ |
| 1 | const z32 = require('./') | |
| 2 | const b4a = require('b4a') | |
| 3 | const test = require('brittle') | |
| 4 | const crypto = require('crypto') | |
| 5 | ||
| 6 | test('basic examples', function (t) { | |
| 7 | { | |
| 8 | const s = z32.encode('Example z-base-32') | |
| 9 | const b = z32.decode(s) | |
| 10 | ||
| 11 | t.is(s, 'eihgn5mopt11y6tpcjozg3jpgc3y') | |
| 12 | t.is(b.toString(), 'Example z-base-32') | |
| 13 | } | |
| 14 | ||
| 15 | { | |
| 16 | const s = z32.encode('The quick brown fox jumps over the lazy dog. 👀') | |
| 17 | const b = z32.decode(s) | |
| 18 | ||
| 19 | t.is(s, 'ktwgkedtqiwsg43ycj3g675qrbug66bypj4s4hdurbzzc3m1rb4go3jyptozw6jyctzsqmty6nx3dyy') | |
| 20 | t.is(b.toString(), 'The quick brown fox jumps over the lazy dog. 👀') | |
| 21 | } | |
| 22 | }) | |
| 23 | ||
| 24 | test('random buffers', function (t) { | |
| 25 | for (let i = 0; i < 1e5; i++) { | |
| 26 | const b = crypto.randomBytes(Math.round(Math.random() * 100)) | |
| 27 | const s = z32.encode(b) | |
| 28 | const o = z32.decode(s) | |
| 29 | ||
| 30 | if (!b4a.equals(o, b)) { | |
| 31 | t.alike(o, b) | |
| 32 | return | |
| 33 | } | |
| 34 | } | |
| 35 | ||
| 36 | t.pass('all random buffers passed') | |
| 37 | }) | |
| 38 | ||
| 39 | test('bad inputs', function (t) { | |
| 40 | t.exception(function () { | |
| 41 | z32.decode('!!!') | |
| 42 | }) | |
| 43 | t.exception(function () { | |
| 44 | z32.decode('~~~') | |
| 45 | }) | |
| 46 | t.exception(function () { | |
| 47 | z32.decode('I1I1I1') | |
| 48 | }) | |
| 49 | }) |
| … | … | @@ -0,0 +1,1019 @@ |
| 1 | { | |
| 2 | "name": "hyper-inspector", | |
| 3 | "version": "0.1.0", | |
| 4 | "lockfileVersion": 3, | |
| 5 | "requires": true, | |
| 6 | "packages": { | |
| 7 | "": { | |
| 8 | "name": "hyper-inspector", | |
| 9 | "version": "0.1.0", | |
| 10 | "license": "MIT", | |
| 11 | "dependencies": { | |
| 12 | "b4a": "^1.6.7", | |
| 13 | "corestore": "^6.18.0", | |
| 14 | "hyperdrive": "^11.13.0", | |
| 15 | "paparam": "^1.10.0", | |
| 16 | "rocksdb-native": "^3.13.0" | |
| 17 | }, | |
| 18 | "bin": { | |
| 19 | "hyper-inspector": "bin/cli.js" | |
| 20 | } | |
| 21 | }, | |
| 22 | "node_modules/@hyperswarm/secret-stream": { | |
| 23 | "version": "6.9.1", | |
| 24 | "resolved": "https://registry.npmjs.org/@hyperswarm/secret-stream/-/secret-stream-6.9.1.tgz", | |
| 25 | "integrity": "sha512-xb0S5y3YJwBakD77JOGBHlBxdp63mHClZoXBYoLv+9wH8e054ESKlmQptWqjJK5dv5VMUIVYOJB4MaOpB0JdGw==", | |
| 26 | "license": "Apache-2.0", | |
| 27 | "dependencies": { | |
| 28 | "b4a": "^1.1.0", | |
| 29 | "hypercore-crypto": "^3.3.1", | |
| 30 | "noise-curve-ed": "^2.0.1", | |
| 31 | "noise-handshake": "^4.0.0", | |
| 32 | "sodium-secretstream": "^1.1.0", | |
| 33 | "sodium-universal": "^5.0.0", | |
| 34 | "streamx": "^2.14.0", | |
| 35 | "timeout-refresh": "^2.0.0", | |
| 36 | "unslab": "^1.3.0" | |
| 37 | } | |
| 38 | }, | |
| 39 | "node_modules/@hyperswarm/secret-stream/node_modules/sodium-native": { | |
| 40 | "version": "5.1.0", | |
| 41 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.1.0.tgz", | |
| 42 | "integrity": "sha512-3RxgyWyJlhTsABPnJVpCI5CoTDANZTqqFrEPqr+kjfnRaBihpVtMUE3yTF40ukdoB1APXeoBNKF3MzZAIHg39g==", | |
| 43 | "license": "MIT", | |
| 44 | "dependencies": { | |
| 45 | "bare-assert": "^1.2.0", | |
| 46 | "require-addon": "^1.1.0", | |
| 47 | "which-runtime": "^1.2.1" | |
| 48 | }, | |
| 49 | "engines": { | |
| 50 | "bare": ">=1.16.0" | |
| 51 | } | |
| 52 | }, | |
| 53 | "node_modules/@hyperswarm/secret-stream/node_modules/sodium-universal": { | |
| 54 | "version": "5.0.1", | |
| 55 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz", | |
| 56 | "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==", | |
| 57 | "license": "MIT", | |
| 58 | "dependencies": { | |
| 59 | "sodium-native": "^5.0.1" | |
| 60 | }, | |
| 61 | "peerDependencies": { | |
| 62 | "sodium-javascript": "~0.8.0" | |
| 63 | }, | |
| 64 | "peerDependenciesMeta": { | |
| 65 | "sodium-javascript": { | |
| 66 | "optional": true | |
| 67 | } | |
| 68 | } | |
| 69 | }, | |
| 70 | "node_modules/b4a": { | |
| 71 | "version": "1.8.0", | |
| 72 | "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", | |
| 73 | "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==", | |
| 74 | "license": "Apache-2.0", | |
| 75 | "peerDependencies": { | |
| 76 | "react-native-b4a": "*" | |
| 77 | }, | |
| 78 | "peerDependenciesMeta": { | |
| 79 | "react-native-b4a": { | |
| 80 | "optional": true | |
| 81 | } | |
| 82 | } | |
| 83 | }, | |
| 84 | "node_modules/bare-addon-resolve": { | |
| 85 | "version": "1.10.0", | |
| 86 | "resolved": "https://registry.npmjs.org/bare-addon-resolve/-/bare-addon-resolve-1.10.0.tgz", | |
| 87 | "integrity": "sha512-sSd0jieRJlDaODOzj0oe0RjFVC1QI0ZIjGIdPkbrTXsdVVtENg14c+lHHAhHwmWCZ2nQlMhy8jA3Y5LYPc/isA==", | |
| 88 | "license": "Apache-2.0", | |
| 89 | "dependencies": { | |
| 90 | "bare-module-resolve": "^1.10.0", | |
| 91 | "bare-semver": "^1.0.0" | |
| 92 | }, | |
| 93 | "peerDependencies": { | |
| 94 | "bare-url": "*" | |
| 95 | }, | |
| 96 | "peerDependenciesMeta": { | |
| 97 | "bare-url": { | |
| 98 | "optional": true | |
| 99 | } | |
| 100 | } | |
| 101 | }, | |
| 102 | "node_modules/bare-ansi-escapes": { | |
| 103 | "version": "2.2.3", | |
| 104 | "resolved": "https://registry.npmjs.org/bare-ansi-escapes/-/bare-ansi-escapes-2.2.3.tgz", | |
| 105 | "integrity": "sha512-02ES4/E2RbrtZSnHJ9LntBhYkLA6lPpSEeP8iqS3MccBIVhVBlEmruF1I7HZqx5Q8aiTeYfQVeqmrU9YO2yYoQ==", | |
| 106 | "license": "Apache-2.0", | |
| 107 | "dependencies": { | |
| 108 | "bare-stream": "^2.6.5" | |
| 109 | }, | |
| 110 | "peerDependencies": { | |
| 111 | "bare-buffer": "*" | |
| 112 | }, | |
| 113 | "peerDependenciesMeta": { | |
| 114 | "bare-buffer": { | |
| 115 | "optional": true | |
| 116 | } | |
| 117 | } | |
| 118 | }, | |
| 119 | "node_modules/bare-assert": { | |
| 120 | "version": "1.2.0", | |
| 121 | "resolved": "https://registry.npmjs.org/bare-assert/-/bare-assert-1.2.0.tgz", | |
| 122 | "integrity": "sha512-c6uvgvTJBspTDxtVnPgrBKmLgcpW3Fp72NVKDLg6oT4QjQbhGtvrkHMhGYMK1sh4vjBHOBmuUalyt9hSzV37fQ==", | |
| 123 | "license": "Apache-2.0", | |
| 124 | "dependencies": { | |
| 125 | "bare-inspect": "^3.1.2" | |
| 126 | } | |
| 127 | }, | |
| 128 | "node_modules/bare-events": { | |
| 129 | "version": "2.8.2", | |
| 130 | "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", | |
| 131 | "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", | |
| 132 | "license": "Apache-2.0", | |
| 133 | "peerDependencies": { | |
| 134 | "bare-abort-controller": "*" | |
| 135 | }, | |
| 136 | "peerDependenciesMeta": { | |
| 137 | "bare-abort-controller": { | |
| 138 | "optional": true | |
| 139 | } | |
| 140 | } | |
| 141 | }, | |
| 142 | "node_modules/bare-fs": { | |
| 143 | "version": "4.5.6", | |
| 144 | "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.6.tgz", | |
| 145 | "integrity": "sha512-1QovqDrR80Pmt5HPAsMsXTCFcDYr+NSUKW6nd6WO5v0JBmnItc/irNRzm2KOQ5oZ69P37y+AMujNyNtG+1Rggw==", | |
| 146 | "license": "Apache-2.0", | |
| 147 | "dependencies": { | |
| 148 | "bare-events": "^2.5.4", | |
| 149 | "bare-path": "^3.0.0", | |
| 150 | "bare-stream": "^2.6.4", | |
| 151 | "bare-url": "^2.2.2", | |
| 152 | "fast-fifo": "^1.3.2" | |
| 153 | }, | |
| 154 | "engines": { | |
| 155 | "bare": ">=1.16.0" | |
| 156 | }, | |
| 157 | "peerDependencies": { | |
| 158 | "bare-buffer": "*" | |
| 159 | }, | |
| 160 | "peerDependenciesMeta": { | |
| 161 | "bare-buffer": { | |
| 162 | "optional": true | |
| 163 | } | |
| 164 | } | |
| 165 | }, | |
| 166 | "node_modules/bare-inspect": { | |
| 167 | "version": "3.1.4", | |
| 168 | "resolved": "https://registry.npmjs.org/bare-inspect/-/bare-inspect-3.1.4.tgz", | |
| 169 | "integrity": "sha512-jfW5KRA84o3REpI6Vr4nbvMn+hqVAw8GU1mMdRwUsY5yJovQamxYeKGVKGqdzs+8ZbG4jRzGUXP/3Ji/DnqfPg==", | |
| 170 | "license": "Apache-2.0", | |
| 171 | "dependencies": { | |
| 172 | "bare-ansi-escapes": "^2.1.0", | |
| 173 | "bare-type": "^1.0.0" | |
| 174 | }, | |
| 175 | "engines": { | |
| 176 | "bare": ">=1.18.0" | |
| 177 | } | |
| 178 | }, | |
| 179 | "node_modules/bare-module-resolve": { | |
| 180 | "version": "1.12.1", | |
| 181 | "resolved": "https://registry.npmjs.org/bare-module-resolve/-/bare-module-resolve-1.12.1.tgz", | |
| 182 | "integrity": "sha512-hbmAPyFpEq8FoZMd5sFO3u6MC5feluWoGE8YKlA8fCrl6mNtx68Wjg4DTiDJcqRJaovTvOYKfYngoBUnbaT7eg==", | |
| 183 | "license": "Apache-2.0", | |
| 184 | "dependencies": { | |
| 185 | "bare-semver": "^1.0.0" | |
| 186 | }, | |
| 187 | "peerDependencies": { | |
| 188 | "bare-url": "*" | |
| 189 | }, | |
| 190 | "peerDependenciesMeta": { | |
| 191 | "bare-url": { | |
| 192 | "optional": true | |
| 193 | } | |
| 194 | } | |
| 195 | }, | |
| 196 | "node_modules/bare-os": { | |
| 197 | "version": "3.8.0", | |
| 198 | "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.8.0.tgz", | |
| 199 | "integrity": "sha512-Dc9/SlwfxkXIGYhvMQNUtKaXCaGkZYGcd1vuNUUADVqzu4/vQfvnMkYYOUnt2VwQ2AqKr/8qAVFRtwETljgeFg==", | |
| 200 | "license": "Apache-2.0", | |
| 201 | "engines": { | |
| 202 | "bare": ">=1.14.0" | |
| 203 | } | |
| 204 | }, | |
| 205 | "node_modules/bare-path": { | |
| 206 | "version": "3.0.0", | |
| 207 | "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", | |
| 208 | "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", | |
| 209 | "license": "Apache-2.0", | |
| 210 | "dependencies": { | |
| 211 | "bare-os": "^3.0.1" | |
| 212 | } | |
| 213 | }, | |
| 214 | "node_modules/bare-semver": { | |
| 215 | "version": "1.0.2", | |
| 216 | "resolved": "https://registry.npmjs.org/bare-semver/-/bare-semver-1.0.2.tgz", | |
| 217 | "integrity": "sha512-ESVaN2nzWhcI5tf3Zzcq9aqCZ676VWzqw07eEZ0qxAcEOAFYBa0pWq8sK34OQeHLY3JsfKXZS9mDyzyxGjeLzA==", | |
| 218 | "license": "Apache-2.0" | |
| 219 | }, | |
| 220 | "node_modules/bare-stream": { | |
| 221 | "version": "2.10.0", | |
| 222 | "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.10.0.tgz", | |
| 223 | "integrity": "sha512-DOPZF/DDcDruKDA43cOw6e9Quq5daua7ygcAwJE/pKJsRWhgSSemi7qVNGE5kyDIxIeN1533G/zfbvWX7Wcb9w==", | |
| 224 | "license": "Apache-2.0", | |
| 225 | "dependencies": { | |
| 226 | "streamx": "^2.25.0", | |
| 227 | "teex": "^1.0.1" | |
| 228 | }, | |
| 229 | "peerDependencies": { | |
| 230 | "bare-buffer": "*", | |
| 231 | "bare-events": "*" | |
| 232 | }, | |
| 233 | "peerDependenciesMeta": { | |
| 234 | "bare-buffer": { | |
| 235 | "optional": true | |
| 236 | }, | |
| 237 | "bare-events": { | |
| 238 | "optional": true | |
| 239 | } | |
| 240 | } | |
| 241 | }, | |
| 242 | "node_modules/bare-type": { | |
| 243 | "version": "1.1.0", | |
| 244 | "resolved": "https://registry.npmjs.org/bare-type/-/bare-type-1.1.0.tgz", | |
| 245 | "integrity": "sha512-LdtnnEEYldOc87Dr4GpsKnStStZk3zfgoEMXy8yvEZkXrcCv9RtYDrUYWFsBQHtaB0s1EUWmcvS6XmEZYIj3Bw==", | |
| 246 | "license": "Apache-2.0", | |
| 247 | "engines": { | |
| 248 | "bare": ">=1.2.0" | |
| 249 | } | |
| 250 | }, | |
| 251 | "node_modules/bare-url": { | |
| 252 | "version": "2.4.0", | |
| 253 | "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.0.tgz", | |
| 254 | "integrity": "sha512-NSTU5WN+fy/L0DDenfE8SXQna4voXuW0FHM7wH8i3/q9khUSchfPbPezO4zSFMnDGIf9YE+mt/RWhZgNRKRIXA==", | |
| 255 | "license": "Apache-2.0", | |
| 256 | "dependencies": { | |
| 257 | "bare-path": "^3.0.0" | |
| 258 | } | |
| 259 | }, | |
| 260 | "node_modules/big-sparse-array": { | |
| 261 | "version": "1.0.3", | |
| 262 | "resolved": "https://registry.npmjs.org/big-sparse-array/-/big-sparse-array-1.0.3.tgz", | |
| 263 | "integrity": "sha512-6RjV/3mSZORlMdpUaQ6rUSpG637cZm0//E54YYGtQg1c1O+AbZP8UTdJ/TchsDZcTVLmyWZcseBfp2HBeXUXOQ==", | |
| 264 | "license": "MIT" | |
| 265 | }, | |
| 266 | "node_modules/binary-stream-equals": { | |
| 267 | "version": "1.0.0", | |
| 268 | "resolved": "https://registry.npmjs.org/binary-stream-equals/-/binary-stream-equals-1.0.0.tgz", | |
| 269 | "integrity": "sha512-xiUT5LGfD8JiLhbXiG+ByOnbgb9f2ssRLfZDQMl3nZdf89EotQZGZuMkDN8J3n46emabE7RnJ1q0r7Hv3INExw==", | |
| 270 | "license": "MIT", | |
| 271 | "dependencies": { | |
| 272 | "b4a": "^1.3.1" | |
| 273 | } | |
| 274 | }, | |
| 275 | "node_modules/codecs": { | |
| 276 | "version": "3.1.0", | |
| 277 | "resolved": "https://registry.npmjs.org/codecs/-/codecs-3.1.0.tgz", | |
| 278 | "integrity": "sha512-Dqx8NwvBvnMeuPQdVKy/XEF71igjR5apxBvCGeV0pP1tXadOiaLvDTXt7xh+/5wI1ASB195mXQGJbw3Ml4YDWQ==", | |
| 279 | "license": "MIT", | |
| 280 | "dependencies": { | |
| 281 | "b4a": "^1.6.3" | |
| 282 | } | |
| 283 | }, | |
| 284 | "node_modules/compact-encoding": { | |
| 285 | "version": "2.19.1", | |
| 286 | "resolved": "https://registry.npmjs.org/compact-encoding/-/compact-encoding-2.19.1.tgz", | |
| 287 | "integrity": "sha512-4J4FHc5UugLv2YMss1tL8OBZdgR0vVkV4EuXoAqFS4NbK4gz0Qp+E+VGUnwvDqmMOw2ce/H8duXh3rreC0Qccg==", | |
| 288 | "license": "Apache-2.0", | |
| 289 | "dependencies": { | |
| 290 | "b4a": "^1.3.0" | |
| 291 | } | |
| 292 | }, | |
| 293 | "node_modules/corestore": { | |
| 294 | "version": "6.18.4", | |
| 295 | "resolved": "https://registry.npmjs.org/corestore/-/corestore-6.18.4.tgz", | |
| 296 | "integrity": "sha512-KL1cOUyj3DFRNzeVRekGPRiGN4sIfGo7BsncSJQQBT3Rh2y1nQ32MOR9X5DlOMlWIT4txwqBzhzam6+JXtwFVw==", | |
| 297 | "license": "MIT", | |
| 298 | "dependencies": { | |
| 299 | "b4a": "^1.6.4", | |
| 300 | "hypercore": "^10.37.10", | |
| 301 | "hypercore-crypto": "^3.4.0", | |
| 302 | "hypercore-id-encoding": "^1.2.0", | |
| 303 | "read-write-mutexify": "^2.1.0", | |
| 304 | "ready-resource": "^1.0.0", | |
| 305 | "safety-catch": "^1.0.1", | |
| 306 | "sodium-universal": "^4.0.0", | |
| 307 | "xache": "^1.1.0" | |
| 308 | } | |
| 309 | }, | |
| 310 | "node_modules/crc-native": { | |
| 311 | "version": "1.1.8", | |
| 312 | "resolved": "https://registry.npmjs.org/crc-native/-/crc-native-1.1.8.tgz", | |
| 313 | "integrity": "sha512-R/RYbD8lhG5M1k0iLX49tJdwHM0ipevJPpZw2Wk7b4EShyvx2P0uaq79xyRpcknjiVGiUfjFA8Gyy0mOlhABvw==", | |
| 314 | "license": "Apache-2.0", | |
| 315 | "optional": true, | |
| 316 | "dependencies": { | |
| 317 | "require-addon": "^1.1.0" | |
| 318 | } | |
| 319 | }, | |
| 320 | "node_modules/crc-universal": { | |
| 321 | "version": "1.0.4", | |
| 322 | "resolved": "https://registry.npmjs.org/crc-universal/-/crc-universal-1.0.4.tgz", | |
| 323 | "integrity": "sha512-CE9xWEI6Gd5V0Bdmj5NoWH3d7+EIe4zUpy1sv6uvKYznzsDP1vhiWsTBmL6q9IrH2P6RRshp+8AkhR4CMtY5Hg==", | |
| 324 | "license": "ISC", | |
| 325 | "optionalDependencies": { | |
| 326 | "crc-native": "^1.0.3" | |
| 327 | } | |
| 328 | }, | |
| 329 | "node_modules/debounceify": { | |
| 330 | "version": "1.1.0", | |
| 331 | "resolved": "https://registry.npmjs.org/debounceify/-/debounceify-1.1.0.tgz", | |
| 332 | "integrity": "sha512-eKuHDVfJVg+u/0nPy8P+fhnLgbyuTgVxuCRrS/R7EpDSMMkBDgSes41MJtSAY1F1hcqfHz3Zy/qpqHHIp/EhdA==", | |
| 333 | "license": "MIT" | |
| 334 | }, | |
| 335 | "node_modules/events-universal": { | |
| 336 | "version": "1.0.1", | |
| 337 | "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", | |
| 338 | "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", | |
| 339 | "license": "Apache-2.0", | |
| 340 | "dependencies": { | |
| 341 | "bare-events": "^2.7.0" | |
| 342 | } | |
| 343 | }, | |
| 344 | "node_modules/fast-fifo": { | |
| 345 | "version": "1.3.2", | |
| 346 | "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", | |
| 347 | "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", | |
| 348 | "license": "MIT" | |
| 349 | }, | |
| 350 | "node_modules/flat-tree": { | |
| 351 | "version": "1.13.0", | |
| 352 | "resolved": "https://registry.npmjs.org/flat-tree/-/flat-tree-1.13.0.tgz", | |
| 353 | "integrity": "sha512-fT3HIuCPwHhFgJ20QYzDHgUG0zMmFg5cHvFiFo5h+QMSJ28TihsEVY0f8HGliuO+pOzmvjMx1odToeaEWkTnyQ==", | |
| 354 | "license": "MIT" | |
| 355 | }, | |
| 356 | "node_modules/fs-native-extensions": { | |
| 357 | "version": "1.4.5", | |
| 358 | "resolved": "https://registry.npmjs.org/fs-native-extensions/-/fs-native-extensions-1.4.5.tgz", | |
| 359 | "integrity": "sha512-ekV0T//iDm4AvhOcuPaHpxub4DI7HvY5ucLJVDvi7T2J+NZkQ9S6MuvgP0yeQvoqNUaAGyLjVYb1905BF9bpmg==", | |
| 360 | "license": "Apache-2.0", | |
| 361 | "optional": true, | |
| 362 | "dependencies": { | |
| 363 | "require-addon": "^1.1.0", | |
| 364 | "which-runtime": "^1.2.0" | |
| 365 | } | |
| 366 | }, | |
| 367 | "node_modules/hyperbee": { | |
| 368 | "version": "2.27.3", | |
| 369 | "resolved": "https://registry.npmjs.org/hyperbee/-/hyperbee-2.27.3.tgz", | |
| 370 | "integrity": "sha512-PXURH2U4juUZyJRKHTrY5z1zX851pmI1Q0jfv5F/hCIErDt/ND8jOZuxc3hfOLM9f0W3qJEDTMlV5AJBkVPy8w==", | |
| 371 | "license": "MIT", | |
| 372 | "dependencies": { | |
| 373 | "b4a": "^1.6.0", | |
| 374 | "codecs": "^3.0.0", | |
| 375 | "debounceify": "^1.0.0", | |
| 376 | "hypercore-errors": "^1.0.0", | |
| 377 | "mutexify": "^1.4.0", | |
| 378 | "protocol-buffers-encodings": "^1.2.0", | |
| 379 | "rache": "^1.0.0", | |
| 380 | "ready-resource": "^1.0.0", | |
| 381 | "resolve-reject-promise": "^1.1.0", | |
| 382 | "safety-catch": "^1.0.2", | |
| 383 | "streamx": "^2.12.4", | |
| 384 | "unslab": "^1.2.0" | |
| 385 | } | |
| 386 | }, | |
| 387 | "node_modules/hyperblobs": { | |
| 388 | "version": "2.9.0", | |
| 389 | "resolved": "https://registry.npmjs.org/hyperblobs/-/hyperblobs-2.9.0.tgz", | |
| 390 | "integrity": "sha512-kbJV2+QvXIcFejqOj2UgSjeeHMjWqP1n083vcI+j4E93MhV3oVV2L8/6X+K/id/KQrUmngPTzNTHHXKlkb1CEA==", | |
| 391 | "license": "Apache-2.0", | |
| 392 | "dependencies": { | |
| 393 | "b4a": "^1.6.1", | |
| 394 | "bare-events": "^2.5.0", | |
| 395 | "compact-encoding": "^2.18.0", | |
| 396 | "hypercore-crypto": "^3.6.1", | |
| 397 | "hypercore-errors": "^1.1.1", | |
| 398 | "mutexify": "^1.4.0", | |
| 399 | "speedometer": "^1.1.0", | |
| 400 | "streamx": "^2.13.2" | |
| 401 | } | |
| 402 | }, | |
| 403 | "node_modules/hypercore": { | |
| 404 | "version": "10.38.2", | |
| 405 | "resolved": "https://registry.npmjs.org/hypercore/-/hypercore-10.38.2.tgz", | |
| 406 | "integrity": "sha512-2vx42g2RKdP/o2XzlxKfvgEaZ3+h/B855gCR8iscUDnxUoOZej7vXZ2buex0sweDUEW5aEfMxFR6o5hncio3Nw==", | |
| 407 | "license": "MIT", | |
| 408 | "dependencies": { | |
| 409 | "@hyperswarm/secret-stream": "^6.0.0", | |
| 410 | "b4a": "^1.1.0", | |
| 411 | "bare-events": "^2.2.0", | |
| 412 | "big-sparse-array": "^1.0.3", | |
| 413 | "compact-encoding": "^2.11.0", | |
| 414 | "crc-universal": "^1.0.2", | |
| 415 | "fast-fifo": "^1.3.0", | |
| 416 | "flat-tree": "^1.9.0", | |
| 417 | "hypercore-crypto": "^3.2.1", | |
| 418 | "hypercore-errors": "^1.2.0", | |
| 419 | "hypercore-id-encoding": "^1.2.0", | |
| 420 | "is-options": "^1.0.1", | |
| 421 | "protomux": "^3.5.0", | |
| 422 | "quickbit-universal": "^2.2.0", | |
| 423 | "random-access-file": "^4.0.0", | |
| 424 | "random-array-iterator": "^1.0.0", | |
| 425 | "safety-catch": "^1.0.1", | |
| 426 | "sodium-universal": "^4.0.0", | |
| 427 | "streamx": "^2.12.4", | |
| 428 | "unslab": "^1.3.0", | |
| 429 | "xache": "^1.1.0", | |
| 430 | "z32": "^1.0.0" | |
| 431 | } | |
| 432 | }, | |
| 433 | "node_modules/hypercore-crypto": { | |
| 434 | "version": "3.6.1", | |
| 435 | "resolved": "https://registry.npmjs.org/hypercore-crypto/-/hypercore-crypto-3.6.1.tgz", | |
| 436 | "integrity": "sha512-ltIz2uDwy9pO/ZGTvqcjzyBkvt6O4cVm4r/nNxh0GFs/RbQtqP/i4wCvLEdmU7ptgtnw7fI67WYD1aHPuv4OVA==", | |
| 437 | "license": "MIT", | |
| 438 | "dependencies": { | |
| 439 | "b4a": "^1.6.6", | |
| 440 | "compact-encoding": "^2.15.0", | |
| 441 | "sodium-universal": "^5.0.0" | |
| 442 | } | |
| 443 | }, | |
| 444 | "node_modules/hypercore-crypto/node_modules/sodium-native": { | |
| 445 | "version": "5.1.0", | |
| 446 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.1.0.tgz", | |
| 447 | "integrity": "sha512-3RxgyWyJlhTsABPnJVpCI5CoTDANZTqqFrEPqr+kjfnRaBihpVtMUE3yTF40ukdoB1APXeoBNKF3MzZAIHg39g==", | |
| 448 | "license": "MIT", | |
| 449 | "dependencies": { | |
| 450 | "bare-assert": "^1.2.0", | |
| 451 | "require-addon": "^1.1.0", | |
| 452 | "which-runtime": "^1.2.1" | |
| 453 | }, | |
| 454 | "engines": { | |
| 455 | "bare": ">=1.16.0" | |
| 456 | } | |
| 457 | }, | |
| 458 | "node_modules/hypercore-crypto/node_modules/sodium-universal": { | |
| 459 | "version": "5.0.1", | |
| 460 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz", | |
| 461 | "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==", | |
| 462 | "license": "MIT", | |
| 463 | "dependencies": { | |
| 464 | "sodium-native": "^5.0.1" | |
| 465 | }, | |
| 466 | "peerDependencies": { | |
| 467 | "sodium-javascript": "~0.8.0" | |
| 468 | }, | |
| 469 | "peerDependenciesMeta": { | |
| 470 | "sodium-javascript": { | |
| 471 | "optional": true | |
| 472 | } | |
| 473 | } | |
| 474 | }, | |
| 475 | "node_modules/hypercore-errors": { | |
| 476 | "version": "1.5.0", | |
| 477 | "resolved": "https://registry.npmjs.org/hypercore-errors/-/hypercore-errors-1.5.0.tgz", | |
| 478 | "integrity": "sha512-5KQ/SuDxsvet+7qWA35Ay6zdD9WyAHQoyWHGcPUTbmJBd300gvNIJoi3oma7kp4TTCSzii6qYumNZe/s0j/saQ==", | |
| 479 | "license": "Apache-2.0", | |
| 480 | "dependencies": { | |
| 481 | "hypercore-id-encoding": "^1.3.0" | |
| 482 | } | |
| 483 | }, | |
| 484 | "node_modules/hypercore-id-encoding": { | |
| 485 | "version": "1.3.0", | |
| 486 | "resolved": "https://registry.npmjs.org/hypercore-id-encoding/-/hypercore-id-encoding-1.3.0.tgz", | |
| 487 | "integrity": "sha512-W6sHdGo5h7LXEsoWfKf/KfuROZmZRQDlGqJF2EPHW+noCK66Vvr0+zE6cL0vqQi18s0kQPeN7Sq3QyR0Ytc2VQ==", | |
| 488 | "license": "Apache-2.0", | |
| 489 | "dependencies": { | |
| 490 | "b4a": "^1.5.3", | |
| 491 | "z32": "^1.0.0" | |
| 492 | } | |
| 493 | }, | |
| 494 | "node_modules/hyperdrive": { | |
| 495 | "version": "11.13.4", | |
| 496 | "resolved": "https://registry.npmjs.org/hyperdrive/-/hyperdrive-11.13.4.tgz", | |
| 497 | "integrity": "sha512-PN0gClSRx+QBbsLGnGqo2ei9TavJN8d6D3jv2yNMlu7zgXSnqR6QcGo8mgyKnrzgR336oNfvQ2C2Y6cY5klScg==", | |
| 498 | "license": "Apache-2.0", | |
| 499 | "dependencies": { | |
| 500 | "hyperbee": "^2.11.1", | |
| 501 | "hyperblobs": "^2.3.0", | |
| 502 | "hypercore": "^10.33.0", | |
| 503 | "hypercore-errors": "^1.0.0", | |
| 504 | "is-options": "^1.0.2", | |
| 505 | "mirror-drive": "^1.2.0", | |
| 506 | "ready-resource": "^1.0.0", | |
| 507 | "safety-catch": "^1.0.2", | |
| 508 | "speedometer": "^1.1.0", | |
| 509 | "streamx": "^2.12.4", | |
| 510 | "sub-encoder": "^2.1.1", | |
| 511 | "unix-path-resolve": "^1.0.2" | |
| 512 | } | |
| 513 | }, | |
| 514 | "node_modules/is-options": { | |
| 515 | "version": "1.0.2", | |
| 516 | "resolved": "https://registry.npmjs.org/is-options/-/is-options-1.0.2.tgz", | |
| 517 | "integrity": "sha512-u+Ai74c8Q74aS8BuHwPdI1jptGOT1FQXgCq8/zv0xRuE+wRgSMEJLj8lVO8Zp9BeGb29BXY6AsNPinfqjkr7Fg==", | |
| 518 | "license": "MIT", | |
| 519 | "dependencies": { | |
| 520 | "b4a": "^1.1.1" | |
| 521 | } | |
| 522 | }, | |
| 523 | "node_modules/mirror-drive": { | |
| 524 | "version": "1.13.0", | |
| 525 | "resolved": "https://registry.npmjs.org/mirror-drive/-/mirror-drive-1.13.0.tgz", | |
| 526 | "integrity": "sha512-dwf2eIaeFgzv03hfW4aCQ/smNIGuxbsnNRrdGAGIyS70Hp0lbOoHXce2V34hGCoxmzn7jeLA4bHJiWlfKOu2Sg==", | |
| 527 | "license": "Apache-2.0", | |
| 528 | "dependencies": { | |
| 529 | "bare-events": "^2.8.2", | |
| 530 | "binary-stream-equals": "^1.0.0", | |
| 531 | "rabin-stream": "^2.0.0", | |
| 532 | "same-data": "^1.0.0", | |
| 533 | "speedometer": "^1.1.0", | |
| 534 | "streamx": "^2.22.1", | |
| 535 | "unix-path-resolve": "^1.0.2" | |
| 536 | } | |
| 537 | }, | |
| 538 | "node_modules/mutexify": { | |
| 539 | "version": "1.4.0", | |
| 540 | "resolved": "https://registry.npmjs.org/mutexify/-/mutexify-1.4.0.tgz", | |
| 541 | "integrity": "sha512-pbYSsOrSB/AKN5h/WzzLRMFgZhClWccf2XIB4RSMC8JbquiB0e0/SH5AIfdQMdyHmYtv4seU7yV/TvAwPLJ1Yg==", | |
| 542 | "license": "MIT", | |
| 543 | "dependencies": { | |
| 544 | "queue-tick": "^1.0.0" | |
| 545 | } | |
| 546 | }, | |
| 547 | "node_modules/nanoassert": { | |
| 548 | "version": "2.0.0", | |
| 549 | "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", | |
| 550 | "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==", | |
| 551 | "license": "ISC" | |
| 552 | }, | |
| 553 | "node_modules/noise-curve-ed": { | |
| 554 | "version": "2.1.0", | |
| 555 | "resolved": "https://registry.npmjs.org/noise-curve-ed/-/noise-curve-ed-2.1.0.tgz", | |
| 556 | "integrity": "sha512-zAzJx+VwZM3w6EA1hTmDhJfvAnCeBQn/1FAeZ0LtGxCcCtlAK/uJXQVF/eDVUOaAZ286lHlx77WJ+qj9SmsRRg==", | |
| 557 | "license": "ISC", | |
| 558 | "dependencies": { | |
| 559 | "b4a": "^1.1.0", | |
| 560 | "nanoassert": "^2.0.0", | |
| 561 | "sodium-universal": "^5.0.0" | |
| 562 | } | |
| 563 | }, | |
| 564 | "node_modules/noise-curve-ed/node_modules/sodium-native": { | |
| 565 | "version": "5.1.0", | |
| 566 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.1.0.tgz", | |
| 567 | "integrity": "sha512-3RxgyWyJlhTsABPnJVpCI5CoTDANZTqqFrEPqr+kjfnRaBihpVtMUE3yTF40ukdoB1APXeoBNKF3MzZAIHg39g==", | |
| 568 | "license": "MIT", | |
| 569 | "dependencies": { | |
| 570 | "bare-assert": "^1.2.0", | |
| 571 | "require-addon": "^1.1.0", | |
| 572 | "which-runtime": "^1.2.1" | |
| 573 | }, | |
| 574 | "engines": { | |
| 575 | "bare": ">=1.16.0" | |
| 576 | } | |
| 577 | }, | |
| 578 | "node_modules/noise-curve-ed/node_modules/sodium-universal": { | |
| 579 | "version": "5.0.1", | |
| 580 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz", | |
| 581 | "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==", | |
| 582 | "license": "MIT", | |
| 583 | "dependencies": { | |
| 584 | "sodium-native": "^5.0.1" | |
| 585 | }, | |
| 586 | "peerDependencies": { | |
| 587 | "sodium-javascript": "~0.8.0" | |
| 588 | }, | |
| 589 | "peerDependenciesMeta": { | |
| 590 | "sodium-javascript": { | |
| 591 | "optional": true | |
| 592 | } | |
| 593 | } | |
| 594 | }, | |
| 595 | "node_modules/noise-handshake": { | |
| 596 | "version": "4.2.0", | |
| 597 | "resolved": "https://registry.npmjs.org/noise-handshake/-/noise-handshake-4.2.0.tgz", | |
| 598 | "integrity": "sha512-9O/VTNX/E2/AToyMTTDU0J/4WhaXMTdqc2DHs9vf+snoZ0cenSBq0dNYTVV1snYYEkmo6QeRrYMxtqtoYnY+LA==", | |
| 599 | "license": "Apache-2.0", | |
| 600 | "dependencies": { | |
| 601 | "b4a": "^1.1.0", | |
| 602 | "nanoassert": "^2.0.0", | |
| 603 | "sodium-universal": "^5.0.0" | |
| 604 | } | |
| 605 | }, | |
| 606 | "node_modules/noise-handshake/node_modules/sodium-native": { | |
| 607 | "version": "5.1.0", | |
| 608 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.1.0.tgz", | |
| 609 | "integrity": "sha512-3RxgyWyJlhTsABPnJVpCI5CoTDANZTqqFrEPqr+kjfnRaBihpVtMUE3yTF40ukdoB1APXeoBNKF3MzZAIHg39g==", | |
| 610 | "license": "MIT", | |
| 611 | "dependencies": { | |
| 612 | "bare-assert": "^1.2.0", | |
| 613 | "require-addon": "^1.1.0", | |
| 614 | "which-runtime": "^1.2.1" | |
| 615 | }, | |
| 616 | "engines": { | |
| 617 | "bare": ">=1.16.0" | |
| 618 | } | |
| 619 | }, | |
| 620 | "node_modules/noise-handshake/node_modules/sodium-universal": { | |
| 621 | "version": "5.0.1", | |
| 622 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz", | |
| 623 | "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==", | |
| 624 | "license": "MIT", | |
| 625 | "dependencies": { | |
| 626 | "sodium-native": "^5.0.1" | |
| 627 | }, | |
| 628 | "peerDependencies": { | |
| 629 | "sodium-javascript": "~0.8.0" | |
| 630 | }, | |
| 631 | "peerDependenciesMeta": { | |
| 632 | "sodium-javascript": { | |
| 633 | "optional": true | |
| 634 | } | |
| 635 | } | |
| 636 | }, | |
| 637 | "node_modules/paparam": { | |
| 638 | "version": "1.10.1", | |
| 639 | "resolved": "https://registry.npmjs.org/paparam/-/paparam-1.10.1.tgz", | |
| 640 | "integrity": "sha512-viyQI64VIja0Za3njIzhoEP8ZVkgPowhZPuG0E96NwBfYJ6ZIyrrlhWGtFPkdN7eYLl2L8CTWL+wla0evm1KKQ==", | |
| 641 | "license": "Apache-2.0" | |
| 642 | }, | |
| 643 | "node_modules/protocol-buffers-encodings": { | |
| 644 | "version": "1.2.0", | |
| 645 | "resolved": "https://registry.npmjs.org/protocol-buffers-encodings/-/protocol-buffers-encodings-1.2.0.tgz", | |
| 646 | "integrity": "sha512-daeNPuKh1NlLD1uDfbLpD+xyUTc07nEtfHwmBZmt/vH0B7VOM+JOCOpDcx9ZRpqHjAiIkGqyTDi+wfGSl17R9w==", | |
| 647 | "license": "MIT", | |
| 648 | "dependencies": { | |
| 649 | "b4a": "^1.6.0", | |
| 650 | "signed-varint": "^2.0.1", | |
| 651 | "varint": "5.0.0" | |
| 652 | } | |
| 653 | }, | |
| 654 | "node_modules/protomux": { | |
| 655 | "version": "3.10.1", | |
| 656 | "resolved": "https://registry.npmjs.org/protomux/-/protomux-3.10.1.tgz", | |
| 657 | "integrity": "sha512-jgBqx8ZyaBWea/DFG4eOu1scOaeBwcnagiRC1XFVrjeGt7oAb0Pk5udPpBUpJ4DJBRjra50jD6YcZiQQTRqaaA==", | |
| 658 | "license": "MIT", | |
| 659 | "dependencies": { | |
| 660 | "b4a": "^1.3.1", | |
| 661 | "compact-encoding": "^2.5.1", | |
| 662 | "queue-tick": "^1.0.0", | |
| 663 | "safety-catch": "^1.0.1", | |
| 664 | "unslab": "^1.3.0" | |
| 665 | } | |
| 666 | }, | |
| 667 | "node_modules/queue-tick": { | |
| 668 | "version": "1.0.1", | |
| 669 | "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", | |
| 670 | "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", | |
| 671 | "license": "MIT" | |
| 672 | }, | |
| 673 | "node_modules/quickbit-native": { | |
| 674 | "version": "2.4.8", | |
| 675 | "resolved": "https://registry.npmjs.org/quickbit-native/-/quickbit-native-2.4.8.tgz", | |
| 676 | "integrity": "sha512-FcCcqI+nIAWGknqhtrYT5TSD7t/N+Xd8ctM+2PrIIBuwOi5hx0SxAvuPtzLIEMfT/2h9+fhBakUe2uALOHX6yw==", | |
| 677 | "license": "Apache-2.0", | |
| 678 | "optional": true, | |
| 679 | "dependencies": { | |
| 680 | "require-addon": "^1.1.0" | |
| 681 | } | |
| 682 | }, | |
| 683 | "node_modules/quickbit-universal": { | |
| 684 | "version": "2.2.0", | |
| 685 | "resolved": "https://registry.npmjs.org/quickbit-universal/-/quickbit-universal-2.2.0.tgz", | |
| 686 | "integrity": "sha512-w02i1R8n7+6pEKTud8DfF8zbFY9o7RtPlUc3jWbtCkDKvhbx/AvV7oNnz4/TcmsPGpSJS+fq5Ud6RH6+YPvSGg==", | |
| 687 | "license": "ISC", | |
| 688 | "dependencies": { | |
| 689 | "b4a": "^1.6.0", | |
| 690 | "simdle-universal": "^1.1.0" | |
| 691 | }, | |
| 692 | "optionalDependencies": { | |
| 693 | "quickbit-native": "^2.2.0" | |
| 694 | } | |
| 695 | }, | |
| 696 | "node_modules/rabin-native": { | |
| 697 | "version": "2.0.0", | |
| 698 | "resolved": "https://registry.npmjs.org/rabin-native/-/rabin-native-2.0.0.tgz", | |
| 699 | "integrity": "sha512-x1BlYdIWh+nk9G0scvxyscrYiDPJ7vQZepqqPlVUSInIko1Zxooi8cm6lzBghEGI9aN3DXiunC8FXLgy6kke3Q==", | |
| 700 | "license": "Apache-2.0", | |
| 701 | "dependencies": { | |
| 702 | "require-addon": "^1.1.0" | |
| 703 | } | |
| 704 | }, | |
| 705 | "node_modules/rabin-stream": { | |
| 706 | "version": "2.0.0", | |
| 707 | "resolved": "https://registry.npmjs.org/rabin-stream/-/rabin-stream-2.0.0.tgz", | |
| 708 | "integrity": "sha512-EzS2Ig/qsMBSk1PahC0O0IPdRZe3bspHHLWChhqW1feKz+J46tlVL3g4wWr4lrQdXGzABIMsOhvdT6bjXYMuUw==", | |
| 709 | "license": "Apache-2.0", | |
| 710 | "dependencies": { | |
| 711 | "rabin-native": "^2.0.0", | |
| 712 | "streamx": "^2.23.0" | |
| 713 | } | |
| 714 | }, | |
| 715 | "node_modules/rache": { | |
| 716 | "version": "1.0.0", | |
| 717 | "resolved": "https://registry.npmjs.org/rache/-/rache-1.0.0.tgz", | |
| 718 | "integrity": "sha512-e0k0g0w/8jOCB+7YqCIlOa+OJ38k0wrYS4x18pMSmqOvLKoyhmMhmQyCcvfY6VaP8D75cqkEnlakXs+RYYLqNg==", | |
| 719 | "license": "Apache-2.0" | |
| 720 | }, | |
| 721 | "node_modules/random-access-file": { | |
| 722 | "version": "4.1.2", | |
| 723 | "resolved": "https://registry.npmjs.org/random-access-file/-/random-access-file-4.1.2.tgz", | |
| 724 | "integrity": "sha512-GQM6R78DceZDcQod8KxlDFwXIiUvlvuy1EOzxTDsjuDjW5NlnlZi0MOk6iI4itAj/2vcvdqcEExYbVpC/dJcEw==", | |
| 725 | "license": "MIT", | |
| 726 | "dependencies": { | |
| 727 | "bare-fs": "^4.0.1", | |
| 728 | "bare-path": "^3.0.0", | |
| 729 | "random-access-storage": "^3.0.0" | |
| 730 | }, | |
| 731 | "optionalDependencies": { | |
| 732 | "fs-native-extensions": "^1.3.1" | |
| 733 | } | |
| 734 | }, | |
| 735 | "node_modules/random-access-storage": { | |
| 736 | "version": "3.0.2", | |
| 737 | "resolved": "https://registry.npmjs.org/random-access-storage/-/random-access-storage-3.0.2.tgz", | |
| 738 | "integrity": "sha512-Es9maUyWdJXWKckKy9s1+vT+DEgAt+PBb9lxPaake/0EDUsHehloKGv9v1zimS2V3gpFAcQXubvc1Rgci2sDPQ==", | |
| 739 | "license": "MIT", | |
| 740 | "dependencies": { | |
| 741 | "bare-events": "^2.2.0", | |
| 742 | "queue-tick": "^1.0.0" | |
| 743 | } | |
| 744 | }, | |
| 745 | "node_modules/random-array-iterator": { | |
| 746 | "version": "1.0.0", | |
| 747 | "resolved": "https://registry.npmjs.org/random-array-iterator/-/random-array-iterator-1.0.0.tgz", | |
| 748 | "integrity": "sha512-u7xCM93XqKEvPTP6xZp2ehttcAemKnh73oKNf1FvzuVCfpt6dILDt1Kxl1LeBjm2iNIeR49VGFhy4Iz3yOun+Q==", | |
| 749 | "license": "MIT" | |
| 750 | }, | |
| 751 | "node_modules/read-write-mutexify": { | |
| 752 | "version": "2.1.0", | |
| 753 | "resolved": "https://registry.npmjs.org/read-write-mutexify/-/read-write-mutexify-2.1.0.tgz", | |
| 754 | "integrity": "sha512-fDw/p5/acI1ytVY1UbxEDma/ej1yJH/n9NcjS9YNzcE6sPBPWdlru3ydRa/UBowUg4zqOvNMD5SOGYJrlQ6MzQ==", | |
| 755 | "license": "MIT" | |
| 756 | }, | |
| 757 | "node_modules/ready-resource": { | |
| 758 | "version": "1.2.0", | |
| 759 | "resolved": "https://registry.npmjs.org/ready-resource/-/ready-resource-1.2.0.tgz", | |
| 760 | "integrity": "sha512-nfcco/8iAFV0M+2PYnmIc+/xY0iRb35d42HFHQ7AfjulbGEAFa+XWpByfwSyeVeiBoMLLFVMv1HixxNCqzSQ1g==", | |
| 761 | "license": "MIT", | |
| 762 | "dependencies": { | |
| 763 | "bare-events": "^2.2.0" | |
| 764 | } | |
| 765 | }, | |
| 766 | "node_modules/refcounter": { | |
| 767 | "version": "1.0.0", | |
| 768 | "resolved": "https://registry.npmjs.org/refcounter/-/refcounter-1.0.0.tgz", | |
| 769 | "integrity": "sha512-1WosVzUy0kPUaPMEtlNDwm99UsteALIhXXR8rerELoa63WkYIXAl0hxgwPFrIYBRWZPGUyekQ04FRtPJ7dHk9w==", | |
| 770 | "license": "Apache-2.0" | |
| 771 | }, | |
| 772 | "node_modules/require-addon": { | |
| 773 | "version": "1.2.0", | |
| 774 | "resolved": "https://registry.npmjs.org/require-addon/-/require-addon-1.2.0.tgz", | |
| 775 | "integrity": "sha512-VNPDZlYgIYQwWp9jMTzljx+k0ZtatKlcvOhktZ/anNPI3dQ9NXk7cq2U4iJ1wd9IrytRnYhyEocFWbkdPb+MYA==", | |
| 776 | "license": "Apache-2.0", | |
| 777 | "dependencies": { | |
| 778 | "bare-addon-resolve": "^1.3.0" | |
| 779 | }, | |
| 780 | "engines": { | |
| 781 | "bare": ">=1.10.0" | |
| 782 | } | |
| 783 | }, | |
| 784 | "node_modules/resolve-reject-promise": { | |
| 785 | "version": "1.1.0", | |
| 786 | "resolved": "https://registry.npmjs.org/resolve-reject-promise/-/resolve-reject-promise-1.1.0.tgz", | |
| 787 | "integrity": "sha512-LWsTOA91AqzBTjSGgX79Tc130pwcBK6xjpJEO+qRT5IKZ6bGnHKcc8QL3upUBcWuU8OTIDzKK2VNSwmmlqvAVg==", | |
| 788 | "license": "MIT" | |
| 789 | }, | |
| 790 | "node_modules/rocksdb-native": { | |
| 791 | "version": "3.15.0", | |
| 792 | "resolved": "https://registry.npmjs.org/rocksdb-native/-/rocksdb-native-3.15.0.tgz", | |
| 793 | "integrity": "sha512-czYx0hOzKAy+HYmJROZm7thvJ1tHFWHNkGI3nomumXWzrJghJdrWo4MIhehGX4OTff0lnOCoB2zu6ekzIKY0rQ==", | |
| 794 | "license": "Apache-2.0", | |
| 795 | "dependencies": { | |
| 796 | "compact-encoding": "^2.15.0", | |
| 797 | "ready-resource": "^1.0.0", | |
| 798 | "refcounter": "^1.0.0", | |
| 799 | "require-addon": "^1.0.2", | |
| 800 | "resolve-reject-promise": "^1.1.0", | |
| 801 | "signal-promise": "^1.0.3", | |
| 802 | "streamx": "^2.16.1" | |
| 803 | }, | |
| 804 | "engines": { | |
| 805 | "bare": ">=1.16.0" | |
| 806 | } | |
| 807 | }, | |
| 808 | "node_modules/safety-catch": { | |
| 809 | "version": "1.0.3", | |
| 810 | "resolved": "https://registry.npmjs.org/safety-catch/-/safety-catch-1.0.3.tgz", | |
| 811 | "integrity": "sha512-Zq+J1TefpoEq/HTUabo0YXX5MNvttjWYODGohgPBO2jfko8Wqx3JYMgE823szDFVamdH5PlpByvfiWScTdSYDA==", | |
| 812 | "license": "MIT" | |
| 813 | }, | |
| 814 | "node_modules/same-data": { | |
| 815 | "version": "1.0.0", | |
| 816 | "resolved": "https://registry.npmjs.org/same-data/-/same-data-1.0.0.tgz", | |
| 817 | "integrity": "sha512-Eqn7N2yV+aKMlUHTRqUwYG1Iv0cJqjlvLKj/GoP5PozJn361QaOYX14+v87r7NqQUZC22noP/LfLrSQiPwAygw==", | |
| 818 | "license": "MIT" | |
| 819 | }, | |
| 820 | "node_modules/signal-promise": { | |
| 821 | "version": "1.0.3", | |
| 822 | "resolved": "https://registry.npmjs.org/signal-promise/-/signal-promise-1.0.3.tgz", | |
| 823 | "integrity": "sha512-WBgv0UnIq2C+Aeh0/n+IRpP6967eIx9WpynTUoiW3isPpfe1zu2LJzyfXdo9Tgef8yR/sGjcMvoUXD7EYdiz+g==", | |
| 824 | "license": "MIT" | |
| 825 | }, | |
| 826 | "node_modules/signed-varint": { | |
| 827 | "version": "2.0.1", | |
| 828 | "resolved": "https://registry.npmjs.org/signed-varint/-/signed-varint-2.0.1.tgz", | |
| 829 | "integrity": "sha512-abgDPg1106vuZZOvw7cFwdCABddfJRz5akcCcchzTbhyhYnsG31y4AlZEgp315T7W3nQq5P4xeOm186ZiPVFzw==", | |
| 830 | "license": "MIT", | |
| 831 | "dependencies": { | |
| 832 | "varint": "~5.0.0" | |
| 833 | } | |
| 834 | }, | |
| 835 | "node_modules/simdle-native": { | |
| 836 | "version": "1.3.9", | |
| 837 | "resolved": "https://registry.npmjs.org/simdle-native/-/simdle-native-1.3.9.tgz", | |
| 838 | "integrity": "sha512-Isc8sP4OiiIU0mpslD4GHEnR0VQWvR/54WN7YtwEDkNdTJVWtpmvsSvsgRlw5BNGxdYXlVRegdnrSu10H/PhvA==", | |
| 839 | "license": "Apache-2.0", | |
| 840 | "optional": true, | |
| 841 | "dependencies": { | |
| 842 | "b4a": "^1.6.0", | |
| 843 | "require-addon": "^1.1.0" | |
| 844 | } | |
| 845 | }, | |
| 846 | "node_modules/simdle-universal": { | |
| 847 | "version": "1.1.2", | |
| 848 | "resolved": "https://registry.npmjs.org/simdle-universal/-/simdle-universal-1.1.2.tgz", | |
| 849 | "integrity": "sha512-3n3w1bs+uwgHKQjt6arez83EywNlhZzYvNOhvAASTl/8KqNIcqr6aHyGt3JRlfuUC7iB0tomJRPlJ2cRGIpBzA==", | |
| 850 | "license": "ISC", | |
| 851 | "dependencies": { | |
| 852 | "b4a": "^1.6.0" | |
| 853 | }, | |
| 854 | "optionalDependencies": { | |
| 855 | "simdle-native": "^1.1.1" | |
| 856 | } | |
| 857 | }, | |
| 858 | "node_modules/sodium-native": { | |
| 859 | "version": "4.3.3", | |
| 860 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-4.3.3.tgz", | |
| 861 | "integrity": "sha512-OnxSlN3uyY8D0EsLHpmm2HOFmKddQVvEMmsakCrXUzSd8kjjbzL413t4ZNF3n0UxSwNgwTyUvkmZHTfuCeiYSw==", | |
| 862 | "license": "MIT", | |
| 863 | "dependencies": { | |
| 864 | "require-addon": "^1.1.0" | |
| 865 | } | |
| 866 | }, | |
| 867 | "node_modules/sodium-secretstream": { | |
| 868 | "version": "1.2.0", | |
| 869 | "resolved": "https://registry.npmjs.org/sodium-secretstream/-/sodium-secretstream-1.2.0.tgz", | |
| 870 | "integrity": "sha512-q/DbraNFXm1KfCiiZvapmz5UC3OlpirYFIvBK2MhGaOFSb3gRyk8OXTi17UI9SGfshQNCpsVvlopogbzZNyW6Q==", | |
| 871 | "license": "MIT", | |
| 872 | "dependencies": { | |
| 873 | "b4a": "^1.1.1", | |
| 874 | "sodium-universal": "^5.0.0" | |
| 875 | } | |
| 876 | }, | |
| 877 | "node_modules/sodium-secretstream/node_modules/sodium-native": { | |
| 878 | "version": "5.1.0", | |
| 879 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.1.0.tgz", | |
| 880 | "integrity": "sha512-3RxgyWyJlhTsABPnJVpCI5CoTDANZTqqFrEPqr+kjfnRaBihpVtMUE3yTF40ukdoB1APXeoBNKF3MzZAIHg39g==", | |
| 881 | "license": "MIT", | |
| 882 | "dependencies": { | |
| 883 | "bare-assert": "^1.2.0", | |
| 884 | "require-addon": "^1.1.0", | |
| 885 | "which-runtime": "^1.2.1" | |
| 886 | }, | |
| 887 | "engines": { | |
| 888 | "bare": ">=1.16.0" | |
| 889 | } | |
| 890 | }, | |
| 891 | "node_modules/sodium-secretstream/node_modules/sodium-universal": { | |
| 892 | "version": "5.0.1", | |
| 893 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz", | |
| 894 | "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==", | |
| 895 | "license": "MIT", | |
| 896 | "dependencies": { | |
| 897 | "sodium-native": "^5.0.1" | |
| 898 | }, | |
| 899 | "peerDependencies": { | |
| 900 | "sodium-javascript": "~0.8.0" | |
| 901 | }, | |
| 902 | "peerDependenciesMeta": { | |
| 903 | "sodium-javascript": { | |
| 904 | "optional": true | |
| 905 | } | |
| 906 | } | |
| 907 | }, | |
| 908 | "node_modules/sodium-universal": { | |
| 909 | "version": "4.0.1", | |
| 910 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-4.0.1.tgz", | |
| 911 | "integrity": "sha512-sNp13PrxYLaUFHTGoDKkSDFvoEu51bfzE12RwGlqU1fcrkpAOK0NvizaJzOWV0Omtk9me2+Pnbjcf/l0efxuGQ==", | |
| 912 | "license": "MIT", | |
| 913 | "dependencies": { | |
| 914 | "sodium-native": "^4.0.0" | |
| 915 | }, | |
| 916 | "peerDependencies": { | |
| 917 | "sodium-javascript": "~0.8.0" | |
| 918 | }, | |
| 919 | "peerDependenciesMeta": { | |
| 920 | "sodium-javascript": { | |
| 921 | "optional": true | |
| 922 | } | |
| 923 | } | |
| 924 | }, | |
| 925 | "node_modules/speedometer": { | |
| 926 | "version": "1.1.0", | |
| 927 | "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-1.1.0.tgz", | |
| 928 | "integrity": "sha512-z/wAiTESw2XVPssY2XRcme4niTc4S5FkkJ4gknudtVoc33Zil8TdTxHy5torRcgqMqksJV2Yz8HQcvtbsnw0mQ==", | |
| 929 | "license": "MIT" | |
| 930 | }, | |
| 931 | "node_modules/streamx": { | |
| 932 | "version": "2.25.0", | |
| 933 | "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.25.0.tgz", | |
| 934 | "integrity": "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==", | |
| 935 | "license": "MIT", | |
| 936 | "dependencies": { | |
| 937 | "events-universal": "^1.0.0", | |
| 938 | "fast-fifo": "^1.3.2", | |
| 939 | "text-decoder": "^1.1.0" | |
| 940 | } | |
| 941 | }, | |
| 942 | "node_modules/sub-encoder": { | |
| 943 | "version": "2.1.3", | |
| 944 | "resolved": "https://registry.npmjs.org/sub-encoder/-/sub-encoder-2.1.3.tgz", | |
| 945 | "integrity": "sha512-Xxx04ygZo/1J3yHvaSA6VhDmiSaBQkw/PmO3YnnYFXle+tfOGToC6FcDpIfMztWZXJzuKG14b/57HMkiL58C6A==", | |
| 946 | "license": "Apache-2.0", | |
| 947 | "dependencies": { | |
| 948 | "b4a": "^1.6.0", | |
| 949 | "codecs": "^3.1.0" | |
| 950 | } | |
| 951 | }, | |
| 952 | "node_modules/teex": { | |
| 953 | "version": "1.0.1", | |
| 954 | "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", | |
| 955 | "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", | |
| 956 | "license": "MIT", | |
| 957 | "dependencies": { | |
| 958 | "streamx": "^2.12.5" | |
| 959 | } | |
| 960 | }, | |
| 961 | "node_modules/text-decoder": { | |
| 962 | "version": "1.2.7", | |
| 963 | "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", | |
| 964 | "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", | |
| 965 | "license": "Apache-2.0", | |
| 966 | "dependencies": { | |
| 967 | "b4a": "^1.6.4" | |
| 968 | } | |
| 969 | }, | |
| 970 | "node_modules/timeout-refresh": { | |
| 971 | "version": "2.0.1", | |
| 972 | "resolved": "https://registry.npmjs.org/timeout-refresh/-/timeout-refresh-2.0.1.tgz", | |
| 973 | "integrity": "sha512-SVqEcMZBsZF9mA78rjzCrYrUs37LMJk3ShZ851ygZYW1cMeIjs9mL57KO6Iv5mmjSQnOe/29/VAfGXo+oRCiVw==", | |
| 974 | "license": "MIT" | |
| 975 | }, | |
| 976 | "node_modules/unix-path-resolve": { | |
| 977 | "version": "1.0.2", | |
| 978 | "resolved": "https://registry.npmjs.org/unix-path-resolve/-/unix-path-resolve-1.0.2.tgz", | |
| 979 | "integrity": "sha512-kG4g5nobBBaMnH2XbrS4sLUXEpx4nY2J3C6KAlAUcnahG2HChxSPVKWYrqEq76iTo+cyMkLUjqxGaQR2tz097Q==", | |
| 980 | "license": "MIT" | |
| 981 | }, | |
| 982 | "node_modules/unslab": { | |
| 983 | "version": "1.3.0", | |
| 984 | "resolved": "https://registry.npmjs.org/unslab/-/unslab-1.3.0.tgz", | |
| 985 | "integrity": "sha512-YATkfKAFj47kTzmiQrWXMyRvaVrHsW6MEALa4bm+FhiA2YG4oira+Z3DXN6LrYOYn2Y8eO94Lwl9DOHjs1FpoQ==", | |
| 986 | "license": "Apache-2.0", | |
| 987 | "dependencies": { | |
| 988 | "b4a": "^1.6.6" | |
| 989 | } | |
| 990 | }, | |
| 991 | "node_modules/varint": { | |
| 992 | "version": "5.0.0", | |
| 993 | "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz", | |
| 994 | "integrity": "sha512-gC13b/bWrqQoKY2EmROCZ+AR0jitc6DnDGaQ6Ls9QpKmuSgJB1eQ7H3KETtQm7qSdMWMKCmsshyCmUwMLh3OAA==", | |
| 995 | "license": "MIT" | |
| 996 | }, | |
| 997 | "node_modules/which-runtime": { | |
| 998 | "version": "1.3.2", | |
| 999 | "resolved": "https://registry.npmjs.org/which-runtime/-/which-runtime-1.3.2.tgz", | |
| 1000 | "integrity": "sha512-5kwCfWml7+b2NO7KrLMhYihjRx0teKkd3yGp1Xk5Vaf2JGdSh+rgVhEALAD9c/59dP+YwJHXoEO7e8QPy7gOkw==", | |
| 1001 | "license": "Apache-2.0" | |
| 1002 | }, | |
| 1003 | "node_modules/xache": { | |
| 1004 | "version": "1.2.1", | |
| 1005 | "resolved": "https://registry.npmjs.org/xache/-/xache-1.2.1.tgz", | |
| 1006 | "integrity": "sha512-igRS6jPreJ54ABdzhh4mCDXcz+XMaWO2q1ABRV2yWYuk29jlp8VT7UBdCqNkX7rpYBbXsebVVKkwIuYZjyZNqA==", | |
| 1007 | "license": "MIT" | |
| 1008 | }, | |
| 1009 | "node_modules/z32": { | |
| 1010 | "version": "1.1.0", | |
| 1011 | "resolved": "https://registry.npmjs.org/z32/-/z32-1.1.0.tgz", | |
| 1012 | "integrity": "sha512-1WUHy+VS6d0HPNspDxvLssBbeQjXMjSnpv0vH82vRAUfg847NmX3OXozp/hRP5jPhxBbrVzrgvAt+UsGNzRFQQ==", | |
| 1013 | "license": "MIT", | |
| 1014 | "dependencies": { | |
| 1015 | "b4a": "^1.5.3" | |
| 1016 | } | |
| 1017 | } | |
| 1018 | } | |
| 1019 | } |
| … | … | @@ -0,0 +1,21 @@ |
| 1 | { | |
| 2 | "name": "hyper-inspector", | |
| 3 | "version": "0.1.0", | |
| 4 | "description": "Inspect, audit, and manage Hypercore/Hyperdrive corestores", | |
| 5 | "type": "module", | |
| 6 | "main": "index.js", | |
| 7 | "bin": { | |
| 8 | "hyper-inspector": "./bin/cli.js" | |
| 9 | }, | |
| 10 | "scripts": { | |
| 11 | "test": "brittle test/*.js" | |
| 12 | }, | |
| 13 | "dependencies": { | |
| 14 | "b4a": "^1.6.7", | |
| 15 | "corestore": "^6.18.0", | |
| 16 | "hyperdrive": "^11.13.0", | |
| 17 | "rocksdb-native": "^3.13.0", | |
| 18 | "paparam": "^1.10.0" | |
| 19 | }, | |
| 20 | "license": "MIT" | |
| 21 | } |