Go bindings for libbbf, a high-performance binary container format for digital comics and manga.
https://github.com/ef1500/libbbf
- Go 92.3%
- C 4.2%
- C++ 3.5%
|
|
||
|---|---|---|
| .forgejo/workflows | ||
| examples | ||
| internal/capi | ||
| thirdparty/libbbf | ||
| .gitignore | ||
| bbf_capi_impl.cpp | ||
| bbf_test.go | ||
| builder.go | ||
| builder_cgo.go | ||
| errors.go | ||
| format.go | ||
| go.mod | ||
| go.sum | ||
| libbbf_impl.cpp | ||
| media_type.go | ||
| reader.go | ||
| README.md | ||
| xxhash_impl.c | ||
go-bbf
Go bindings for libbbf v3, a high-performance binary container format for digital comics and manga.
Installation
go get git.macco.dev/insidiousfiddler/go-bbf
Requirements:
- Go 1.17+
- C++17 compiler (gcc 7+, clang 5+)
- CGO enabled
Usage
Creating a BBF file
package main
import (
"log"
bbf "git.macco.dev/insidiousfiddler/go-bbf"
)
func main() {
// Create a new BBF file
builder, err := bbf.NewBuilder("output.bbf")
if err != nil {
log.Fatal(err)
}
defer builder.Close()
// Add metadata
builder.AddMetadata("title", "My Comic")
builder.AddMetadata("author", "John Doe")
// Add pages (media type is auto-detected from extension)
builder.AddPage("page001.png")
builder.AddPage("page002.png")
builder.AddPage("page003.jpg")
// Add sections (chapters) - v3 uses parent by title
builder.AddTopLevelSection("Chapter 1", 0)
builder.AddTopLevelSection("Chapter 2", 2)
// Nested sections: parent is specified by title
builder.AddSection("Part A", 0, "Chapter 1") // Child of "Chapter 1"
// Finalize and write the file
if err := builder.Finalize(); err != nil {
log.Fatal(err)
}
}
Reading a BBF file
package main
import (
"fmt"
"log"
"os"
bbf "git.macco.dev/insidiousfiddler/go-bbf"
)
func main() {
// Open BBF file
reader, err := bbf.Open("comic.bbf")
if err != nil {
log.Fatal(err)
}
defer reader.Close()
// Get file info
fmt.Printf("Pages: %d\n", reader.PageCount())
fmt.Printf("Assets: %d\n", reader.AssetCount())
// Read metadata
if title, ok := reader.GetMetadata("title"); ok {
fmt.Printf("Title: %s\n", title)
}
// Read sections
for _, section := range reader.Sections() {
fmt.Printf("Section: %s (starts at page %d)\n",
section.Title, section.StartPageIndex)
}
// Extract a single page
data, mediaType, err := reader.ReadPage(0)
if err != nil {
log.Fatal(err)
}
os.WriteFile("page_0"+mediaType.String(), data, 0644)
// Extract all pages to a directory
reader.ExtractAll("./output")
// Verify file integrity
if err := reader.Verify(); err != nil {
log.Fatal("Integrity check failed:", err)
}
}
API Reference
Builder
// Create builder with default config (4KB alignment)
func NewBuilder(outputPath string) (*Builder, error)
// Create builder with custom configuration
func NewBuilderWithConfig(outputPath string, config BuilderConfig) (*Builder, error)
// BuilderConfig for v3 features
type BuilderConfig struct {
AlignmentExponent uint8 // Alignment = 2^AlignmentExponent (default: 12 for 4KB)
ReamThresholdExponent uint8 // Variable alignment threshold
Petrified bool // Index structures before assets
}
func (b *Builder) AddPage(imagePath string) error
func (b *Builder) AddPageWithType(imagePath string, mediaType MediaType) error
func (b *Builder) AddPageWithTypeAndFlags(imagePath string, mediaType MediaType, flags uint64) error
// v3: Sections use parent title (not index) for hierarchy
func (b *Builder) AddSection(title string, startPage uint64, parentTitle string) error
func (b *Builder) AddTopLevelSection(title string, startPage uint64) error
func (b *Builder) AddMetadata(key, value string) error
func (b *Builder) Finalize() error
func (b *Builder) Close() error
Reader
func Open(path string) (*Reader, error)
func OpenReader(r io.ReaderAt, size int64) (*Reader, error)
func (r *Reader) PageCount() int
func (r *Reader) AssetCount() int
func (r *Reader) Sections() []Section
func (r *Reader) Metadata() map[string]string
func (r *Reader) GetMetadata(key string) (string, bool)
func (r *Reader) ReadPage(index int) ([]byte, MediaType, error)
func (r *Reader) ReadAsset(index int) ([]byte, MediaType, error)
func (r *Reader) ExtractPage(index int, w io.Writer) error
func (r *Reader) ExtractAll(dir string) error
// v3 header feature queries
func (r *Reader) IsPetrified() bool
func (r *Reader) HasVariableReam() bool
func (r *Reader) Alignment() uint64
func (r *Reader) ReamThreshold() uint64
func (r *Reader) Verify() error // Verifies all assets (XXH3-128) and index hash (XXH3-64)
func (r *Reader) VerifyAsset(index int) error
func (r *Reader) Close() error
MediaType
const (
MediaTypeUnknown MediaType = 0x00
MediaTypeAVIF MediaType = 0x01
MediaTypePNG MediaType = 0x02
MediaTypeWEBP MediaType = 0x03
MediaTypeJXL MediaType = 0x04
MediaTypeBMP MediaType = 0x05
MediaTypeGIF MediaType = 0x07
MediaTypeTIFF MediaType = 0x08
MediaTypeJPG MediaType = 0x09
)
func DetectMediaType(extension string) MediaType
func DetectMediaTypeFromPath(path string) MediaType
BBF Format v3
BBF (Bound Book Format) v3 "Sapphire" is a binary container optimized for comics/manga:
- 64-byte header with footer offset for flexible layout
- 256-byte footer with all 64-bit entry counts
- Configurable alignment (default 4KB) for DirectStorage/mmap performance
- XXH3-128 hashing for asset integrity and deduplication
- XXH3-64 hashing for index integrity verification
- Hierarchical sections with parent by title (not index)
- Key-value metadata with optional parent identifiers
- Petrified layout option (index structures before assets)
- Variable ream size support for small asset optimization
Format Structure
[Header - 64 bytes]
Magic: "BBF3"
Footer Offset (for index location)
Alignment/Ream configuration
[Asset Data - aligned to 2^alignmentExponent]
Raw image data (deduplicated by XXH3-128 hash)
[Index Region]
String Pool
Asset Table (48 bytes each)
Page Table (16 bytes each)
Section Table (32 bytes each)
Metadata Table (32 bytes each)
[Footer - 256 bytes]
Table offsets and counts
Index hash (XXH3-64)
License
MIT License - see LICENSE for details.
Credits
- libbbf by ef1500
- xxhash for hashing
- zeebo/xxh3 for Go XXH3 implementation