Documentation
¶
Overview ¶
Package mskblob is an external asset container: a self-describing pack file that bundles many resources (their bytes plus a full index) into one sidecar artifact kept outside the Go binary. A build tool uses it to write blobs, and a consuming application imports it to load and serve them at runtime.
It is the runtime/format companion of miniskin (github.com/pablo-botella/miniskin — https://pkg.go.dev/github.com/pablo-botella/miniskin), the build-time assembler that produces these .blob files; miniskin supports blobs using this component from v0.3.12. mskblob is standalone with no dependencies.
Manifest ¶
An Item is the single unit of both a manifest and a blob's index. A Manifest (an optional id plus items) is the interchange format: build a blob from one with Write, or produce one from a blob with Blob.Manifest. The declarative fields (URL, Key, Filename, RestType, Src) say what to pack; Size/CRC32/Offset are computed by the writer — informational in a manifest and ignored when building.
Format ¶
BLOCK A — header (64 bytes, little-endian): 0 magic "MSPK" (4) 4 version uint8 5 flags uint8 (reserved) 6 reserved [2]byte 8 count uint32 number of index entries 12 dataCRC uint32 crc32(IEEE) of block D 16 dataOffset uint32 absolute offset where block D begins 20 reserved pad to 64 BLOCK B — id (64 bytes): guid ASCII, zero-padded (the sync token) BLOCK C — index (count entries, each 16-byte aligned): entrysize uint32 total bytes of this entry (incl. itself + pad) key string\0 asset key size uint64 data byte length crc32 uint32 crc32 of the data (also the ETag) restype int32 resource-type flags (Static, Parse, …) url string\0 http route, RELATIVE to the blob's base filename string\0 source filename pad \0… to the next 16-byte boundary BLOCK D — data: concatenated bytes at dataOffset; each entry's offset is the running sum of sizes.
Index ¶
- Constants
- func MimeByExt(name string) string
- func NewID() (string, error)
- func Write(path string, items []Item, opts Options) (string, error)
- type Blob
- func (b *Blob) Bytes(it *Item) ([]byte, error)
- func (b *Blob) Close() error
- func (b *Blob) GetByKey(key string) *Item
- func (b *Blob) GetByURL(url string) *Item
- func (b *Blob) Handler(base string, mw Middleware) http.Handler
- func (b *Blob) Header() Header
- func (b *Blob) Items() []Item
- func (b *Blob) Manifest() Manifest
- func (b *Blob) Reader(it *Item) *io.SectionReader
- type FolderOptions
- type Header
- type Item
- type Manifest
- func (m *Manifest) AddFile(url, src string) *Manifest
- func (m *Manifest) AddFolder(dir string, opts FolderOptions) (int, error)
- func (m *Manifest) AddItem(it Item) *Manifest
- func (m Manifest) MarshalJSON() ([]byte, error)
- func (m *Manifest) UnmarshalJSON(b []byte) error
- func (m *Manifest) Write(path string) (string, error)
- type Middleware
- type Options
- type RestType
Constants ¶
const ( DispatchAuto = 0 // let the blob serve it (a static entry; anything else → 404) DispatchDone = 1 // the middleware already wrote the whole response )
Dispatch codes a Middleware returns to drive Blob.Handler.
Variables ¶
This section is empty.
Functions ¶
func MimeByExt ¶
MimeByExt maps a path's extension to a Content-Type, matching miniskin's static guessMime criterion.
func Write ¶
Write packs items into a self-describing blob at path and returns its id. Items are written in lexical URL order so the data (and dataCRC) is reproducible; only the guid varies unless Options.ID is set. Each item's Size/CRC32/Offset are recomputed from its Src — any values already set are ignored. With Options.SkipUnchanged and a pinned Options.ID, Write is a no-op (returning that id, no Src read) when path already holds a blob with that id.
Types ¶
type Blob ¶
type Blob struct {
// contains filtered or unexported fields
}
Blob is an opened, indexed blob ready to serve.
func Load ¶
Load opens a blob and, when expectID is non-empty, verifies its guid matches (so a stale/mismatched data file is rejected).
func (*Blob) Bytes ¶
Bytes reads an entry's whole content into memory. For large resources prefer Blob.Reader, which streams without allocating the whole thing.
func (*Blob) GetByKey ¶
GetByKey returns the entry with a logical key, or nil if absent (folding case when the blob is case-insensitive). This is how non-served resources (templates, parse fragments) are reached.
func (*Blob) GetByURL ¶
GetByURL returns the entry served under a relative URL, or nil if absent. The lookup folds case when the blob is case-insensitive. (A pure in-memory lookup — it cannot fail.)
func (*Blob) Handler ¶
func (b *Blob) Handler(base string, mw Middleware) http.Handler
Handler is the package's default sub-mux for a blob mounted at base: register only base on your own mux (yourMux.Handle(base, b.Handler(base, mw))) and it routes everything below by relative URL. It is a building block, not a web server — no listening, TLS, or config lives here.
For each request it looks the base-stripped path up and calls mw (when non-nil) with the matched item (nil if the URL is absent). mw returns:
- DispatchAuto (0): the blob serves it — but the default handler serves ONLY static entries (streamed lazily, ETag = crc32, If-None-Match → 304). Anything else (template, response, or an absent URL) is treated as if it weren't there → 404. Serve those from the middleware. A nil mw is always auto.
- DispatchDone (1): mw already wrote the response; nothing more is done.
- any other int: returned as that HTTP status via http.Error.
type FolderOptions ¶
type FolderOptions struct {
Recurse bool // descend into subdirectories (default: top level only)
Include string // comma-separated globs matched on the file name (default: all)
Exclude string // comma-separated globs matched on the file name
Base string // write Src relative to this dir (default: the scanned dir)
Prefix string // optional URL/key prefix prepended to every added entry
NoCase bool // case-insensitive Include/Exclude matching (e.g. *.jpg matches .JPG)
}
FolderOptions filters and shapes an Manifest.AddFolder scan.
type Header ¶
type Header struct {
Version uint8
ID string
Count uint32
DataCRC32 uint32
DataSize int64
NoCase bool // URL/Key lookups fold case
}
Header is a blob's metadata (blocks A + B).
func ReadHeader ¶
ReadHeader reads only a blob's header (cheap: 128 bytes). Used for cache checks (matching the guid) and inspection.
type Item ¶
type Item struct {
URL string
Key string
Filename string
RestType RestType
Src string
Size uint64
CRC32 uint32
Offset uint64
}
Item is one resource: the unit of both a manifest and a blob's index. The declarative fields describe what to pack; Src is the build-time source path; Size/CRC32/Offset are computed by the writer — informational in a manifest and ignored when building.
In JSON, restype is a human flag mask (comma-separated names, "static,parse"); the computed numbers are fixed-width 32-bit hex strings (0x%08X) — readable and free of float64 precision loss. The 64-bit Size and Offset are split into low/high halves (sizeLow/sizeHigh, offsetLow/offsetHigh), like the binary header. On decode the computed numbers are ignored (recomputed on build), so only the declarative fields are read; restype also accepts a hex/decimal value.
func (Item) MarshalJSON ¶
MarshalJSON renders the item with hex-string numbers; 64-bit Size/Offset split into low/high halves.
func (*Item) UnmarshalJSON ¶
UnmarshalJSON reads only the declarative fields (url, key, filename, restype, src). The computed numbers (crc32, size*, offset*) are ignored — they are recomputed when a blob is built, so there is nothing to parse.
type Manifest ¶
type Manifest struct {
ID string
Version uint8
Count uint32
DataCRC32 uint32
NoCase bool
Items []Item
}
Manifest is the interchange format: an id plus the items. Version, Count and DataCRC32 are informational (filled from a blob, emitted for inspection, and ignored on decode).
func NewManifest ¶
NewManifest starts an empty manifest. Pass an id to pin the blob's guid; leave it empty to get a fresh one when written.
func (*Manifest) AddFile ¶
AddFile appends one static resource: bytes are read from src at Write time and served under url. The common case — no knowledge of the index internals needed.
func (*Manifest) AddFolder ¶
func (m *Manifest) AddFolder(dir string, opts FolderOptions) (int, error)
AddFolder scans dir and appends a static item per matching file: URL is the path relative to dir (Prefix prepended), Src is relative to opts.Base (default dir). It returns how many entries were added. As everywhere, size/crc/offset are left for Write to compute. With NoCase, the file name and the globs are folded to lower case before matching (the stored URL/Src keep their real case).
func (*Manifest) AddItem ¶
AddItem appends one declarative item (size/crc/offset are computed at Write time, so you only fill the descriptive fields). Returns the manifest for chaining.
func (Manifest) MarshalJSON ¶
MarshalJSON renders the manifest header (DataCRC32 as a hex string) plus items. The header is always present (id, count and nocase are emitted even when empty, so the shape is consistent); version/dataCRC32 appear only once a blob is built.
func (*Manifest) UnmarshalJSON ¶
UnmarshalJSON reads the declarative header (id, nocase) and items, plus DataCRC32 (kept as an expected value a caller can check a rebuild against); version and count are ignored.
type Middleware ¶
Middleware is the single hook Blob.Handler calls for every routed request, with the matched item — which is nil when the URL is absent, so it can also answer unknown routes. Its return value decides what happens next: DispatchAuto, DispatchDone, or any other int as an HTTP status code.
type Options ¶
type Options struct {
ID string // guid; a fresh v4 GUID is generated when empty
NoCase bool // record the blob as case-insensitive (URL/Key lookups fold case)
// SkipUnchanged short-circuits the write when a blob already exists at path
// with the same pinned ID. A pinned id is the content's identity, so a target
// already carrying it holds the same bytes by contract — Write returns that id
// without reading any Src or rewriting the file. Needs a pinned ID: with an
// empty ID every write mints a fresh guid, so there is nothing to compare and
// the build proceeds normally.
SkipUnchanged bool
}
Options tunes how a blob is written.
type RestType ¶
type RestType int32
RestType is a bitmask of resource-type flags (mirroring miniskin item type= flags), JSON-encoded as a fixed-width hex string ("0x00000005" = Static|Parse, "" when none).