| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999 |
- // Copyright 2014 The lldb Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // The storage space management.
- package lldb
- import (
- "bytes"
- "errors"
- "fmt"
- "io"
- "sort"
- "strings"
- "sync"
- "github.com/cznic/internal/buffer"
- "github.com/cznic/mathutil"
- "github.com/cznic/zappy"
- )
- const (
- maxBuf = maxRq + 20
- )
- // Options are passed to the NewAllocator to amend some configuration. The
- // compatibility promise is the same as of struct types in the Go standard
- // library - introducing changes can be made only by adding new exported
- // fields, which is backward compatible as long as client code uses field names
- // to assign values of imported struct types literals.
- //
- // NOTE: No options are currently defined.
- type Options struct{}
- // AllocStats record statistics about a Filer. It can be optionally filled by
- // Allocator.Verify, if successful.
- type AllocStats struct {
- Handles int64 // total valid handles in use
- Compression int64 // number of compressed blocks
- TotalAtoms int64 // total number of atoms == AllocAtoms + FreeAtoms
- AllocBytes int64 // bytes allocated (after decompression, if/where used)
- AllocAtoms int64 // atoms allocated/used, including relocation atoms
- Relocations int64 // number of relocated used blocks
- FreeAtoms int64 // atoms unused
- AllocMap map[int64]int64 // allocated block size in atoms -> count of such blocks
- FreeMap map[int64]int64 // free block size in atoms -> count of such blocks
- }
- /*
- Allocator implements "raw" storage space management (allocation and
- deallocation) for a low level of a DB engine. The storage is an abstraction
- provided by a Filer.
- The terms MUST or MUST NOT, if/where used in the documentation of Allocator,
- written in all caps as seen here, are a requirement for any possible
- alternative implementations aiming for compatibility with this one.
- Filer file
- A Filer file, or simply 'file', is a linear, contiguous sequence of blocks.
- Blocks may be either free (currently unused) or allocated (currently used).
- Some blocks may eventually become virtual in a sense as they may not be
- realized in the storage (sparse files).
- Free Lists Table
- File starts with a FLT. This table records heads of 14 doubly linked free
- lists. The zero based index (I) vs minimal size of free blocks in that list,
- except the last one which registers free blocks of size 4112+ atoms:
- MinSize == 2^I
- For example 0 -> 1, 1 -> 2, ... 12 -> 4096.
- Each entry in the FLT is 8 bytes in netwtork order, MSB MUST be zero, ie. the
- slot value is effectively only 7 bytes. The value is the handle of the head of
- the respective doubly linked free list. The FLT size is 14*8 == 112(0x70)
- bytes. If the free blocks list for any particular size is empty, the respective
- FLT slot is zero. Sizes of free blocks in one list MUST NOT overlap with sizes
- of free lists in other list. For example, even though a free block of size 2
- technically is of minimal size >= 1, it MUST NOT be put to the list for slot 0
- (minimal size 1), but in slot 1( minimal size 2).
- slot 0: sizes [1, 2)
- slot 1: sizes [2, 4)
- slot 2: sizes [4, 8)
- ...
- slot 11: sizes [2048, 4096)
- slot 12: sizes [4096, 4112)
- slot 13: sizes [4112, inf)
- The last FLT slot collects all free blocks bigger than its minimal size. That
- still respects the 'no overlap' invariant.
- File blocks
- A block is a linear, contiguous sequence of atoms. The first and last atoms of
- a block provide information about, for example, whether the block is free or
- used, what is the size of the block, etc. Details are discussed elsewhere. The
- first block of a file starts immediately after FLT, ie. at file offset
- 112(0x70).
- Block atoms
- An atom is a fixed size piece of a block (and thus of a file too); it is 16
- bytes long. A consequence is that for a valid file:
- filesize == 0 (mod 16)
- The first atom of the first block is considered to be atom #1.
- Block handles
- A handle is an integer referring to a block. The reference is the number of the
- atom the block starts with. Put in other way:
- handle == offset/16 - 6
- offset == 16 * (handle + 6)
- `offset` is the offset of the first byte of the block, measured in bytes
- - as in fseek(3). Handle has type `int64`, but only the lower 7 bytes may be
- nonzero while referring to a block, both in code as well as when persisted in
- the the file's internal bookkeeping structures - see 'Block types' bellow. So a
- handle is effectively only `uint56`. This also means that the maximum usable
- size of a file is 2^56 atoms. That is 2^60 bytes == 1 exabyte (10^18 bytes).
- Nil handles
- A handle with numeric value of '0' refers to no block.
- Zero padding
- A padding is used to round-up a block size to be a whole number of atoms. Any
- padding, if present, MUST be all zero bytes. Note that the size of padding is
- in [0, 15].
- Content wiping
- When a block is deallocated, its data content is not wiped as the added
- overhead may be substantial while not necessarily needed. Client code should
- however overwrite the content of any block having sensitive data with eg. zeros
- (good compression) - before deallocating the block.
- Block tags
- Every block is tagged in its first byte (a head tag) and last byte (tail tag).
- Block types are:
- 1. Short content used block (head tags 0x00-0xFB)
- 2. Long content used block (head tag 0xFC)
- 3. Relocated used block (head tag 0xFD)
- 4. Short, single atom, free block (head tag 0xFE)
- 5. Long free block (head tag 0xFF)
- Note: Relocated used block, 3. above (head tag 0xFD) MUST NOT refer to blocks
- other then 1. or 2. above (head tags 0x00-0xFC).
- Content blocks
- Used blocks (head tags 0x00-0xFC) tail tag distinguish used/unused block and if
- content is compressed or not.
- Content compression
- The tail flag of an used block is one of
- CC == 0 // Content is not compressed.
- CC == 1 // Content is in zappy compression format.
- If compression of written content is enabled, there are two cases: If
- compressed size < original size then the compressed content should be written
- if it will save at least one atom of the block. If compressed size >= original
- size then the compressed content should not be used.
- It's recommended to use compression. For example the BTrees implementation
- assumes compression is used. Using compression may cause a slowdown in some
- cases while it may as well cause a speedup.
- Short content block
- Short content block carries content of length between N == 0(0x00) and N ==
- 251(0xFB) bytes.
- |<-first atom start ... last atom end->|
- +---++-- ... --+-- ... --++------+
- | 0 || 1... | 0x*...0x*E || 0x*F |
- +---++-- ... --+-- ... --++------+
- | N || content | padding || CC |
- +---++-- ... --+-- ... --++------+
- A == (N+1)/16 + 1 // The number of atoms in the block [1, 16]
- padding == 15 - (N+1)%16 // Length of the zero padding
- Long content block
- Long content block carries content of length between N == 252(0xFC) and N ==
- 65787(0x100FB) bytes.
- |<-first atom start ... last atom end->|
- +------++------+-- ... --+-- ... --++------+
- | 0 || 1..2 | 3... | 0x*...0x*E || 0x*F |
- +------++------+-- ... --+-- ... --++------+
- | 0xFC || M | content | padding || CC |
- +------++------+-- ... --+-- ... --++------+
- A == (N+3)/16 + 1 // The number of atoms in the block [16, 4112]
- M == N % 0x10000 // Stored as 2 bytes in network byte order
- padding == 15 - (N+3)%16 // Length of the zero padding
- Relocated used block
- Relocated block allows to permanently assign a handle to some content and
- resize the content anytime afterwards without having to update all the possible
- existing references; the handle can be constant while the content size may be
- dynamic. When relocating a block, any space left by the original block content,
- above this single atom block, MUST be reclaimed.
- Relocations MUST point only to a used short or long block == blocks with tags
- 0x00...0xFC.
- +------++------+---------++----+
- | 0 || 1..7 | 8...14 || 15 |
- +------++------+---------++----+
- | 0xFD || H | padding || 0 |
- +------++------+---------++----+
- H is the handle of the relocated block in network byte order.
- Free blocks
- Free blocks are the result of space deallocation. Free blocks are organized in
- one or more doubly linked lists, abstracted by the FLT interface. Free blocks
- MUST be "registered" by putting them in such list. Allocator MUST reuse a big
- enough free block, if such exists, before growing the file size. When a free
- block is created by deallocation or reallocation it MUST be joined with any
- adjacently existing free blocks before "registering". If the resulting free
- block is now a last block of a file, the free block MUST be discarded and the
- file size MUST be truncated accordingly instead. Put differently, there MUST
- NOT ever be a free block at the file end.
- A single free atom
- Is an unused block of size 1 atom.
- +------++------+--------++------+
- | 0 || 1..7 | 8...14 || 15 |
- +------++------+--------++------+
- | 0xFE || P | N || 0xFE |
- +------++------+--------++------+
- P and N, stored in network byte order, are the previous and next free block
- handles in the doubly linked list to which this free block belongs.
- A long unused block
- Is an unused block of size > 1 atom.
- +------++------+-------+---------+- ... -+----------++------+
- | 0 || 1..7 | 8..14 | 15...21 | | Z-7..Z-1 || Z |
- +------++------+-------+---------+- ... -+----------++------+
- | 0xFF || S | P | N | Leak | S || 0xFF |
- +------++------+-------+---------+- ... -+----------++------+
- Z == 16 * S - 1
- S is the size of this unused block in atoms. P and N are the previous and next
- free block handles in the doubly linked list to which this free block belongs.
- Leak contains any data the block had before deallocating this block. See also
- the subtitle 'Content wiping' above. S, P and N are stored in network byte
- order. Large free blocks may trigger a consideration of file hole punching of
- the Leak field - for some value of 'large'.
- Note: Allocator methods vs CRUD[1]:
- Alloc [C]reate
- Get [R]ead
- Realloc [U]pdate
- Free [D]elete
- Note: No Allocator method returns io.EOF.
- [1]: http://en.wikipedia.org/wiki/Create,_read,_update_and_delete
- */
- type Allocator struct {
- f Filer
- flt flt
- Compress bool // enables content compression
- cache cache
- m map[int64]*node
- lru lst
- expHit int64
- expMiss int64
- cacheSz int
- hit uint16
- miss uint16
- mu sync.Mutex
- }
- // NewAllocator returns a new Allocator. To open an existing file, pass its
- // Filer. To create a "new" file, pass a Filer which file is of zero size.
- func NewAllocator(f Filer, opts *Options) (a *Allocator, err error) {
- if opts == nil { // Enforce *Options is always passed
- return nil, errors.New("NewAllocator: nil opts passed")
- }
- a = &Allocator{
- f: f,
- cacheSz: 10,
- }
- a.cinit()
- switch x := f.(type) {
- case *RollbackFiler:
- x.afterRollback = func() error {
- a.cinit()
- return a.flt.load(a.f, 0)
- }
- case *ACIDFiler0:
- x.RollbackFiler.afterRollback = func() error {
- a.cinit()
- return a.flt.load(a.f, 0)
- }
- }
- sz, err := f.Size()
- if err != nil {
- return
- }
- a.flt.init()
- if sz == 0 {
- var b [fltSz]byte
- if err = a.f.BeginUpdate(); err != nil {
- return
- }
- if _, err = f.WriteAt(b[:], 0); err != nil {
- a.f.Rollback()
- return
- }
- return a, a.f.EndUpdate()
- }
- return a, a.flt.load(f, 0)
- }
- // CacheStats reports cache statistics.
- //
- //TODO return a struct perhaps.
- func (a *Allocator) CacheStats() (buffersUsed, buffersTotal int, bytesUsed, bytesTotal, hits, misses int64) {
- buffersUsed = len(a.m)
- buffersTotal = buffersUsed + len(a.cache)
- bytesUsed = a.lru.size()
- bytesTotal = bytesUsed + a.cache.size()
- hits = a.expHit
- misses = a.expMiss
- return
- }
- func (a *Allocator) cinit() {
- for h, n := range a.m {
- a.cache.put(a.lru.remove(n))
- delete(a.m, h)
- }
- if a.m == nil {
- a.m = map[int64]*node{}
- }
- }
- func (a *Allocator) cadd(b []byte, h int64) {
- if len(a.m) < a.cacheSz {
- n := a.cache.get(len(b))
- n.h = h
- copy(n.b, b)
- a.m[h] = a.lru.pushFront(n)
- return
- }
- // cache full
- delete(a.m, a.cache.put(a.lru.removeBack()).h)
- n := a.cache.get(len(b))
- n.h = h
- copy(n.b, b)
- a.m[h] = a.lru.pushFront(n)
- return
- }
- func (a *Allocator) cfree(h int64) {
- n, ok := a.m[h]
- if !ok { // must have been evicted
- return
- }
- a.cache.put(a.lru.remove(n))
- delete(a.m, h)
- }
- // Alloc allocates storage space for b and returns the handle of the new block
- // with content set to b or an error, if any. The returned handle is valid only
- // while the block is used - until the block is deallocated. No two valid
- // handles share the same value within the same Filer, but any value of a
- // handle not referring to any used block may become valid any time as a result
- // of Alloc.
- //
- // Invoking Alloc on an empty Allocator is guaranteed to return handle with
- // value 1. The intended use of content of handle 1 is a root "directory" of
- // other data held by an Allocator.
- //
- // Passing handles not obtained initially from Alloc or not anymore valid to
- // any other Allocator methods can result in an irreparably corrupted database.
- func (a *Allocator) Alloc(b []byte) (handle int64, err error) {
- pbuf := buffer.Get(zappy.MaxEncodedLen(len(b)))
- defer buffer.Put(pbuf)
- buf := *pbuf
- buf, _, cc, err := a.makeUsedBlock(buf, b)
- if err != nil {
- return
- }
- if handle, err = a.alloc(buf, cc); err == nil {
- a.cadd(b, handle)
- }
- return
- }
- func (a *Allocator) alloc(b []byte, cc byte) (h int64, err error) {
- rqAtoms := n2atoms(len(b))
- if h = a.flt.find(rqAtoms); h == 0 { // must grow
- var sz int64
- if sz, err = a.f.Size(); err != nil {
- return
- }
- h = off2h(sz)
- err = a.writeUsedBlock(h, cc, b)
- return
- }
- // Handle is the first item of a free blocks list.
- tag, s, prev, next, err := a.nfo(h)
- if err != nil {
- return
- }
- if tag != tagFreeShort && tag != tagFreeLong {
- err = &ErrILSEQ{Type: ErrExpFreeTag, Off: h2off(h), Arg: int64(tag)}
- return
- }
- if prev != 0 {
- err = &ErrILSEQ{Type: ErrHead, Off: h2off(h), Arg: prev}
- return
- }
- if s < int64(rqAtoms) {
- err = &ErrILSEQ{Type: ErrSmall, Arg: int64(rqAtoms), Arg2: s, Off: h2off(h)}
- return
- }
- if err = a.unlink(h, s, prev, next); err != nil {
- return
- }
- if s > int64(rqAtoms) {
- freeH := h + int64(rqAtoms)
- freeAtoms := s - int64(rqAtoms)
- if err = a.link(freeH, freeAtoms); err != nil {
- return
- }
- }
- return h, a.writeUsedBlock(h, cc, b)
- }
- // Free deallocates the block referred to by handle or returns an error, if
- // any.
- //
- // After Free succeeds, handle is invalid and must not be used.
- //
- // Handle must have been obtained initially from Alloc and must be still valid,
- // otherwise a database may get irreparably corrupted.
- func (a *Allocator) Free(handle int64) (err error) {
- if handle <= 0 || handle > maxHandle {
- return &ErrINVAL{"Allocator.Free: handle out of limits", handle}
- }
- a.cfree(handle)
- return a.free(handle, 0, true)
- }
- func (a *Allocator) free(h, from int64, acceptRelocs bool) (err error) {
- tag, atoms, _, n, err := a.nfo(h)
- if err != nil {
- return
- }
- switch tag {
- default:
- // nop
- case tagUsedLong:
- // nop
- case tagUsedRelocated:
- if !acceptRelocs {
- return &ErrILSEQ{Type: ErrUnexpReloc, Off: h2off(h), Arg: h2off(from)}
- }
- if err = a.free(n, h, false); err != nil {
- return
- }
- case tagFreeShort, tagFreeLong:
- return &ErrINVAL{"Allocator.Free: attempt to free a free block at off", h2off(h)}
- }
- return a.free2(h, atoms)
- }
- func (a *Allocator) free2(h, atoms int64) (err error) {
- sz, err := a.f.Size()
- if err != nil {
- return
- }
- ltag, latoms, lp, ln, err := a.leftNfo(h)
- if err != nil {
- return
- }
- if ltag != tagFreeShort && ltag != tagFreeLong {
- latoms = 0
- }
- var rtag byte
- var ratoms, rp, rn int64
- isTail := h2off(h)+atoms*16 == sz
- if !isTail {
- if rtag, ratoms, rp, rn, err = a.nfo(h + atoms); err != nil {
- return
- }
- }
- if rtag != tagFreeShort && rtag != tagFreeLong {
- ratoms = 0
- }
- switch {
- case latoms == 0 && ratoms == 0:
- // -> isolated <-
- if isTail { // cut tail
- return a.f.Truncate(h2off(h))
- }
- return a.link(h, atoms)
- case latoms == 0 && ratoms != 0:
- // right join ->
- if err = a.unlink(h+atoms, ratoms, rp, rn); err != nil {
- return
- }
- return a.link(h, atoms+ratoms)
- case latoms != 0 && ratoms == 0:
- // <- left join
- if err = a.unlink(h-latoms, latoms, lp, ln); err != nil {
- return
- }
- if isTail {
- return a.f.Truncate(h2off(h - latoms))
- }
- return a.link(h-latoms, latoms+atoms)
- }
- // case latoms != 0 && ratoms != 0:
- // <- middle join ->
- lh, rh := h-latoms, h+atoms
- if err = a.unlink(lh, latoms, lp, ln); err != nil {
- return
- }
- // Prev unlink may have invalidated rp or rn
- if _, _, rp, rn, err = a.nfo(rh); err != nil {
- return
- }
- if err = a.unlink(rh, ratoms, rp, rn); err != nil {
- return
- }
- return a.link(h-latoms, latoms+atoms+ratoms)
- }
- // Add a free block h to the appropriate free list
- func (a *Allocator) link(h, atoms int64) (err error) {
- if err = a.makeFree(h, atoms, 0, a.flt.head(atoms)); err != nil {
- return
- }
- return a.flt.setHead(h, atoms, a.f)
- }
- // Remove free block h from the free list
- func (a *Allocator) unlink(h, atoms, p, n int64) (err error) {
- switch {
- case p == 0 && n == 0:
- // single item list, must be head
- return a.flt.setHead(0, atoms, a.f)
- case p == 0 && n != 0:
- // head of list (has next item[s])
- if err = a.prev(n, 0); err != nil {
- return
- }
- // new head
- return a.flt.setHead(n, atoms, a.f)
- case p != 0 && n == 0:
- // last item in list
- return a.next(p, 0)
- }
- // case p != 0 && n != 0:
- // intermediate item in a list
- if err = a.next(p, n); err != nil {
- return
- }
- return a.prev(n, p)
- }
- //TODO remove ?
- // Return len(slice) == n, reuse src if possible.
- func need(n int, src []byte) []byte {
- if cap(src) < n {
- return *buffer.Get(n)
- }
- return src[:n]
- }
- // Get returns the data content of a block referred to by handle or an error if
- // any. The returned slice may be a sub-slice of buf if buf was large enough
- // to hold the entire content. Otherwise, a newly allocated slice will be
- // returned. It is valid to pass a nil buf.
- //
- // If the content was stored using compression then it is transparently
- // returned decompressed.
- //
- // Handle must have been obtained initially from Alloc and must be still valid,
- // otherwise invalid data may be returned without detecting the error.
- //
- // Get is safe for concurrent access by multiple goroutines iff no other
- // goroutine mutates the DB.
- func (a *Allocator) Get(buf []byte, handle int64) (b []byte, err error) {
- buf = buf[:cap(buf)]
- a.mu.Lock() // X1+
- if n, ok := a.m[handle]; ok {
- a.lru.moveToFront(n)
- b = need(len(n.b), buf)
- copy(b, n.b)
- a.expHit++
- a.hit++
- a.mu.Unlock() // X1-
- return
- }
- a.expMiss++
- a.miss++
- if a.miss > 10 && len(a.m) < 500 {
- if 100*a.hit/a.miss < 95 {
- a.cacheSz++
- }
- a.hit, a.miss = 0, 0
- }
- a.mu.Unlock() // X1-
- defer func(h int64) {
- if err == nil {
- a.mu.Lock() // X2+
- a.cadd(b, h)
- a.mu.Unlock() // X2-
- }
- }(handle)
- pfirst := buffer.Get(16)
- defer buffer.Put(pfirst)
- first := *pfirst
- relocated := false
- relocSrc := handle
- reloc:
- if handle <= 0 || handle > maxHandle {
- return nil, &ErrINVAL{"Allocator.Get: handle out of limits", handle}
- }
- off := h2off(handle)
- if err = a.read(first, off); err != nil {
- return
- }
- switch tag := first[0]; tag {
- default:
- dlen := int(tag)
- atoms := n2atoms(dlen)
- switch atoms {
- case 1:
- switch tag := first[15]; tag {
- default:
- return nil, &ErrILSEQ{Type: ErrTailTag, Off: off, Arg: int64(tag)}
- case tagNotCompressed:
- b = need(dlen, buf)
- copy(b, first[1:])
- return
- case tagCompressed:
- return zappy.Decode(buf, first[1:dlen+1])
- }
- default:
- pcc := buffer.Get(1)
- defer buffer.Put(pcc)
- cc := *pcc
- dlen := int(tag)
- atoms := n2atoms(dlen)
- tailOff := off + 16*int64(atoms) - 1
- if err = a.read(cc, tailOff); err != nil {
- return
- }
- switch tag := cc[0]; tag {
- default:
- return nil, &ErrILSEQ{Type: ErrTailTag, Off: off, Arg: int64(tag)}
- case tagNotCompressed:
- b = need(dlen, buf)
- off += 1
- if err = a.read(b, off); err != nil {
- b = buf[:0]
- }
- return
- case tagCompressed:
- pzbuf := buffer.Get(dlen)
- defer buffer.Put(pzbuf)
- zbuf := *pzbuf
- off += 1
- if err = a.read(zbuf, off); err != nil {
- return buf[:0], err
- }
- return zappy.Decode(buf, zbuf)
- }
- }
- case 0:
- return buf[:0], nil
- case tagUsedLong:
- pcc := buffer.Get(1)
- defer buffer.Put(pcc)
- cc := *pcc
- dlen := m2n(int(first[1])<<8 | int(first[2]))
- atoms := n2atoms(dlen)
- tailOff := off + 16*int64(atoms) - 1
- if err = a.read(cc, tailOff); err != nil {
- return
- }
- switch tag := cc[0]; tag {
- default:
- return nil, &ErrILSEQ{Type: ErrTailTag, Off: off, Arg: int64(tag)}
- case tagNotCompressed:
- b = need(dlen, buf)
- off += 3
- if err = a.read(b, off); err != nil {
- b = buf[:0]
- }
- return
- case tagCompressed:
- pzbuf := buffer.Get(dlen)
- defer buffer.Put(pzbuf)
- zbuf := *pzbuf
- off += 3
- if err = a.read(zbuf, off); err != nil {
- return buf[:0], err
- }
- return zappy.Decode(buf, zbuf)
- }
- case tagFreeShort, tagFreeLong:
- return nil, &ErrILSEQ{Type: ErrExpUsedTag, Off: off, Arg: int64(tag)}
- case tagUsedRelocated:
- if relocated {
- return nil, &ErrILSEQ{Type: ErrUnexpReloc, Off: off, Arg: relocSrc}
- }
- handle = b2h(first[1:])
- relocated = true
- goto reloc
- }
- }
- var reallocTestHook bool
- // Realloc sets the content of a block referred to by handle or returns an
- // error, if any.
- //
- // Handle must have been obtained initially from Alloc and must be still valid,
- // otherwise a database may get irreparably corrupted.
- func (a *Allocator) Realloc(handle int64, b []byte) (err error) {
- if handle <= 0 || handle > maxHandle {
- return &ErrINVAL{"Realloc: handle out of limits", handle}
- }
- a.cfree(handle)
- if err = a.realloc(handle, b); err != nil {
- return
- }
- if reallocTestHook {
- if err = cacheAudit(a.m, &a.lru); err != nil {
- return
- }
- }
- a.cadd(b, handle)
- return
- }
- func (a *Allocator) realloc(handle int64, b []byte) (err error) {
- var dlen, needAtoms0 int
- pb8 := buffer.Get(8)
- defer buffer.Put(pb8)
- b8 := *pb8
- pdst := buffer.Get(zappy.MaxEncodedLen(len(b)))
- defer buffer.Put(pdst)
- dst := *pdst
- b, needAtoms0, cc, err := a.makeUsedBlock(dst, b)
- if err != nil {
- return
- }
- needAtoms := int64(needAtoms0)
- off := h2off(handle)
- if err = a.read(b8[:], off); err != nil {
- return
- }
- switch tag := b8[0]; tag {
- default:
- dlen = int(b8[0])
- case tagUsedLong:
- dlen = m2n(int(b8[1])<<8 | int(b8[2]))
- case tagUsedRelocated:
- if err = a.free(b2h(b8[1:]), handle, false); err != nil {
- return err
- }
- dlen = 0
- case tagFreeShort, tagFreeLong:
- return &ErrINVAL{"Allocator.Realloc: invalid handle", handle}
- }
- atoms := int64(n2atoms(dlen))
- retry:
- switch {
- case needAtoms < atoms:
- // in place shrink
- if err = a.writeUsedBlock(handle, cc, b); err != nil {
- return
- }
- fh, fa := handle+needAtoms, atoms-needAtoms
- sz, err := a.f.Size()
- if err != nil {
- return err
- }
- if h2off(fh)+16*fa == sz {
- return a.f.Truncate(h2off(fh))
- }
- return a.free2(fh, fa)
- case needAtoms == atoms:
- // in place replace
- return a.writeUsedBlock(handle, cc, b)
- }
- // case needAtoms > atoms:
- // in place extend or relocate
- var sz int64
- if sz, err = a.f.Size(); err != nil {
- return
- }
- off = h2off(handle)
- switch {
- case off+atoms*16 == sz:
- // relocating tail block - shortcut
- return a.writeUsedBlock(handle, cc, b)
- default:
- if off+atoms*16 < sz {
- // handle is not a tail block, check right neighbour
- rh := handle + atoms
- rtag, ratoms, p, n, e := a.nfo(rh)
- if e != nil {
- return e
- }
- if rtag == tagFreeShort || rtag == tagFreeLong {
- // Right neighbour is a free block
- if needAtoms <= atoms+ratoms {
- // can expand in place
- if err = a.unlink(rh, ratoms, p, n); err != nil {
- return
- }
- atoms += ratoms
- goto retry
- }
- }
- }
- }
- if atoms > 1 {
- if err = a.realloc(handle, nil); err != nil {
- return
- }
- }
- var newH int64
- if newH, err = a.alloc(b, cc); err != nil {
- return err
- }
- prb := buffer.CGet(16)
- defer buffer.Put(prb)
- rb := *prb
- rb[0] = tagUsedRelocated
- h2b(rb[1:], newH)
- if err = a.writeAt(rb[:], h2off(handle)); err != nil {
- return
- }
- return a.writeUsedBlock(newH, cc, b)
- }
- func (a *Allocator) writeAt(b []byte, off int64) (err error) {
- var n int
- if n, err = a.f.WriteAt(b, off); err != nil {
- return
- }
- if n != len(b) {
- err = io.ErrShortWrite
- }
- return
- }
- func (a *Allocator) write(off int64, b ...[]byte) (err error) {
- rq := 0
- for _, part := range b {
- rq += len(part)
- }
- pbuf := buffer.Get(rq)
- defer buffer.Put(pbuf)
- buf := *pbuf
- buf = buf[:0]
- for _, part := range b {
- buf = append(buf, part...)
- }
- return a.writeAt(buf, off)
- }
- func (a *Allocator) read(b []byte, off int64) (err error) {
- var rn int
- if rn, err = a.f.ReadAt(b, off); rn != len(b) {
- return &ErrILSEQ{Type: ErrOther, Off: off, More: err}
- }
- return nil
- }
- // nfo returns h's tag. If it's a free block then return also (s)ize (in
- // atoms), (p)rev and (n)ext. If it's a used block then only (s)ize is returned
- // (again in atoms). If it's a used relocate block then (n)ext is set to the
- // relocation target handle.
- func (a *Allocator) nfo(h int64) (tag byte, s, p, n int64, err error) {
- off := h2off(h)
- rq := int64(22)
- sz, err := a.f.Size()
- if err != nil {
- return
- }
- if off+rq >= sz {
- if rq = sz - off; rq < 15 {
- err = io.ErrUnexpectedEOF
- return
- }
- }
- pbuf := buffer.Get(22)
- defer buffer.Put(pbuf)
- buf := *pbuf
- if err = a.read(buf[:rq], off); err != nil {
- return
- }
- switch tag = buf[0]; tag {
- default:
- s = int64(n2atoms(int(tag)))
- case tagUsedLong:
- s = int64(n2atoms(m2n(int(buf[1])<<8 | int(buf[2]))))
- case tagFreeLong:
- if rq < 22 {
- err = io.ErrUnexpectedEOF
- return
- }
- s, p, n = b2h(buf[1:]), b2h(buf[8:]), b2h(buf[15:])
- case tagUsedRelocated:
- s, n = 1, b2h(buf[1:])
- case tagFreeShort:
- s, p, n = 1, b2h(buf[1:]), b2h(buf[8:])
- }
- return
- }
- // leftNfo returns nfo for h's left neighbor if h > 1 and the left neighbor is
- // a free block. Otherwise all zero values are returned instead.
- func (a *Allocator) leftNfo(h int64) (tag byte, s, p, n int64, err error) {
- if !(h > 1) {
- return
- }
- pbuf := buffer.Get(8)
- defer buffer.Put(pbuf)
- buf := *pbuf
- off := h2off(h)
- if err = a.read(buf[:], off-8); err != nil {
- return
- }
- switch tag := buf[7]; tag {
- case tagFreeShort:
- return a.nfo(h - 1)
- case tagFreeLong:
- return a.nfo(h - b2h(buf[:]))
- }
- return
- }
- // Set h.prev = p
- func (a *Allocator) prev(h, p int64) (err error) {
- pb := buffer.Get(7)
- defer buffer.Put(pb)
- b := *pb
- off := h2off(h)
- if err = a.read(b[:1], off); err != nil {
- return
- }
- switch tag := b[0]; tag {
- default:
- return &ErrILSEQ{Type: ErrExpFreeTag, Off: off, Arg: int64(tag)}
- case tagFreeShort:
- off += 1
- case tagFreeLong:
- off += 8
- }
- return a.writeAt(h2b(b[:7], p), off)
- }
- // Set h.next = n
- func (a *Allocator) next(h, n int64) (err error) {
- pb := buffer.Get(7)
- defer buffer.Put(pb)
- b := *pb
- off := h2off(h)
- if err = a.read(b[:1], off); err != nil {
- return
- }
- switch tag := b[0]; tag {
- default:
- return &ErrILSEQ{Type: ErrExpFreeTag, Off: off, Arg: int64(tag)}
- case tagFreeShort:
- off += 8
- case tagFreeLong:
- off += 15
- }
- return a.writeAt(h2b(b[:7], n), off)
- }
- // Make the filer image @h a free block.
- func (a *Allocator) makeFree(h, atoms, prev, next int64) (err error) {
- pbuf := buffer.Get(22)
- defer buffer.Put(pbuf)
- buf := *pbuf
- switch {
- case atoms == 1:
- buf[0], buf[15] = tagFreeShort, tagFreeShort
- h2b(buf[1:], prev)
- h2b(buf[8:], next)
- if err = a.write(h2off(h), buf[:16]); err != nil {
- return
- }
- default:
- buf[0] = tagFreeLong
- h2b(buf[1:], atoms)
- h2b(buf[8:], prev)
- h2b(buf[15:], next)
- if err = a.write(h2off(h), buf[:22]); err != nil {
- return
- }
- h2b(buf[:], atoms)
- buf[7] = tagFreeLong
- if err = a.write(h2off(h+atoms)-8, buf[:8]); err != nil {
- return
- }
- }
- if prev != 0 {
- if err = a.next(prev, h); err != nil {
- return
- }
- }
- if next != 0 {
- err = a.prev(next, h)
- }
- return
- }
- func (a *Allocator) makeUsedBlock(dst []byte, b []byte) (w []byte, rqAtoms int, cc byte, err error) {
- cc = tagNotCompressed
- w = b
- var n int
- if n = len(b); n > maxRq {
- return nil, 0, 0, &ErrINVAL{"Allocator.makeUsedBlock: content size out of limits", n}
- }
- rqAtoms = n2atoms(n)
- if a.Compress && n > 14 { // attempt compression
- if dst, err = zappy.Encode(dst, b); err != nil {
- return
- }
- n2 := len(dst)
- if rqAtoms2 := n2atoms(n2); rqAtoms2 < rqAtoms { // compression saved at least a single atom
- w, n, rqAtoms, cc = dst, n2, rqAtoms2, tagCompressed
- }
- }
- return
- }
- func (a *Allocator) writeUsedBlock(h int64, cc byte, b []byte) (err error) {
- n := len(b)
- rq := n2atoms(n) << 4
- pbuf := buffer.Get(rq)
- defer buffer.Put(pbuf)
- buf := *pbuf
- switch n <= maxShort {
- case true:
- buf[0] = byte(n)
- copy(buf[1:], b)
- case false:
- m := n2m(n)
- buf[0], buf[1], buf[2] = tagUsedLong, byte(m>>8), byte(m)
- copy(buf[3:], b)
- }
- if p := n2padding(n); p != 0 {
- copy(buf[rq-1-p:], zeros[:])
- }
- buf[rq-1] = cc
- return a.writeAt(buf, h2off(h))
- }
- func (a *Allocator) verifyUnused(h, totalAtoms int64, tag byte, log func(error) bool, fast bool) (atoms, prev, next int64, err error) {
- switch tag {
- default:
- panic("internal error")
- case tagFreeShort:
- var b [16]byte
- off := h2off(h)
- if err = a.read(b[:], off); err != nil {
- return
- }
- if b[15] != tagFreeShort {
- err = &ErrILSEQ{Type: ErrShortFreeTailTag, Off: off, Arg: int64(b[15])}
- log(err)
- return
- }
- atoms, prev, next = 1, b2h(b[1:]), b2h(b[8:])
- case tagFreeLong:
- var b [22]byte
- off := h2off(h)
- if err = a.read(b[:], off); err != nil {
- return
- }
- atoms, prev, next = b2h(b[1:]), b2h(b[8:]), b2h(b[15:])
- if fast {
- return
- }
- if atoms < 2 {
- err = &ErrILSEQ{Type: ErrLongFreeBlkTooShort, Off: off, Arg: int64(atoms)}
- break
- }
- if h+atoms-1 > totalAtoms {
- err = &ErrILSEQ{Type: ErrLongFreeBlkTooLong, Off: off, Arg: atoms}
- break
- }
- if prev > totalAtoms {
- err = &ErrILSEQ{Type: ErrLongFreePrevBeyondEOF, Off: off, Arg: next}
- break
- }
- if next > totalAtoms {
- err = &ErrILSEQ{Type: ErrLongFreeNextBeyondEOF, Off: off, Arg: next}
- break
- }
- toff := h2off(h+atoms) - 8
- if err = a.read(b[:8], toff); err != nil {
- return
- }
- if b[7] != tag {
- err = &ErrILSEQ{Type: ErrLongFreeTailTag, Off: off, Arg: int64(b[7])}
- break
- }
- if s2 := b2h(b[:]); s2 != atoms {
- err = &ErrILSEQ{Type: ErrVerifyTailSize, Off: off, Arg: atoms, Arg2: s2}
- break
- }
- }
- if err != nil {
- log(err)
- }
- return
- }
- func (a *Allocator) verifyUsed(h, totalAtoms int64, tag byte, buf, ubuf []byte, log func(error) bool, fast bool) (compressed bool, dlen int, atoms, link int64, err error) {
- var (
- padding int
- doff int64
- padZeros [15]byte
- tailBuf [16]byte
- )
- switch tag {
- default: // Short used
- dlen = int(tag)
- atoms = int64((dlen+1)/16) + 1
- padding = 15 - (dlen+1)%16
- doff = h2off(h) + 1
- case tagUsedLong:
- off := h2off(h) + 1
- var b2 [2]byte
- if err = a.read(b2[:], off); err != nil {
- return
- }
- dlen = m2n(int(b2[0])<<8 | int(b2[1]))
- atoms = int64((dlen+3)/16) + 1
- padding = 15 - (dlen+3)%16
- doff = h2off(h) + 3
- case tagUsedRelocated:
- dlen = 7
- atoms = 1
- padding = 7
- doff = h2off(h) + 1
- case tagFreeShort, tagFreeLong:
- panic("internal error")
- }
- if fast {
- if tag == tagUsedRelocated {
- dlen = 0
- if err = a.read(buf[:7], doff); err != nil {
- return
- }
- link = b2h(buf)
- }
- return false, dlen, atoms, link, nil
- }
- if ok := h+atoms-1 <= totalAtoms; !ok { // invalid last block
- err = &ErrILSEQ{Type: ErrVerifyUsedSpan, Off: h2off(h), Arg: atoms}
- log(err)
- return
- }
- tailsz := 1 + padding
- off := h2off(h) + 16*atoms - int64(tailsz)
- if err = a.read(tailBuf[:tailsz], off); err != nil {
- return false, 0, 0, 0, err
- }
- if ok := bytes.Equal(padZeros[:padding], tailBuf[:padding]); !ok {
- err = &ErrILSEQ{Type: ErrVerifyPadding, Off: h2off(h)}
- log(err)
- return
- }
- var cc byte
- switch cc = tailBuf[padding]; cc {
- default:
- err = &ErrILSEQ{Type: ErrTailTag, Off: h2off(h)}
- log(err)
- return
- case tagCompressed:
- compressed = true
- if tag == tagUsedRelocated {
- err = &ErrILSEQ{Type: ErrTailTag, Off: h2off(h)}
- log(err)
- return
- }
- fallthrough
- case tagNotCompressed:
- if err = a.read(buf[:dlen], doff); err != nil {
- return false, 0, 0, 0, err
- }
- }
- if cc == tagCompressed {
- if ubuf, err = zappy.Decode(ubuf, buf[:dlen]); err != nil || len(ubuf) > maxRq {
- err = &ErrILSEQ{Type: ErrDecompress, Off: h2off(h)}
- log(err)
- return
- }
- dlen = len(ubuf)
- }
- if tag == tagUsedRelocated {
- link = b2h(buf)
- if link == 0 {
- err = &ErrILSEQ{Type: ErrNullReloc, Off: h2off(h)}
- log(err)
- return
- }
- if link > totalAtoms { // invalid last block
- err = &ErrILSEQ{Type: ErrRelocBeyondEOF, Off: h2off(h), Arg: link}
- log(err)
- return
- }
- }
- return
- }
- var nolog = func(error) bool { return false }
- // Verify attempts to find any structural errors in a Filer wrt the
- // organization of it as defined by Allocator. 'bitmap' is a scratch pad for
- // necessary bookkeeping and will grow to at most to Allocator's
- // Filer.Size()/128 (0,78%). Any problems found are reported to 'log' except
- // non verify related errors like disk read fails etc. If 'log' returns false
- // or the error doesn't allow to (reliably) continue, the verification process
- // is stopped and an error is returned from the Verify function. Passing a nil
- // log works like providing a log function always returning false. Any
- // non-structural errors, like for instance Filer read errors, are NOT reported
- // to 'log', but returned as the Verify's return value, because Verify cannot
- // proceed in such cases. Verify returns nil only if it fully completed
- // verifying Allocator's Filer without detecting any error.
- //
- // It is recommended to limit the number reported problems by returning false
- // from 'log' after reaching some limit. Huge and corrupted DB can produce an
- // overwhelming error report dataset.
- //
- // The verifying process will scan the whole DB at least 3 times (a trade
- // between processing space and time consumed). It doesn't read the content of
- // free blocks above the head/tail info bytes. If the 3rd phase detects lost
- // free space, then a 4th scan (a faster one) is performed to precisely report
- // all of them.
- //
- // If the DB/Filer to be verified is reasonably small, respective if its
- // size/128 can comfortably fit within process's free memory, then it is
- // recommended to consider using a MemFiler for the bit map.
- //
- // Statistics are returned via 'stats' if non nil. The statistics are valid
- // only if Verify succeeded, ie. it didn't reported anything to log and it
- // returned a nil error.
- func (a *Allocator) Verify(bitmap Filer, log func(error) bool, stats *AllocStats) (err error) {
- if log == nil {
- log = nolog
- }
- n, err := bitmap.Size()
- if err != nil {
- return
- }
- if n != 0 {
- return &ErrINVAL{"Allocator.Verify: bit map initial size non zero (%d)", n}
- }
- var bits int64
- bitMask := [8]byte{1, 2, 4, 8, 16, 32, 64, 128}
- byteBuf := []byte{0}
- //DONE
- // +performance, this implementation is hopefully correct but _very_
- // naive, probably good as a prototype only. Use maybe a MemFiler
- // "cache" etc.
- // ----
- // Turns out the OS caching is as effective as it can probably get.
- bit := func(on bool, h int64) (wasOn bool, err error) {
- m := bitMask[h&7]
- off := h >> 3
- var v byte
- sz, err := bitmap.Size()
- if err != nil {
- return
- }
- if off < sz {
- if n, err := bitmap.ReadAt(byteBuf, off); n != 1 {
- return false, &ErrILSEQ{Type: ErrOther, Off: off, More: fmt.Errorf("Allocator.Verify - reading bitmap: %s", err)}
- }
- v = byteBuf[0]
- }
- switch wasOn = v&m != 0; on {
- case true:
- if !wasOn {
- v |= m
- bits++
- }
- case false:
- if wasOn {
- v ^= m
- bits--
- }
- }
- byteBuf[0] = v
- if n, err := bitmap.WriteAt(byteBuf, off); n != 1 || err != nil {
- return false, &ErrILSEQ{Type: ErrOther, Off: off, More: fmt.Errorf("Allocator.Verify - writing bitmap: %s", err)}
- }
- return
- }
- // Phase 1 - sequentially scan a.f to reliably determine block
- // boundaries. Set a bit for every block start.
- var (
- buf, ubuf [maxRq]byte
- prevH, h, atoms int64
- wasOn bool
- tag byte
- st = AllocStats{
- AllocMap: map[int64]int64{},
- FreeMap: map[int64]int64{},
- }
- dlen int
- )
- fsz, err := a.f.Size()
- if err != nil {
- return
- }
- ok := fsz%16 == 0
- totalAtoms := (fsz - fltSz) / atomLen
- if !ok {
- err = &ErrILSEQ{Type: ErrFileSize, Name: a.f.Name(), Arg: fsz}
- log(err)
- return
- }
- st.TotalAtoms = totalAtoms
- prevTag := -1
- lastH := int64(-1)
- for h = 1; h <= totalAtoms; h += atoms {
- prevH = h // For checking last block == used
- off := h2off(h)
- if err = a.read(buf[:1], off); err != nil {
- return
- }
- switch tag = buf[0]; tag {
- default: // Short used
- fallthrough
- case tagUsedLong, tagUsedRelocated:
- var compressed bool
- if compressed, dlen, atoms, _, err = a.verifyUsed(h, totalAtoms, tag, buf[:], ubuf[:], log, false); err != nil {
- return
- }
- if compressed {
- st.Compression++
- }
- st.AllocAtoms += atoms
- switch {
- case tag == tagUsedRelocated:
- st.AllocMap[1]++
- st.Relocations++
- default:
- st.AllocMap[atoms]++
- st.AllocBytes += int64(dlen)
- st.Handles++
- }
- case tagFreeShort, tagFreeLong:
- if prevTag == tagFreeShort || prevTag == tagFreeLong {
- err = &ErrILSEQ{Type: ErrAdjacentFree, Off: h2off(lastH), Arg: off}
- log(err)
- return
- }
- if atoms, _, _, err = a.verifyUnused(h, totalAtoms, tag, log, false); err != nil {
- return
- }
- st.FreeMap[atoms]++
- st.FreeAtoms += atoms
- }
- if wasOn, err = bit(true, h); err != nil {
- return
- }
- if wasOn {
- panic("internal error")
- }
- prevTag = int(tag)
- lastH = h
- }
- if totalAtoms != 0 && (tag == tagFreeShort || tag == tagFreeLong) {
- err = &ErrILSEQ{Type: ErrFreeTailBlock, Off: h2off(prevH)}
- log(err)
- return
- }
- // Phase 2 - check used blocks, turn off the map bit for every used
- // block.
- for h = 1; h <= totalAtoms; h += atoms {
- off := h2off(h)
- if err = a.read(buf[:1], off); err != nil {
- return
- }
- var link int64
- switch tag = buf[0]; tag {
- default: // Short used
- fallthrough
- case tagUsedLong, tagUsedRelocated:
- if _, _, atoms, link, err = a.verifyUsed(h, totalAtoms, tag, buf[:], ubuf[:], log, true); err != nil {
- return
- }
- case tagFreeShort, tagFreeLong:
- if atoms, _, _, err = a.verifyUnused(h, totalAtoms, tag, log, true); err != nil {
- return
- }
- }
- turnoff := true
- switch tag {
- case tagUsedRelocated:
- if err = a.read(buf[:1], h2off(link)); err != nil {
- return
- }
- switch linkedTag := buf[0]; linkedTag {
- case tagFreeShort, tagFreeLong, tagUsedRelocated:
- err = &ErrILSEQ{Type: ErrInvalidRelocTarget, Off: off, Arg: link}
- log(err)
- return
- }
- case tagFreeShort, tagFreeLong:
- turnoff = false
- }
- if !turnoff {
- continue
- }
- if wasOn, err = bit(false, h); err != nil {
- return
- }
- if !wasOn {
- panic("internal error")
- }
- }
- // Phase 3 - using the flt check heads link to proper free blocks. For
- // every free block, walk the list, verify the {next, prev} links and
- // turn the respective map bit off. After processing all free lists,
- // the map bits count should be zero. Otherwise there are "lost" free
- // blocks.
- var prev, next, fprev, fnext int64
- rep := a.flt
- for _, list := range rep {
- prev, next = 0, list.head
- for ; next != 0; prev, next = next, fnext {
- if wasOn, err = bit(false, next); err != nil {
- return
- }
- if !wasOn {
- err = &ErrILSEQ{Type: ErrFLT, Off: h2off(next), Arg: h}
- log(err)
- return
- }
- off := h2off(next)
- if err = a.read(buf[:1], off); err != nil {
- return
- }
- switch tag = buf[0]; tag {
- default:
- panic("internal error")
- case tagFreeShort, tagFreeLong:
- if atoms, fprev, fnext, err = a.verifyUnused(next, totalAtoms, tag, log, true); err != nil {
- return
- }
- if min := list.minSize; atoms < min {
- err = &ErrILSEQ{Type: ErrFLTSize, Off: h2off(next), Arg: atoms, Arg2: min}
- log(err)
- return
- }
- if fprev != prev {
- err = &ErrILSEQ{Type: ErrFreeChaining, Off: h2off(next)}
- log(err)
- return
- }
- }
- }
- }
- if bits == 0 { // Verify succeeded
- if stats != nil {
- *stats = st
- }
- return
- }
- // Phase 4 - if after phase 3 there are lost free blocks, report all of
- // them to 'log'
- for i := range ubuf { // setup zeros for compares
- ubuf[i] = 0
- }
- var off, lh int64
- rem, err := bitmap.Size()
- if err != nil {
- return err
- }
- for rem != 0 {
- rq := int(mathutil.MinInt64(64*1024, rem))
- var n int
- if n, err = bitmap.ReadAt(buf[:rq], off); n != rq {
- return &ErrILSEQ{Type: ErrOther, Off: off, More: fmt.Errorf("bitmap ReadAt(size %d, off %#x): %s", rq, off, err)}
- }
- if !bytes.Equal(buf[:rq], ubuf[:rq]) {
- for d, v := range buf[:rq] {
- if v != 0 {
- for i, m := range bitMask {
- if v&m != 0 {
- lh = 8*(off+int64(d)) + int64(i)
- err = &ErrILSEQ{Type: ErrLostFreeBlock, Off: h2off(lh)}
- log(err)
- return
- }
- }
- }
- }
- }
- off += int64(rq)
- rem -= int64(rq)
- }
- return
- }
- type fltSlot struct {
- head int64
- minSize int64
- }
- func (f fltSlot) String() string {
- return fmt.Sprintf("head %#x, minSize %#x\n", f.head, f.minSize)
- }
- type flt [14]fltSlot
- func (f *flt) init() {
- sz := 1
- for i := range *f {
- f[i].minSize, f[i].head = int64(sz), 0
- sz <<= 1
- }
- f[13].minSize = 4112
- }
- func (f *flt) load(fi Filer, off int64) (err error) {
- pb := buffer.Get(fltSz)
- defer buffer.Put(pb)
- b := *pb
- if _, err = fi.ReadAt(b[:], off); err != nil {
- return
- }
- for i := range *f {
- off := 8*i + 1
- f[i].head = b2h(b[off:])
- }
- return
- }
- func (f *flt) find(rq int) (h int64) {
- switch {
- case rq < 1:
- panic(rq)
- case rq >= maxFLTRq:
- h, f[13].head = f[13].head, 0
- return
- default:
- g := f[mathutil.Log2Uint16(uint16(rq)):]
- for i := range g {
- p := &g[i]
- if rq <= int(p.minSize) {
- if h = p.head; h != 0 {
- p.head = 0
- return
- }
- }
- }
- return
- }
- }
- func (f *flt) head(atoms int64) (h int64) {
- switch {
- case atoms < 1:
- panic(atoms)
- case atoms >= maxFLTRq:
- return f[13].head
- default:
- lg := mathutil.Log2Uint16(uint16(atoms))
- g := f[lg:]
- for i := range g {
- if atoms < g[i+1].minSize {
- return g[i].head
- }
- }
- panic("internal error")
- }
- }
- func (f *flt) setHead(h, atoms int64, fi Filer) (err error) {
- switch {
- case atoms < 1:
- panic(atoms)
- case atoms >= maxFLTRq:
- pb := buffer.Get(7)
- defer buffer.Put(pb)
- b := *pb
- if _, err = fi.WriteAt(h2b(b[:], h), 8*13+1); err != nil {
- return
- }
- f[13].head = h
- return
- default:
- lg := mathutil.Log2Uint16(uint16(atoms))
- g := f[lg:]
- for i := range f {
- if atoms < g[i+1].minSize {
- pb := buffer.Get(7)
- defer buffer.Put(pb)
- b := *pb
- if _, err = fi.WriteAt(h2b(b[:], h), 8*int64(i+lg)+1); err != nil {
- return
- }
- g[i].head = h
- return
- }
- }
- panic("internal error")
- }
- }
- func (f *flt) String() string {
- a := []string{}
- for i, v := range *f {
- a = append(a, fmt.Sprintf("[%2d] %s", i, v))
- }
- return strings.Join(a, "")
- }
- type node struct {
- b []byte
- h int64
- prev, next *node
- }
- type cache []*node
- func (c *cache) get(n int) *node {
- r, _ := c.get2(n)
- return r
- }
- func (c *cache) get2(n int) (r *node, isZeroed bool) {
- s := *c
- lens := len(s)
- if lens == 0 {
- return &node{b: make([]byte, n, mathutil.Min(2*n, maxBuf))}, true
- }
- i := sort.Search(lens, func(x int) bool { return len(s[x].b) >= n })
- if i == lens {
- i--
- s[i].b, isZeroed = make([]byte, n, mathutil.Min(2*n, maxBuf)), true
- }
- r = s[i]
- r.b = r.b[:n]
- copy(s[i:], s[i+1:])
- s = s[:lens-1]
- *c = s
- return
- }
- func (c *cache) cget(n int) (r *node) {
- r, ok := c.get2(n)
- if ok {
- return
- }
- for i := range r.b {
- r.b[i] = 0
- }
- return
- }
- func (c *cache) size() (sz int64) {
- for _, n := range *c {
- sz += int64(cap(n.b))
- }
- return
- }
- func (c *cache) put(n *node) *node {
- s := *c
- n.b = n.b[:cap(n.b)]
- lenb := len(n.b)
- lens := len(s)
- i := sort.Search(lens, func(x int) bool { return len(s[x].b) >= lenb })
- s = append(s, nil)
- copy(s[i+1:], s[i:])
- s[i] = n
- *c = s
- return n
- }
- type lst struct {
- front, back *node
- }
- func (l *lst) pushFront(n *node) *node {
- if l.front == nil {
- l.front, l.back, n.prev, n.next = n, n, nil, nil
- return n
- }
- n.prev, n.next, l.front.prev, l.front = nil, l.front, n, n
- return n
- }
- func (l *lst) remove(n *node) *node {
- if n.prev == nil {
- l.front = n.next
- } else {
- n.prev.next = n.next
- }
- if n.next == nil {
- l.back = n.prev
- } else {
- n.next.prev = n.prev
- }
- n.prev, n.next = nil, nil
- return n
- }
- func (l *lst) removeBack() *node {
- return l.remove(l.back)
- }
- func (l *lst) moveToFront(n *node) *node {
- return l.pushFront(l.remove(n))
- }
- func (l *lst) size() (sz int64) {
- for n := l.front; n != nil; n = n.next {
- sz += int64(cap(n.b))
- }
- return
- }
- func cacheAudit(m map[int64]*node, l *lst) (err error) {
- cnt := 0
- for h, n := range m {
- if g, e := n.h, h; g != e {
- return fmt.Errorf("cacheAudit: invalid node handle %d != %d", g, e)
- }
- if cnt, err = l.audit(n, true); err != nil {
- return
- }
- }
- if g, e := cnt, len(m); g != e {
- return fmt.Errorf("cacheAudit: invalid cache size %d != %d", g, e)
- }
- return
- }
- func (l *lst) audit(n *node, onList bool) (cnt int, err error) {
- if !onList && (n.prev != nil || n.next != nil) {
- return -1, fmt.Errorf("lst.audit: free node with non nil linkage")
- }
- if l.front == nil && l.back != nil || l.back == nil && l.front != nil {
- return -1, fmt.Errorf("lst.audit: one of .front/.back is nil while the other is non nil")
- }
- if l.front == l.back && l.front != nil {
- x := l.front
- if x.prev != nil || x.next != nil {
- return -1, fmt.Errorf("lst.audit: single node has non nil linkage")
- }
- if onList && x != n {
- return -1, fmt.Errorf("lst.audit: single node is alien")
- }
- }
- seen := false
- var prev *node
- x := l.front
- for x != nil {
- cnt++
- if x.prev != prev {
- return -1, fmt.Errorf("lst.audit: broken .prev linkage")
- }
- if x == n {
- seen = true
- }
- prev = x
- x = x.next
- }
- if prev != l.back {
- return -1, fmt.Errorf("lst.audit: broken .back linkage")
- }
- if onList && !seen {
- return -1, fmt.Errorf("lst.audit: node missing in list")
- }
- if !onList && seen {
- return -1, fmt.Errorf("lst.audit: node should not be on the list")
- }
- return
- }
|