npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ts-antirez-sds

v1.0.2

Published

Simple Dynamic Strings (the C string library used by Redis), translated to TypeScript

Readme

ts-antirez-sds

A direct TypeScript translation of sds (Simple Dynamic Strings), the C string library written by Salvatore Sanfilippo for Redis.

If you find this project useful, you can support this and further ports at ko-fi.com/scottmoore0.

License

BSD 2-Clause License

sds (Simple Dynamic Strings, original C version) - Copyright (c) 2006-2015 Salvatore Sanfilippo

ts-antirez-sds (direct TypeScript translation) - Copyright (c) 2026 Scott Moore

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Usage

sds is a C string library that hands the caller a char * pointing at the payload, with a packed length/capacity header sitting in the bytes immediately before the pointer. The TypeScript translation preserves this model exactly: an sds value is a CPtr of the shape { buf: Uint8Array, off: number } whose off indexes the first payload byte. C-string arguments (e.g. for sdsnew, sdscat) are also CPtr values, NUL-terminated.

Key differences from the original C version:

  • All functions are reachable as named ES module exports - no header includes, no link step.
  • Strings live in Uint8Array byte buffers rather than malloc'd char *. Garbage collection replaces the C-side sdsfree discipline; calling sdsfree is still safe but no longer required.
  • The header types sdshdr5, sdshdr8, sdshdr16, sdshdr32, sdshdr64 are exported for callers that need to read the per-sds header layout directly.

Installation

Install from npm:

npm install ts-antirez-sds

Or with your preferred package manager:

yarn add ts-antirez-sds
pnpm add ts-antirez-sds

Or clone the repository:

git clone https://github.com/ScottMoore0/ts-antirez-sds.git

Importing

When installed from npm:

import {
  sdsnew,
  sdsempty,
  sdscat,
  sdscpy,
  sdsfree,
  sdslen,
  sdsavail,
  sdsalloc,
} from 'ts-antirez-sds';

Quick example

import { sdsnew, sdscat, sdslen } from 'ts-antirez-sds';

function cstr(s: string) {
  const buf = new Uint8Array(s.length + 1);
  for (let i = 0; i < s.length; i++) buf[i] = s.charCodeAt(i);
  return { buf, off: 0 };
}

function sdsToString(s: any): string {
  const out: number[] = [];
  for (let i = s.off; i < s.buf.length && s.buf[i] !== 0; i++) out.push(s.buf[i]);
  return String.fromCharCode(...out);
}

let s = sdsnew(cstr('hello'));
s = sdscat(s, cstr(', world'));
console.log(sdsToString(s));   // "hello, world"
console.log(sdslen(s));        // 12

Building

Unlike the original C version, ts-antirez-sds requires no compilation step at consumption time. The published package ships pre-built JavaScript and .d.ts files in dist/. The TypeScript sources are kept in the repository for reference and can be compiled locally:

npm install
npm run build

TypeScript Compiler

If your project uses TypeScript, use a typical ES module configuration in tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "node",
    "strict": false,
    "esModuleInterop": true
  },
  "include": ["src/**/*.ts"]
}

Important: the translated source uses patterns that emulate C pointer arithmetic and unsafe type casts. It is intentionally not strict-compliant. The published package ships compiled .js and .d.ts files in dist/, so consumers do not need to type-check the translation. If you import the .ts source directly (rather than the package), isolate it in its own module and wrap it in a strictly-typed API surface.

Node.js / tsx

ts-antirez-sds runs as a plain ES module:

node -e "import('ts-antirez-sds').then(m => console.log(Object.keys(m).length, 'exports'))"

Or under tsx directly against source:

npx tsx -e "import { sdsnew, sdslen } from 'ts-antirez-sds'; const s = sdsnew({ buf: new TextEncoder().encode('hello\0'), off: 0 }); console.log(sdslen(s));"

Or with Deno (use the npm: specifier):

import { sdsnew, sdslen } from 'npm:ts-antirez-sds';

Bundling

ts-antirez-sds has zero npm dependencies, so it bundles cleanly with esbuild, Rollup, or Vite:

npx esbuild --bundle --platform=neutral --outfile=out.js my-app.ts

API

The exports mirror the C API of sds. The most commonly used:

Construction and lifecycle

  • sdsnewlen(init, len) - new sds with explicit length (binary-safe).
  • sdsempty() - new empty sds.
  • sdsnew(cstr) - new sds from a NUL-terminated C string pointer.
  • sdsdup(s) - duplicate.
  • sdsfree(s) - release the underlying buffer (optional in JS).

Length and capacity (header-inline helpers)

  • sdslen(s) - logical length (bytes of payload, NUL not counted).
  • sdsavail(s) - free bytes between length and capacity.
  • sdsalloc(s) - capacity (bytes the header tracks as allocated).
  • sdssetlen(s, len) / sdsinclen(s, inc) / sdssetalloc(s, alloc) - direct header writes for advanced uses.
  • sdsupdatelen(s) - recompute length from the NUL terminator.
  • sdsclear(s) - logical length to 0; capacity untouched.
  • sdsMakeRoomFor(s, addlen) / sdsRemoveFreeSpace(s) - grow / shrink-to-fit.
  • sdsAllocSize(s) / sdsAllocPtr(s) - inspect underlying allocation.
  • sdsIncrLen(s, incr) - bump length after an external write.
  • sdsgrowzero(s, len) - grow to length, NUL-fill the gap.

Mutation

  • sdscatlen(s, t, len) / sdscat(s, t) / sdscatsds(s, t) - append.
  • sdscpylen(s, t, len) / sdscpy(s, t) - overwrite.
  • sdscatprintf(s, fmt, ...) / sdscatvprintf(s, fmt, ap) / sdscatfmt(s, fmt, ...) - printf-style append.
  • sdstrim(s, cset) - trim characters in cset from both ends.
  • sdsrange(s, start, end) - keep [start, end] in place.
  • sdstolower(s) / sdstoupper(s) - in-place ASCII case conversion.
  • sdsmapchars(s, from, to, setlen) - byte-wise translation.

Comparison and conversion

  • sdscmp(s1, s2) - memcmp-like comparison.
  • sdsll2str(buf, value) / sdsull2str(buf, value) - integer to string.
  • sdsfromlonglong(value) - new sds from a long long.

Splitting, joining, escaping

  • sdssplitlen(s, len, sep, seplen, count) - split into an array of sds.
  • sdsfreesplitres(tokens, count) - free the split result.
  • sdssplitargs(line, argc) - shell-style argument split.
  • sdsjoin(argv, argc, sep) / sdsjoinsds(argv, argc, sep, seplen) - reverse of split.
  • sdscatrepr(s, p, len) - append a quoted, C-escaped representation.

Allocator hooks

  • sds_malloc(size) / sds_realloc(ptr, size) / sds_free(ptr) - the malloc/realloc/free wrappers used internally.

Tests

The repository includes a small reference-vector test suite covering construction, length/capacity, concatenation, copy, trim, range, and case conversion:

npm test

Caveats

The following limitations from the upstream C version still apply:

  • Caller-allocated growth - sdsMakeRoomFor and sdsgrowzero reallocate the underlying buffer; functions that may grow return the (possibly new) sds value, and the caller MUST replace the old reference with the returned one (s = sdscat(s, ...)). This matches the C convention.
  • Binary-safe but NUL-terminated - sds always writes a trailing NUL after the logical length, so existing C string consumers can read the payload, but the logical length is the source of truth, not strlen. Always prefer sdslen(s) over scanning for NUL.
  • Standard alphabet only for sdscatrepr - the escape representation matches the upstream's choices (printable ASCII, hex escapes for non-printable bytes); it is not configurable.
  • printf-family conversion specifiers follow the C reference - sdscatprintf and sdscatfmt accept the same format specifiers documented by the upstream sds.
  • sdsalloc is identical to sdslen for short strings - sds picks the smallest header that fits the requested length. For len < 32 the header is sdshdr5, which has no separate alloc field; the upstream sdsalloc is defined to return the length in this case (see sds.h case SDS_TYPE_5: return SDS_TYPE_5_LEN(flags)). For len >= 32 the header is sdshdr8/16/32/64, which has a real alloc field. The translation preserves both behaviours exactly. If your code depends on sdsalloc reflecting a separate capacity, ensure the sds is constructed with len >= 32 (e.g. via sdsnewlen with explicit length, or after sdsMakeRoomFor).

The following C-specific caveats do not apply to the TypeScript version:

  • Memory leaks from forgotten sdsfree - JavaScript's garbage collector reclaims unreferenced sds values automatically. Calling sdsfree remains safe but is no longer load-bearing.
  • Thread safety - JavaScript is single-threaded; no special locking is needed when accessing an sds from one event-loop turn.
  • C standard compliance - the code runs wherever modern TypeScript / JavaScript runs (Node.js, Deno, Bun, browsers).

Acknowledgements