1
2
3
4
5 package tlog
6
7 import (
8 "fmt"
9 "strconv"
10 "strings"
11 )
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 type Tile struct {
40 H int
41 L int
42 N int64
43 W int
44 }
45
46
47
48
49
50 func TileForIndex(h int, index int64) Tile {
51 if h <= 0 {
52 panic(fmt.Sprintf("TileForIndex: invalid height %d", h))
53 }
54 t, _, _ := tileForIndex(h, index)
55 return t
56 }
57
58
59
60
61 func tileForIndex(h int, index int64) (t Tile, start, end int) {
62 level, n := SplitStoredHashIndex(index)
63 t.H = h
64 t.L = level / h
65 level -= t.L * h
66 t.N = n << uint(level) >> uint(t.H)
67 n -= t.N << uint(t.H) >> uint(level)
68 t.W = int((n + 1) << uint(level))
69 return t, int(n<<uint(level)) * HashSize, int((n+1)<<uint(level)) * HashSize
70 }
71
72
73
74
75 func HashFromTile(t Tile, data []byte, index int64) (Hash, error) {
76 if t.H < 1 || t.H > 30 || t.L < 0 || t.L >= 64 || t.W < 1 || t.W > 1<<uint(t.H) {
77 return Hash{}, fmt.Errorf("invalid tile %v", t.Path())
78 }
79 if len(data) < t.W*HashSize {
80 return Hash{}, fmt.Errorf("data len %d too short for tile %v", len(data), t.Path())
81 }
82 t1, start, end := tileForIndex(t.H, index)
83 if t.L != t1.L || t.N != t1.N || t.W < t1.W {
84 return Hash{}, fmt.Errorf("index %v is in %v not %v", index, t1.Path(), t.Path())
85 }
86 return tileHash(data[start:end]), nil
87 }
88
89
90 func tileHash(data []byte) Hash {
91 if len(data) == 0 {
92 panic("bad math in tileHash")
93 }
94 if len(data) == HashSize {
95 var h Hash
96 copy(h[:], data)
97 return h
98 }
99 n := len(data) / 2
100 return NodeHash(tileHash(data[:n]), tileHash(data[n:]))
101 }
102
103
104
105
106
107
108
109 func NewTiles(h int, oldTreeSize, newTreeSize int64) []Tile {
110 if h <= 0 {
111 panic(fmt.Sprintf("NewTiles: invalid height %d", h))
112 }
113 H := uint(h)
114 var tiles []Tile
115 for level := uint(0); newTreeSize>>(H*level) > 0; level++ {
116 oldN := oldTreeSize >> (H * level)
117 newN := newTreeSize >> (H * level)
118 for n := oldN >> H; n < newN>>H; n++ {
119 tiles = append(tiles, Tile{H: h, L: int(level), N: n, W: 1 << H})
120 }
121 n := newN >> H
122 maxW := int(newN - n<<H)
123 minW := 1
124 if oldN > n<<H {
125 minW = int(oldN - n<<H)
126 }
127 for w := minW; w <= maxW; w++ {
128 tiles = append(tiles, Tile{H: h, L: int(level), N: n, W: w})
129 }
130 }
131 return tiles
132 }
133
134
135
136 func ReadTileData(t Tile, r HashReader) ([]byte, error) {
137 size := t.W
138 if size == 0 {
139 size = 1 << uint(t.H)
140 }
141 start := t.N << uint(t.H)
142 indexes := make([]int64, size)
143 for i := 0; i < size; i++ {
144 indexes[i] = StoredHashIndex(t.H*t.L, start+int64(i))
145 }
146
147 hashes, err := r.ReadHashes(indexes)
148 if err != nil {
149 return nil, err
150 }
151 if len(hashes) != len(indexes) {
152 return nil, fmt.Errorf("tlog: ReadHashes(%d indexes) = %d hashes", len(indexes), len(hashes))
153 }
154
155 tile := make([]byte, size*HashSize)
156 for i := 0; i < size; i++ {
157 copy(tile[i*HashSize:], hashes[i][:])
158 }
159 return tile, nil
160 }
161
162
163
164
165
166
167
168 const pathBase = 1000
169
170
171 func (t Tile) Path() string {
172 n := t.N
173 nStr := fmt.Sprintf("%03d", n%pathBase)
174 for n >= pathBase {
175 n /= pathBase
176 nStr = fmt.Sprintf("x%03d/%s", n%pathBase, nStr)
177 }
178 pStr := ""
179 if t.W != 1<<uint(t.H) {
180 pStr = fmt.Sprintf(".p/%d", t.W)
181 }
182 var L string
183 if t.L == -1 {
184 L = "data"
185 } else {
186 L = fmt.Sprintf("%d", t.L)
187 }
188 return fmt.Sprintf("tile/%d/%s/%s%s", t.H, L, nStr, pStr)
189 }
190
191
192 func ParseTilePath(path string) (Tile, error) {
193 f := strings.Split(path, "/")
194 if len(f) < 4 || f[0] != "tile" {
195 return Tile{}, &badPathError{path}
196 }
197 h, err1 := strconv.Atoi(f[1])
198 isData := false
199 if f[2] == "data" {
200 isData = true
201 f[2] = "0"
202 }
203 l, err2 := strconv.Atoi(f[2])
204 if err1 != nil || err2 != nil || h < 1 || l < 0 || h > 30 {
205 return Tile{}, &badPathError{path}
206 }
207 w := 1 << uint(h)
208 if dotP := f[len(f)-2]; strings.HasSuffix(dotP, ".p") {
209 ww, err := strconv.Atoi(f[len(f)-1])
210 if err != nil || ww <= 0 || ww >= w {
211 return Tile{}, &badPathError{path}
212 }
213 w = ww
214 f[len(f)-2] = dotP[:len(dotP)-len(".p")]
215 f = f[:len(f)-1]
216 }
217 f = f[3:]
218 n := int64(0)
219 for _, s := range f {
220 nn, err := strconv.Atoi(strings.TrimPrefix(s, "x"))
221 if err != nil || nn < 0 || nn >= pathBase {
222 return Tile{}, &badPathError{path}
223 }
224 n = n*pathBase + int64(nn)
225 }
226 if isData {
227 l = -1
228 }
229 t := Tile{H: h, L: l, N: n, W: w}
230 if path != t.Path() {
231 return Tile{}, &badPathError{path}
232 }
233 return t, nil
234 }
235
236 type badPathError struct {
237 path string
238 }
239
240 func (e *badPathError) Error() string {
241 return fmt.Sprintf("malformed tile path %q", e.path)
242 }
243
244
245 type TileReader interface {
246
247 Height() int
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264 ReadTiles(tiles []Tile) (data [][]byte, err error)
265
266
267
268
269 SaveTiles(tiles []Tile, data [][]byte)
270 }
271
272
273
274
275
276
277
278 func TileHashReader(tree Tree, tr TileReader) HashReader {
279 return &tileHashReader{tree: tree, tr: tr}
280 }
281
282 type tileHashReader struct {
283 tree Tree
284 tr TileReader
285 }
286
287
288
289 func tileParent(t Tile, k int, n int64) Tile {
290 t.L += k
291 t.N >>= uint(k * t.H)
292 t.W = 1 << uint(t.H)
293 if max := n >> uint(t.L*t.H); t.N<<uint(t.H)+int64(t.W) >= max {
294 if t.N<<uint(t.H) >= max {
295 return Tile{}
296 }
297 t.W = int(max - t.N<<uint(t.H))
298 }
299 return t
300 }
301
302 func (r *tileHashReader) ReadHashes(indexes []int64) ([]Hash, error) {
303 h := r.tr.Height()
304
305 tileOrder := make(map[Tile]int)
306 var tiles []Tile
307
308
309
310 stx := subTreeIndex(0, r.tree.N, nil)
311 stxTileOrder := make([]int, len(stx))
312 for i, x := range stx {
313 tile, _, _ := tileForIndex(h, x)
314 tile = tileParent(tile, 0, r.tree.N)
315 if j, ok := tileOrder[tile]; ok {
316 stxTileOrder[i] = j
317 continue
318 }
319 stxTileOrder[i] = len(tiles)
320 tileOrder[tile] = len(tiles)
321 tiles = append(tiles, tile)
322 }
323
324
325
326
327
328 indexTileOrder := make([]int, len(indexes))
329 for i, x := range indexes {
330 if x >= StoredHashIndex(0, r.tree.N) {
331 return nil, fmt.Errorf("indexes not in tree")
332 }
333
334 tile, _, _ := tileForIndex(h, x)
335
336
337
338 k := 0
339 for ; ; k++ {
340 p := tileParent(tile, k, r.tree.N)
341 if j, ok := tileOrder[p]; ok {
342 if k == 0 {
343 indexTileOrder[i] = j
344 }
345 break
346 }
347 }
348
349
350
351
352
353 for k--; k >= 0; k-- {
354 p := tileParent(tile, k, r.tree.N)
355 if p.W != 1<<uint(p.H) {
356
357
358 return nil, fmt.Errorf("bad math in tileHashReader: %d %d %v", r.tree.N, x, p)
359 }
360 tileOrder[p] = len(tiles)
361 if k == 0 {
362 indexTileOrder[i] = len(tiles)
363 }
364 tiles = append(tiles, p)
365 }
366 }
367
368
369 data, err := r.tr.ReadTiles(tiles)
370 if err != nil {
371 return nil, err
372 }
373 if len(data) != len(tiles) {
374 return nil, fmt.Errorf("TileReader returned bad result slice (len=%d, want %d)", len(data), len(tiles))
375 }
376 for i, tile := range tiles {
377 if len(data[i]) != tile.W*HashSize {
378 return nil, fmt.Errorf("TileReader returned bad result slice (%v len=%d, want %d)", tile.Path(), len(data[i]), tile.W*HashSize)
379 }
380 }
381
382
383
384
385 th, err := HashFromTile(tiles[stxTileOrder[len(stx)-1]], data[stxTileOrder[len(stx)-1]], stx[len(stx)-1])
386 if err != nil {
387 return nil, err
388 }
389 for i := len(stx) - 2; i >= 0; i-- {
390 h, err := HashFromTile(tiles[stxTileOrder[i]], data[stxTileOrder[i]], stx[i])
391 if err != nil {
392 return nil, err
393 }
394 th = NodeHash(h, th)
395 }
396 if th != r.tree.Hash {
397
398
399 return nil, fmt.Errorf("downloaded inconsistent tile")
400 }
401
402
403 for i := len(stx); i < len(tiles); i++ {
404 tile := tiles[i]
405 p := tileParent(tile, 1, r.tree.N)
406 j, ok := tileOrder[p]
407 if !ok {
408 return nil, fmt.Errorf("bad math in tileHashReader %d %v: lost parent of %v", r.tree.N, indexes, tile)
409 }
410 h, err := HashFromTile(p, data[j], StoredHashIndex(p.L*p.H, tile.N))
411 if err != nil {
412 return nil, fmt.Errorf("bad math in tileHashReader %d %v: lost hash of %v: %v", r.tree.N, indexes, tile, err)
413 }
414 if h != tileHash(data[i]) {
415 return nil, fmt.Errorf("downloaded inconsistent tile")
416 }
417 }
418
419
420
421 r.tr.SaveTiles(tiles, data)
422
423
424 hashes := make([]Hash, len(indexes))
425 for i, x := range indexes {
426 j := indexTileOrder[i]
427 h, err := HashFromTile(tiles[j], data[j], x)
428 if err != nil {
429 return nil, fmt.Errorf("bad math in tileHashReader %d %v: lost hash %v: %v", r.tree.N, indexes, x, err)
430 }
431 hashes[i] = h
432 }
433
434 return hashes, nil
435 }
436
View as plain text