package store // Index key layout. Every key starts with a three-byte prefix followed by // fixed-width fields, so a file can be binary-searched by CmpLen bytes and a // range scan is a pair of key constructions. type prefix [3]byte var ( pEid = prefix{'e', 'i', 'd'} // id hash + serial pFpc = prefix{'f', 'p', 'c'} // serial -> full id + pubkey hash + timestamp pSei = prefix{'s', 'e', 'i'} // serial -> full event id pCA = prefix{'c', '-', '-'} // created_at + serial pExp = prefix{'e', 'x', 'p'} // expiration + serial pKC = prefix{'k', 'c', '-'} // kind + created_at + serial pPC = prefix{'p', 'c', '-'} // pubkey hash + created_at + serial pKPC = prefix{'k', 'p', 'c'} // kind + pubkey hash + created_at + serial pTC = prefix{'t', 'c', '-'} // tag letter + value hash + created_at + serial pTKC = prefix{'t', 'k', 'c'} // kind + tag letter + value hash + created_at + serial pTPC = prefix{'t', 'p', 'c'} // pubkey hash + tag letter + value hash + created_at + serial pTKP = prefix{'t', 'k', 'p'} // kind + pubkey hash + tag letter + value hash + created_at + serial pWrd = prefix{'w', 'r', 'd'} // word hash + serial pRep = prefix{'r', 'e', 'p'} // kind + pubkey hash + d hash + created_at + serial pDel = prefix{'d', 'e', 'l'} // full 32-byte event id // Pubkey serials make the graph indexes fixed-width: a pubkey is resolved // to its serial once and every edge then carries five bytes instead of 32. pPks = prefix{'p', 'k', 's'} // pubkey hash + pubkey serial pSpk = prefix{'s', 'p', 'k'} // pubkey serial -> full 32-byte pubkey // Social graph indexes. The two index families are the forward and reverse // direction of the same edge, so each traversal is one prefix scan. pEpg = prefix{'e', 'p', 'g'} // event -> pubkey pPeg = prefix{'p', 'e', 'g'} // pubkey -> event pEeg = prefix{'e', 'e', 'g'} // event -> event (outbound e-tags) pGee = prefix{'g', 'e', 'e'} // event -> event (inbound e-tags) pPpg = prefix{'p', 'p', 'g'} // pubkey -> pubkey (outbound) pGpp = prefix{'g', 'p', 'p'} // pubkey -> pubkey (inbound) ) // Graph edge directions. These values are on-disk bytes and must not change. const ( DirAuthor byte = 0 // event author edge DirPTagOut byte = 1 // event references pubkey via p tag DirPTagIn byte = 2 // pubkey is referenced by an event's p tag DirETagOut byte = 0 // event references target event via e tag DirETagIn byte = 1 // event is referenced by source event via e tag DirPKOut byte = 0 // pubkey references another pubkey DirPKIn byte = 1 // pubkey is referenced by another pubkey ) // Record and comparison lengths. const ( eidRecLen = 3 + HashLen + SerialLen eidCmpLen = 3 + HashLen fpcRecLen = 3 + SerialLen + 32 + HashLen + 8 fpcCmpLen = 3 + SerialLen seiRecLen = 3 + SerialLen seiCmpLen = 3 + SerialLen seiValLen = 32 caRecLen = 3 + 8 + SerialLen caCmpLen = caRecLen expRecLen = caRecLen expCmpLen = expRecLen kcRecLen = 3 + 2 + 8 + SerialLen kcCmpLen = kcRecLen pcRecLen = 3 + HashLen + 8 + SerialLen pcCmpLen = pcRecLen kpcRecLen = 3 + 2 + HashLen + 8 + SerialLen kpcCmpLen = kpcRecLen tcRecLen = 3 + 1 + HashLen + 8 + SerialLen tcCmpLen = tcRecLen tkcRecLen = 3 + 2 + 1 + HashLen + 8 + SerialLen tkcCmpLen = tkcRecLen tpcRecLen = 3 + HashLen + 1 + HashLen + 8 + SerialLen tpcCmpLen = tpcRecLen tkpRecLen = 3 + 2 + HashLen + 1 + HashLen + 8 + SerialLen tkpCmpLen = tkpRecLen wrdRecLen = 3 + HashLen + SerialLen wrdCmpLen = wrdRecLen repRecLen = 3 + 2 + HashLen + HashLen + 8 + SerialLen repCmpLen = repRecLen delRecLen = 3 + 32 delCmpLen = delRecLen pksRecLen = 3 + HashLen + SerialLen // 16 pksCmpLen = 3 + HashLen // 11 spkRecLen = 3 + SerialLen // 8: key spkValLen = 32 // + pubkey spkCmpLen = spkRecLen // 8 epgRecLen = 3 + SerialLen + SerialLen + 2 + 1 // 16 pegRecLen = epgRecLen eegRecLen = epgRecLen geeRecLen = epgRecLen ppgRecLen = 3 + SerialLen + SerialLen + 2 + 1 + SerialLen // 21 gppRecLen = ppgRecLen ) func newKey(p prefix, n int) []byte { k := make([]byte, n) copy(k, p[:]) return k } // prefixEnd returns the smallest key greater than every key starting with p. func prefixEnd(p []byte) []byte { out := make([]byte, len(p)) copy(out, p) for i := len(out) - 1; i >= 0; i-- { out[i]++ if out[i] != 0 { return out[:i+1] } } // Every byte was 0xff: there is no successor. Callers use end==nil to mean // "to the end of the index". return nil } func appendHash(dst []byte, h [HashLen]byte) []byte { return append(dst, h[:]...) } func makeEid(idHash [HashLen]byte, ser uint64) []byte { k := newKey(pEid, eidRecLen) copy(k[3:], idHash[:]) PutSerial(k[3+HashLen:], ser) return k } func makeEidPrefix(idHash [HashLen]byte) []byte { k := make([]byte, eidCmpLen) copy(k, pEid[:]) copy(k[3:], idHash[:]) return k } func makeSei(ser uint64) []byte { k := newKey(pSei, seiRecLen) PutSerial(k[3:], ser) return k } func makeSeiRec(ser uint64, id []byte) []byte { rec := make([]byte, seiRecLen+seiValLen) copy(rec, pSei[:]) PutSerial(rec[3:], ser) copy(rec[seiRecLen:], id[:32]) return rec } func makeFpc(ser uint64, id, pubHash []byte, ts int64) []byte { k := newKey(pFpc, fpcRecLen) PutSerial(k[3:], ser) copy(k[3+SerialLen:], id[:32]) copy(k[3+SerialLen+32:], pubHash[:HashLen]) PutTimestamp(k[3+SerialLen+32+HashLen:], ts) return k } func makeCA(p prefix, ts int64, ser uint64, n int) []byte { k := newKey(p, n) PutTimestamp(k[3:], ts) PutSerial(k[3+8:], ser) return k } func makeKC(kind int, ts int64, ser uint64) []byte { k := newKey(pKC, kcRecLen) PutKind(k[3:], kind) PutTimestamp(k[5:], ts) PutSerial(k[13:], ser) return k } func makePC(pubHash [HashLen]byte, ts int64, ser uint64) []byte { k := newKey(pPC, pcRecLen) copy(k[3:], pubHash[:]) PutTimestamp(k[3+HashLen:], ts) PutSerial(k[3+HashLen+8:], ser) return k } func makeKPC(kind int, pubHash [HashLen]byte, ts int64, ser uint64) []byte { k := newKey(pKPC, kpcRecLen) PutKind(k[3:], kind) copy(k[5:], pubHash[:]) PutTimestamp(k[5+HashLen:], ts) PutSerial(k[5+HashLen+8:], ser) return k } func makeTC(tag byte, valHash [HashLen]byte, ts int64, ser uint64) []byte { k := newKey(pTC, tcRecLen) k[3] = tag copy(k[4:], valHash[:]) PutTimestamp(k[4+HashLen:], ts) PutSerial(k[4+HashLen+8:], ser) return k } func makeTKC(kind int, tag byte, valHash [HashLen]byte, ts int64, ser uint64) []byte { k := newKey(pTKC, tkcRecLen) PutKind(k[3:], kind) k[5] = tag copy(k[6:], valHash[:]) PutTimestamp(k[6+HashLen:], ts) PutSerial(k[6+HashLen+8:], ser) return k } func makeTPC(pubHash [HashLen]byte, tag byte, valHash [HashLen]byte, ts int64, ser uint64) []byte { k := newKey(pTPC, tpcRecLen) copy(k[3:], pubHash[:]) k[3+HashLen] = tag copy(k[4+HashLen:], valHash[:]) PutTimestamp(k[4+HashLen+HashLen:], ts) PutSerial(k[4+HashLen+HashLen+8:], ser) return k } func makeTKP(kind int, pubHash [HashLen]byte, tag byte, valHash [HashLen]byte, ts int64, ser uint64) []byte { k := newKey(pTKP, tkpRecLen) PutKind(k[3:], kind) copy(k[5:], pubHash[:]) k[5+HashLen] = tag copy(k[6+HashLen:], valHash[:]) PutTimestamp(k[6+HashLen+HashLen:], ts) PutSerial(k[6+HashLen+HashLen+8:], ser) return k } func makeWrd(wordHash [HashLen]byte, ser uint64) []byte { k := newKey(pWrd, wrdRecLen) copy(k[3:], wordHash[:]) PutSerial(k[3+HashLen:], ser) return k } func makeRep(kind int, pubHash, dHash [HashLen]byte, ts int64, ser uint64) []byte { k := newKey(pRep, repRecLen) PutKind(k[3:], kind) copy(k[5:], pubHash[:]) copy(k[5+HashLen:], dHash[:]) PutTimestamp(k[5+2*HashLen:], ts) PutSerial(k[5+2*HashLen+8:], ser) return k } func makeRepPrefix(kind int, pubHash, dHash [HashLen]byte) []byte { k := make([]byte, 5+2*HashLen) copy(k, pRep[:]) PutKind(k[3:], kind) copy(k[5:], pubHash[:]) copy(k[5+HashLen:], dHash[:]) return k } func makeDel(id []byte) []byte { k := newKey(pDel, delRecLen) copy(k[3:], id[:32]) return k } // --- pubkey serial keys --- func makePks(h [HashLen]byte, ser uint64) []byte { k := newKey(pPks, pksRecLen) copy(k[3:], h[:]) PutSerial(k[3+HashLen:], ser) return k } func makePksPrefix(h [HashLen]byte) []byte { k := make([]byte, pksCmpLen) copy(k, pPks[:]) copy(k[3:], h[:]) return k } func makeSpk(ser uint64) []byte { k := newKey(pSpk, spkRecLen) PutSerial(k[3:], ser) return k } func makeSpkRec(ser uint64, pubkey []byte) []byte { rec := make([]byte, spkRecLen+spkValLen) copy(rec, pSpk[:]) PutSerial(rec[3:], ser) copy(rec[spkRecLen:], pubkey[:32]) return rec } // --- graph index keys --- // makeEpg: epg|event_serial|pubkey_serial|kind|direction. func makeEpg(evSer, pkSer uint64, kind int, dir byte) []byte { k := newKey(pEpg, epgRecLen) PutSerial(k[3:], evSer) PutSerial(k[8:], pkSer) PutKind(k[13:], kind) k[15] = dir return k } // makePeg: peg|pubkey_serial|kind|direction|event_serial. func makePeg(pkSer uint64, kind int, dir byte, evSer uint64) []byte { k := newKey(pPeg, pegRecLen) PutSerial(k[3:], pkSer) PutKind(k[8:], kind) k[10] = dir PutSerial(k[11:], evSer) return k } // makeEeg: eeg|source_serial|target_serial|kind|direction. func makeEeg(srcSer, tgtSer uint64, kind int, dir byte) []byte { k := newKey(pEeg, eegRecLen) PutSerial(k[3:], srcSer) PutSerial(k[8:], tgtSer) PutKind(k[13:], kind) k[15] = dir return k } // makeGee: gee|target_serial|kind|direction|source_serial. func makeGee(tgtSer uint64, kind int, dir byte, srcSer uint64) []byte { k := newKey(pGee, geeRecLen) PutSerial(k[3:], tgtSer) PutKind(k[8:], kind) k[10] = dir PutSerial(k[11:], srcSer) return k } // makePpg: ppg|source_pk|target_pk|kind|direction|event_serial. func makePpg(srcPK, tgtPK uint64, kind int, dir byte, evSer uint64) []byte { k := newKey(pPpg, ppgRecLen) PutSerial(k[3:], srcPK) PutSerial(k[8:], tgtPK) PutKind(k[13:], kind) k[15] = dir PutSerial(k[16:], evSer) return k } // makeGpp: gpp|target_pk|kind|direction|source_pk|event_serial. func makeGpp(tgtPK uint64, kind int, dir byte, srcPK, evSer uint64) []byte { k := newKey(pGpp, gppRecLen) PutSerial(k[3:], tgtPK) PutKind(k[8:], kind) k[10] = dir PutSerial(k[11:], srcPK) PutSerial(k[16:], evSer) return k } // graphPrefix returns the fixed-width prefix for a graph serial scan. func graphPrefix(p prefix, ser uint64) []byte { k := newKey(p, 3+SerialLen) PutSerial(k[3:], ser) return k }