hash

package
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 22, 2026 License: GPL-3.0 Imports: 11 Imported by: 0

Documentation

Overview

Package hash parses and normalizes the two Stash fingerprints moansubs keys releases on — oshash and phash — and implements the multi-index hashing (MIH) block extraction the bucketed lookup API is built on. See PLAN.md's "Data model" and "Lookup: bucketed by default" sections; the bit ranges and hash-handling rules here are a fixed API contract, not an implementation detail.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EndpointHash

func EndpointHash(normalized string) string

EndpointHash returns the first 12 hex characters of sha256(normalized) — the lookup key GET /api/v1/lookup/stash/{ehash}/{stash_id} and the batch endpoint's stash_ids entries use in place of the endpoint URL itself (WP-C9a spec: "keeps URLs out of paths and the wire shape stable"). normalized must already be NormalizeStashEndpoint's output — this function does not normalize on its own, so two spellings of the same endpoint must be normalized identically before they reach here.

func Hamming

func Hamming(a, b PHash) int

Hamming returns the Hamming distance between two phashes: the number of differing bits, via popcount(a^b).

func NormalizeStashEndpoint

func NormalizeStashEndpoint(s string) (string, error)

NormalizeStashEndpoint normalizes a stash-box GraphQL endpoint URL (e.g. "https://stashdb.org/graphql") to a canonical form: trimmed, scheme and host lowercased, path kept as-is (WP-C9a spec: "trim spaces, lowercase host, keep path"). The scheme is lowercased too — not called out explicitly by the spec, but required for "HTTPS://StashDB.org/graphql" and "https://stashdb.org/graphql" to normalize identically, which the spec's own worked example demands.

Client and server both call this before EndpointHash, so the two ends always agree on which ehash a given endpoint hashes to — this is the single source of truth for that normalization, same role internal/hash plays for oshash/phash.

func ParseStashID

func ParseStashID(s string) (string, error)

ParseStashID lowercases s and validates it is a 36-character UUID shape (WP-C9a spec), rejecting anything else. Shared by the server (upload validation, batch lookup) and the plugin (building a lookup/upload request), so both ends reject the same malformed ids the same way.

Types

type Blocks

type Blocks [5]uint16

Blocks holds the 5 multi-index-hashing (MIH) block values used for bucketed phash lookup.

type OSHash

type OSHash string

OSHash is Stash's oshash: the OpenSubtitles moviehash algorithm's output, always a 16-character zero-padded lowercase hex string. Unlike phash, oshash's %016x formatting is already zero-padded at the source (Stash), so ParseOSHash only needs to validate, not pad.

func ComputeOSHash

func ComputeOSHash(r io.ReaderAt, size int64) (OSHash, error)

ComputeOSHash implements the OpenSubtitles moviehash algorithm exactly as Stash's pkg/hash/oshash computes it (ported from that package's FromReader): file size plus the little-endian uint64 sum of 8-byte words in the first and last chunk of the file, formatted as %016x. Ported to an io.ReaderAt+size signature rather than io.ReadSeeker so callers (e.g. an os.File or an in-memory fixture in tests) don't need seek semantics.

For files smaller than 2*chunkSize, Stash does NOT error like the original OpenSubtitles reference implementation — chunkSize shrinks to the largest multiple of 8 not exceeding the file size, so the head and tail reads overlap (files under 64KiB may double-count almost every byte). Files of 8 bytes or fewer are rejected: there is no room for even one 8-byte word.

func ParseOSHash

func ParseOSHash(s string) (OSHash, error)

ParseOSHash normalizes s to lowercase and validates it is exactly 16 hex characters, rejecting anything else (short/long strings, non-hex characters). Stash always emits this format, but inputs may arrive from elsewhere (e.g. a client's lookup request), so case is normalized before the strict length/charset check.

func (OSHash) BucketPrefix

func (h OSHash) BucketPrefix() string

BucketPrefix returns the first 5 hex characters — the oshash lookup bucket key fixed by PLAN.md's "Lookup: bucketed by default" as an API contract between client and server.

func (OSHash) String

func (h OSHash) String() string

String returns the 16-char lowercase hex form.

type PHash

type PHash uint64

PHash is Stash's 64-bit perceptual hash (goimagehash PerceptionHash over a 5x5 sprite, see PLAN.md Reference), held as the bit pattern of an unsigned 64-bit integer. Never compare two PHash values as strings — Stash's own string form is unpadded (see ParsePHash) — always compare via Hamming or Blocks.

func PHashFromBigint

func PHashFromBigint(v int64) PHash

PHashFromBigint reverses ToBigint: reinterprets a signed bigint's bit pattern back to the unsigned uint64 phash.

func ParsePHash

func ParsePHash(s string) (PHash, error)

ParsePHash parses phash the way Stash's GraphQL API actually emits it: strconv.FormatUint(v, 16) with NO left-padding, so a hash with leading zero bits arrives as fewer than 16 hex characters (PLAN.md hash rule 1). Accepts 1-16 hex characters; callers get the zero-padded canonical form back from String.

func (PHash) Blocks

func (h PHash) Blocks() Blocks

Blocks extracts the 5 MIH blocks by shift-and-mask. By pigeonhole, any two phashes within Hamming distance 4 must match exactly in at least one of these 5 blocks — that property is what makes bucketed lookup exact rather than approximate (see the MIH property test in phash_test.go).

func (PHash) String

func (h PHash) String() string

String always emits the zero-padded 16-char lowercase hex form — the canonical storage/display representation, as opposed to Stash's unpadded wire form that ParsePHash accepts.

func (PHash) ToBigint

func (h PHash) ToBigint() int64

ToBigint reinterprets the uint64 bit pattern as a signed int64 for Postgres `bigint` storage, matching how Stash itself stores phash in SQLite (PLAN.md hash rule 2: store signed, reinterpret on read).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL