| 
									
										
										
										
											2017-04-11 02:59:45 +08:00
										 |  |  | // Copyright 2017 The Prometheus Authors
 | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | package tsdb | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-01-20 14:58:19 +08:00
										 |  |  | 	"bufio" | 
					
						
							| 
									
										
										
										
											2016-12-10 04:23:34 +08:00
										 |  |  | 	"encoding/binary" | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2017-02-16 07:24:53 +08:00
										 |  |  | 	"hash" | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2017-02-23 17:50:22 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2017-02-25 14:24:20 +08:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2016-12-10 04:23:34 +08:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2016-12-12 18:38:43 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"math" | 
					
						
							| 
									
										
										
										
											2016-12-20 20:10:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-23 17:50:22 +08:00
										 |  |  | 	"github.com/coreos/etcd/pkg/fileutil" | 
					
						
							| 
									
										
										
										
											2016-12-20 20:10:37 +08:00
										 |  |  | 	"github.com/pkg/errors" | 
					
						
							| 
									
										
										
										
											2017-04-04 17:27:26 +08:00
										 |  |  | 	"github.com/prometheus/tsdb/labels" | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	// MagicIndex 4 bytes at the head of an index file.
 | 
					
						
							|  |  |  | 	MagicIndex = 0xBAAAD700 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	indexFormatV1 = 1 | 
					
						
							| 
									
										
										
										
											2017-02-23 17:50:22 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-06 20:45:54 +08:00
										 |  |  | const indexFilename = "index" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | const compactionPageBytes = minSectorSize * 64 | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 14:33:54 +08:00
										 |  |  | type indexWriterSeries struct { | 
					
						
							|  |  |  | 	labels labels.Labels | 
					
						
							| 
									
										
										
										
											2017-08-07 02:41:24 +08:00
										 |  |  | 	chunks []ChunkMeta // series file offset of chunks
 | 
					
						
							|  |  |  | 	offset uint32      // index file offset of series reference
 | 
					
						
							| 
									
										
										
										
											2017-04-26 14:33:54 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type indexWriterSeriesSlice []*indexWriterSeries | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s indexWriterSeriesSlice) Len() int      { return len(s) } | 
					
						
							|  |  |  | func (s indexWriterSeriesSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s indexWriterSeriesSlice) Less(i, j int) bool { | 
					
						
							|  |  |  | 	return labels.Compare(s[i].labels, s[j].labels) < 0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type indexWriterStage uint8 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	idxStageNone indexWriterStage = iota | 
					
						
							|  |  |  | 	idxStageSymbols | 
					
						
							|  |  |  | 	idxStageSeries | 
					
						
							| 
									
										
										
										
											2017-04-26 14:33:54 +08:00
										 |  |  | 	idxStageLabelIndex | 
					
						
							|  |  |  | 	idxStagePostings | 
					
						
							|  |  |  | 	idxStageDone | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s indexWriterStage) String() string { | 
					
						
							|  |  |  | 	switch s { | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	case idxStageNone: | 
					
						
							|  |  |  | 		return "none" | 
					
						
							|  |  |  | 	case idxStageSymbols: | 
					
						
							|  |  |  | 		return "symbols" | 
					
						
							|  |  |  | 	case idxStageSeries: | 
					
						
							|  |  |  | 		return "series" | 
					
						
							| 
									
										
										
										
											2017-04-26 14:33:54 +08:00
										 |  |  | 	case idxStageLabelIndex: | 
					
						
							|  |  |  | 		return "label index" | 
					
						
							|  |  |  | 	case idxStagePostings: | 
					
						
							|  |  |  | 		return "postings" | 
					
						
							|  |  |  | 	case idxStageDone: | 
					
						
							|  |  |  | 		return "done" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return "<unknown>" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 21:41:42 +08:00
										 |  |  | // IndexWriter serializes the index for a block of series data.
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | // The methods must be called in the order they are specified in.
 | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | type IndexWriter interface { | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	// AddSymbols registers all string symbols that are encountered in series
 | 
					
						
							|  |  |  | 	// and other indices.
 | 
					
						
							|  |  |  | 	AddSymbols(sym map[string]struct{}) error | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 21:41:42 +08:00
										 |  |  | 	// AddSeries populates the index writer with a series and its offsets
 | 
					
						
							| 
									
										
										
										
											2016-12-10 16:44:00 +08:00
										 |  |  | 	// of chunks that the index can reference.
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	// Implementations may require series to be insert in increasing order by
 | 
					
						
							|  |  |  | 	// their labels.
 | 
					
						
							|  |  |  | 	// The reference numbers are used to resolve entries in postings lists that
 | 
					
						
							|  |  |  | 	// are added later.
 | 
					
						
							| 
									
										
										
										
											2017-09-04 22:08:38 +08:00
										 |  |  | 	AddSeries(ref uint64, l labels.Labels, chunks ...ChunkMeta) error | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// WriteLabelIndex serializes an index from label names to values.
 | 
					
						
							|  |  |  | 	// The passed in values chained tuples of strings of the length of names.
 | 
					
						
							|  |  |  | 	WriteLabelIndex(names []string, values []string) error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// WritePostings writes a postings list for a single label pair.
 | 
					
						
							| 
									
										
										
										
											2017-03-30 07:18:41 +08:00
										 |  |  | 	// The Postings here contain refs to the series that were added.
 | 
					
						
							| 
									
										
										
										
											2016-12-15 04:58:29 +08:00
										 |  |  | 	WritePostings(name, value string, it Postings) error | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-30 07:18:41 +08:00
										 |  |  | 	// Close writes any finalization and closes the resources associated with
 | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | 	// the underlying writer.
 | 
					
						
							|  |  |  | 	Close() error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // indexWriter implements the IndexWriter interface for the standard
 | 
					
						
							|  |  |  | // serialization format.
 | 
					
						
							|  |  |  | type indexWriter struct { | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	f    *os.File | 
					
						
							|  |  |  | 	fbuf *bufio.Writer | 
					
						
							|  |  |  | 	pos  uint64 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	toc   indexTOC | 
					
						
							| 
									
										
										
										
											2017-04-26 14:33:54 +08:00
										 |  |  | 	stage indexWriterStage | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-03 06:35:02 +08:00
										 |  |  | 	// Reusable memory.
 | 
					
						
							| 
									
										
										
										
											2017-04-25 23:21:24 +08:00
										 |  |  | 	buf1    encbuf | 
					
						
							|  |  |  | 	buf2    encbuf | 
					
						
							| 
									
										
										
										
											2017-03-03 06:35:02 +08:00
										 |  |  | 	uint32s []uint32 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	symbols       map[string]uint32 // symbol offsets
 | 
					
						
							| 
									
										
										
										
											2017-09-04 22:08:38 +08:00
										 |  |  | 	seriesOffsets map[uint64]uint64 // offsets of series
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	labelIndexes  []hashEntry       // label index offsets
 | 
					
						
							|  |  |  | 	postings      []hashEntry       // postings lists offsets
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Hold last series to validate that clients insert new series in order.
 | 
					
						
							|  |  |  | 	lastSeries labels.Labels | 
					
						
							| 
									
										
										
										
											2017-02-16 07:24:53 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	crc32 hash.Hash | 
					
						
							| 
									
										
										
										
											2016-12-10 04:23:34 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | type indexTOC struct { | 
					
						
							|  |  |  | 	symbols           uint64 | 
					
						
							|  |  |  | 	series            uint64 | 
					
						
							|  |  |  | 	labelIndices      uint64 | 
					
						
							|  |  |  | 	labelIndicesTable uint64 | 
					
						
							|  |  |  | 	postings          uint64 | 
					
						
							|  |  |  | 	postingsTable     uint64 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-25 14:24:20 +08:00
										 |  |  | func newIndexWriter(dir string) (*indexWriter, error) { | 
					
						
							|  |  |  | 	df, err := fileutil.OpenDir(dir) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-06 20:45:54 +08:00
										 |  |  | 	f, err := os.OpenFile(filepath.Join(dir, indexFilename), os.O_CREATE|os.O_WRONLY, 0666) | 
					
						
							| 
									
										
										
										
											2017-02-25 14:24:20 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := fileutil.Fsync(df); err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrap(err, "sync dir") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	iw := &indexWriter{ | 
					
						
							| 
									
										
										
										
											2017-04-26 14:33:54 +08:00
										 |  |  | 		f:     f, | 
					
						
							|  |  |  | 		fbuf:  bufio.NewWriterSize(f, 1<<22), | 
					
						
							|  |  |  | 		pos:   0, | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 		stage: idxStageNone, | 
					
						
							| 
									
										
										
										
											2017-03-03 06:35:02 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Reusable memory.
 | 
					
						
							| 
									
										
										
										
											2017-04-25 23:21:24 +08:00
										 |  |  | 		buf1:    encbuf{b: make([]byte, 0, 1<<22)}, | 
					
						
							|  |  |  | 		buf2:    encbuf{b: make([]byte, 0, 1<<22)}, | 
					
						
							| 
									
										
										
										
											2017-03-03 06:35:02 +08:00
										 |  |  | 		uint32s: make([]uint32, 0, 1<<15), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Caches.
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 		symbols:       make(map[string]uint32, 1<<13), | 
					
						
							| 
									
										
										
										
											2017-09-04 22:08:38 +08:00
										 |  |  | 		seriesOffsets: make(map[uint64]uint64, 1<<16), | 
					
						
							| 
									
										
										
										
											2017-08-27 00:04:00 +08:00
										 |  |  | 		crc32:         newCRC32(), | 
					
						
							| 
									
										
										
										
											2016-12-10 04:23:34 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-02-25 14:24:20 +08:00
										 |  |  | 	if err := iw.writeMeta(); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return iw, nil | 
					
						
							| 
									
										
										
										
											2016-12-10 04:23:34 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-25 23:21:24 +08:00
										 |  |  | func (w *indexWriter) write(bufs ...[]byte) error { | 
					
						
							|  |  |  | 	for _, b := range bufs { | 
					
						
							|  |  |  | 		n, err := w.fbuf.Write(b) | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 		w.pos += uint64(n) | 
					
						
							| 
									
										
										
										
											2017-04-25 23:21:24 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 		// For now the index file must not grow beyond 4GiB. Some of the fixed-sized
 | 
					
						
							| 
									
										
										
										
											2017-04-28 20:28:25 +08:00
										 |  |  | 		// offset references in v1 are only 4 bytes large.
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 		// Once we move to compressed/varint representations in those areas, this limitation
 | 
					
						
							|  |  |  | 		// can be lifted.
 | 
					
						
							|  |  |  | 		if w.pos > math.MaxUint32 { | 
					
						
							|  |  |  | 			return errors.Errorf("exceeding max size of 4GiB") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-25 23:21:24 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-12-10 04:23:34 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 20:28:25 +08:00
										 |  |  | // addPadding adds zero byte padding until the file size is a multiple of n.
 | 
					
						
							|  |  |  | func (w *indexWriter) addPadding(n int) error { | 
					
						
							|  |  |  | 	p := n - (int(w.pos) % n) | 
					
						
							|  |  |  | 	if p == 0 { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return errors.Wrap(w.write(make([]byte, p)), "add padding") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | // ensureStage handles transitions between write stages and ensures that IndexWriter
 | 
					
						
							|  |  |  | // methods are called in an order valid for the implementation.
 | 
					
						
							|  |  |  | func (w *indexWriter) ensureStage(s indexWriterStage) error { | 
					
						
							|  |  |  | 	if w.stage == s { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if w.stage > s { | 
					
						
							|  |  |  | 		return errors.Errorf("invalid stage %q, currently at %q", s, w.stage) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	// Mark start of sections in table of contents.
 | 
					
						
							|  |  |  | 	switch s { | 
					
						
							|  |  |  | 	case idxStageSymbols: | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 		w.toc.symbols = w.pos | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	case idxStageSeries: | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 		w.toc.series = w.pos | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case idxStageLabelIndex: | 
					
						
							|  |  |  | 		w.toc.labelIndices = w.pos | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case idxStagePostings: | 
					
						
							|  |  |  | 		w.toc.labelIndicesTable = w.pos | 
					
						
							|  |  |  | 		if err := w.writeOffsetTable(w.labelIndexes); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		w.toc.postings = w.pos | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case idxStageDone: | 
					
						
							|  |  |  | 		w.toc.postingsTable = w.pos | 
					
						
							|  |  |  | 		if err := w.writeOffsetTable(w.postings); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := w.writeTOC(); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.stage = s | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-12-11 01:08:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | func (w *indexWriter) writeMeta() error { | 
					
						
							|  |  |  | 	w.buf1.reset() | 
					
						
							|  |  |  | 	w.buf1.putBE32(MagicIndex) | 
					
						
							|  |  |  | 	w.buf1.putByte(indexFormatV1) | 
					
						
							| 
									
										
										
										
											2016-12-10 04:23:34 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	return w.write(w.buf1.get()) | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-04 22:08:38 +08:00
										 |  |  | func (w *indexWriter) AddSeries(ref uint64, lset labels.Labels, chunks ...ChunkMeta) error { | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	if err := w.ensureStage(idxStageSeries); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if labels.Compare(lset, w.lastSeries) <= 0 { | 
					
						
							|  |  |  | 		return errors.Errorf("out-of-order series added with label set %q", lset) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, ok := w.seriesOffsets[ref]; ok { | 
					
						
							| 
									
										
										
										
											2017-02-25 14:24:20 +08:00
										 |  |  | 		return errors.Errorf("series with reference %d already added", ref) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	w.seriesOffsets[ref] = w.pos | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.buf2.reset() | 
					
						
							|  |  |  | 	w.buf2.putUvarint(len(lset)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 04:23:34 +08:00
										 |  |  | 	for _, l := range lset { | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 		offset, ok := w.symbols[l.Name] | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			return errors.Errorf("symbol entry for %q does not exist", l.Name) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		w.buf2.putUvarint32(offset) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		offset, ok = w.symbols[l.Value] | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			return errors.Errorf("symbol entry for %q does not exist", l.Value) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		w.buf2.putUvarint32(offset) | 
					
						
							| 
									
										
										
										
											2016-12-10 04:23:34 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-10 16:44:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	w.buf2.putUvarint(len(chunks)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, c := range chunks { | 
					
						
							|  |  |  | 		w.buf2.putVarint64(c.MinTime) | 
					
						
							|  |  |  | 		w.buf2.putVarint64(c.MaxTime) | 
					
						
							|  |  |  | 		w.buf2.putUvarint64(c.Ref) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.buf1.reset() | 
					
						
							|  |  |  | 	w.buf1.putUvarint(w.buf2.len()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.buf2.putHash(w.crc32) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := w.write(w.buf1.get(), w.buf2.get()); err != nil { | 
					
						
							|  |  |  | 		return errors.Wrap(err, "write series data") | 
					
						
							| 
									
										
										
										
											2016-12-10 16:44:00 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	w.lastSeries = append(w.lastSeries[:0], lset...) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-25 14:24:20 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | func (w *indexWriter) AddSymbols(sym map[string]struct{}) error { | 
					
						
							|  |  |  | 	if err := w.ensureStage(idxStageSymbols); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-10 04:23:34 +08:00
										 |  |  | 	// Generate sorted list of strings we will store as reference table.
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	symbols := make([]string, 0, len(sym)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for s := range sym { | 
					
						
							| 
									
										
										
										
											2016-12-10 04:23:34 +08:00
										 |  |  | 		symbols = append(symbols, s) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sort.Strings(symbols) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 20:17:53 +08:00
										 |  |  | 	const headerSize = 4 | 
					
						
							| 
									
										
										
										
											2016-12-20 20:10:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-25 23:21:24 +08:00
										 |  |  | 	w.buf1.reset() | 
					
						
							|  |  |  | 	w.buf2.reset() | 
					
						
							| 
									
										
										
										
											2016-12-10 04:23:34 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 20:17:53 +08:00
										 |  |  | 	w.buf2.putBE32int(len(symbols)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	w.symbols = make(map[string]uint32, len(symbols)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 04:23:34 +08:00
										 |  |  | 	for _, s := range symbols { | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 		w.symbols[s] = uint32(w.pos) + headerSize + uint32(w.buf2.len()) | 
					
						
							| 
									
										
										
										
											2016-12-10 05:12:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 		// NOTE: len(s) gives the number of runes, not the number of bytes.
 | 
					
						
							|  |  |  | 		// Therefore the read-back length for strings with unicode characters will
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 		// be off when not using putUvarintStr.
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 		w.buf2.putUvarintStr(s) | 
					
						
							| 
									
										
										
										
											2016-12-10 05:12:16 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-10 04:23:34 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-25 23:21:24 +08:00
										 |  |  | 	w.buf1.putBE32int(w.buf2.len()) | 
					
						
							|  |  |  | 	w.buf2.putHash(w.crc32) | 
					
						
							| 
									
										
										
										
											2017-04-25 22:45:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-25 23:21:24 +08:00
										 |  |  | 	err := w.write(w.buf1.get(), w.buf2.get()) | 
					
						
							|  |  |  | 	return errors.Wrap(err, "write symbols") | 
					
						
							| 
									
										
										
										
											2016-12-10 05:27:43 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | func (w *indexWriter) WriteLabelIndex(names []string, values []string) error { | 
					
						
							| 
									
										
										
										
											2017-04-28 20:17:53 +08:00
										 |  |  | 	if len(values)%len(names) != 0 { | 
					
						
							|  |  |  | 		return errors.Errorf("invalid value list length %d for %d names", len(values), len(names)) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-26 14:33:54 +08:00
										 |  |  | 	if err := w.ensureStage(idxStageLabelIndex); err != nil { | 
					
						
							|  |  |  | 		return errors.Wrap(err, "ensure stage") | 
					
						
							| 
									
										
										
										
											2017-01-19 21:01:38 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-12 18:38:43 +08:00
										 |  |  | 	valt, err := newStringTuples(values, len(names)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-12-10 04:40:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-12 18:38:43 +08:00
										 |  |  | 	sort.Sort(valt) | 
					
						
							| 
									
										
										
										
											2016-12-10 04:40:38 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 20:28:25 +08:00
										 |  |  | 	// Align beginning to 4 bytes for more efficient index list scans.
 | 
					
						
							|  |  |  | 	if err := w.addPadding(4); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 05:12:16 +08:00
										 |  |  | 	w.labelIndexes = append(w.labelIndexes, hashEntry{ | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 		keys:   names, | 
					
						
							|  |  |  | 		offset: w.pos, | 
					
						
							| 
									
										
										
										
											2016-12-10 05:12:16 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 01:01:25 +08:00
										 |  |  | 	w.buf2.reset() | 
					
						
							| 
									
										
										
										
											2017-04-28 20:17:53 +08:00
										 |  |  | 	w.buf2.putBE32int(len(names)) | 
					
						
							|  |  |  | 	w.buf2.putBE32int(valt.Len()) | 
					
						
							| 
									
										
										
										
											2016-12-10 04:40:38 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-25 22:45:44 +08:00
										 |  |  | 	for _, v := range valt.s { | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 		offset, ok := w.symbols[v] | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			return errors.Errorf("symbol entry for %q does not exist", v) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		w.buf2.putBE32(offset) | 
					
						
							| 
									
										
										
										
											2017-04-25 22:45:44 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 01:01:25 +08:00
										 |  |  | 	w.buf1.reset() | 
					
						
							| 
									
										
										
										
											2017-04-28 20:17:53 +08:00
										 |  |  | 	w.buf1.putBE32int(w.buf2.len()) | 
					
						
							| 
									
										
										
										
											2017-04-25 22:45:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 01:01:25 +08:00
										 |  |  | 	w.buf2.putHash(w.crc32) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = w.write(w.buf1.get(), w.buf2.get()) | 
					
						
							|  |  |  | 	return errors.Wrap(err, "write label index") | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | // writeOffsetTable writes a sequence of readable hash entries.
 | 
					
						
							|  |  |  | func (w *indexWriter) writeOffsetTable(entries []hashEntry) error { | 
					
						
							|  |  |  | 	w.buf1.reset() | 
					
						
							|  |  |  | 	w.buf1.putBE32int(len(entries)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.buf2.reset() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, e := range entries { | 
					
						
							|  |  |  | 		w.buf2.putUvarint(len(e.keys)) | 
					
						
							|  |  |  | 		for _, k := range e.keys { | 
					
						
							|  |  |  | 			w.buf2.putUvarintStr(k) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		w.buf2.putUvarint64(e.offset) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.buf1.putBE32int(w.buf2.len()) | 
					
						
							|  |  |  | 	w.buf2.putHash(w.crc32) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return w.write(w.buf1.get(), w.buf2.get()) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const indexTOCLen = 6*8 + 4 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (w *indexWriter) writeTOC() error { | 
					
						
							|  |  |  | 	w.buf1.reset() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.buf1.putBE64(w.toc.symbols) | 
					
						
							|  |  |  | 	w.buf1.putBE64(w.toc.series) | 
					
						
							|  |  |  | 	w.buf1.putBE64(w.toc.labelIndices) | 
					
						
							|  |  |  | 	w.buf1.putBE64(w.toc.labelIndicesTable) | 
					
						
							|  |  |  | 	w.buf1.putBE64(w.toc.postings) | 
					
						
							|  |  |  | 	w.buf1.putBE64(w.toc.postingsTable) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.buf1.putHash(w.crc32) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return w.write(w.buf1.get()) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-15 04:58:29 +08:00
										 |  |  | func (w *indexWriter) WritePostings(name, value string, it Postings) error { | 
					
						
							| 
									
										
										
										
											2017-04-26 14:33:54 +08:00
										 |  |  | 	if err := w.ensureStage(idxStagePostings); err != nil { | 
					
						
							|  |  |  | 		return errors.Wrap(err, "ensure stage") | 
					
						
							| 
									
										
										
										
											2017-02-02 14:58:54 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 20:28:25 +08:00
										 |  |  | 	// Align beginning to 4 bytes for more efficient postings list scans.
 | 
					
						
							|  |  |  | 	if err := w.addPadding(4); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 16:44:00 +08:00
										 |  |  | 	w.postings = append(w.postings, hashEntry{ | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 		keys:   []string{name, value}, | 
					
						
							|  |  |  | 		offset: w.pos, | 
					
						
							| 
									
										
										
										
											2016-12-10 16:44:00 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-20 20:10:37 +08:00
										 |  |  | 	// Order of the references in the postings list does not imply order
 | 
					
						
							|  |  |  | 	// of the series references within the persisted block they are mapped to.
 | 
					
						
							|  |  |  | 	// We have to sort the new references again.
 | 
					
						
							| 
									
										
										
										
											2017-03-03 06:35:02 +08:00
										 |  |  | 	refs := w.uint32s[:0] | 
					
						
							| 
									
										
										
										
											2016-12-20 20:10:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 16:44:00 +08:00
										 |  |  | 	for it.Next() { | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 		offset, ok := w.seriesOffsets[it.At()] | 
					
						
							| 
									
										
										
										
											2017-01-02 17:34:55 +08:00
										 |  |  | 		if !ok { | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 			return errors.Errorf("%p series for reference %d not found", w, it.At()) | 
					
						
							| 
									
										
										
										
											2017-01-02 17:34:55 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-09-04 22:08:38 +08:00
										 |  |  | 		if offset > (1<<32)-1 { | 
					
						
							|  |  |  | 			return errors.Errorf("series offset %d exceeds 4 bytes", offset) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		refs = append(refs, uint32(offset)) | 
					
						
							| 
									
										
										
										
											2016-12-20 20:10:37 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if err := it.Err(); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-03-03 06:35:02 +08:00
										 |  |  | 	sort.Sort(uint32slice(refs)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 01:40:52 +08:00
										 |  |  | 	w.buf2.reset() | 
					
						
							| 
									
										
										
										
											2017-04-28 20:17:53 +08:00
										 |  |  | 	w.buf2.putBE32int(len(refs)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-20 20:10:37 +08:00
										 |  |  | 	for _, r := range refs { | 
					
						
							| 
									
										
										
										
											2017-04-26 01:40:52 +08:00
										 |  |  | 		w.buf2.putBE32(r) | 
					
						
							| 
									
										
										
										
											2016-12-10 16:44:00 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-08-07 02:41:24 +08:00
										 |  |  | 	w.uint32s = refs | 
					
						
							| 
									
										
										
										
											2016-12-10 16:44:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 01:40:52 +08:00
										 |  |  | 	w.buf1.reset() | 
					
						
							| 
									
										
										
										
											2017-04-28 20:17:53 +08:00
										 |  |  | 	w.buf1.putBE32int(w.buf2.len()) | 
					
						
							| 
									
										
										
										
											2017-04-25 22:45:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 01:40:52 +08:00
										 |  |  | 	w.buf2.putHash(w.crc32) | 
					
						
							| 
									
										
										
										
											2017-04-25 22:45:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-26 01:40:52 +08:00
										 |  |  | 	err := w.write(w.buf1.get(), w.buf2.get()) | 
					
						
							|  |  |  | 	return errors.Wrap(err, "write postings") | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-03 06:35:02 +08:00
										 |  |  | type uint32slice []uint32 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (s uint32slice) Len() int           { return len(s) } | 
					
						
							|  |  |  | func (s uint32slice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] } | 
					
						
							|  |  |  | func (s uint32slice) Less(i, j int) bool { return s[i] < s[j] } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 05:12:16 +08:00
										 |  |  | type hashEntry struct { | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	keys   []string | 
					
						
							|  |  |  | 	offset uint64 | 
					
						
							| 
									
										
										
										
											2016-12-10 05:12:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | func (w *indexWriter) Close() error { | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	if err := w.ensureStage(idxStageDone); err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-10 01:34:29 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-25 22:45:44 +08:00
										 |  |  | 	if err := w.fbuf.Flush(); err != nil { | 
					
						
							| 
									
										
										
										
											2017-02-25 14:24:20 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := fileutil.Fsync(w.f); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return w.f.Close() | 
					
						
							| 
									
										
										
										
											2016-12-10 03:45:46 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // IndexReader provides reading access of serialized index data.
 | 
					
						
							|  |  |  | type IndexReader interface { | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	// Symbols returns a set of string symbols that may occur in series' labels
 | 
					
						
							|  |  |  | 	// and indices.
 | 
					
						
							|  |  |  | 	Symbols() (map[string]struct{}, error) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	// LabelValues returns the possible label values
 | 
					
						
							|  |  |  | 	LabelValues(names ...string) (StringTuples, error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Postings returns the postings list iterator for the label pair.
 | 
					
						
							| 
									
										
										
										
											2017-03-30 07:18:41 +08:00
										 |  |  | 	// The Postings here contain the offsets to the series inside the index.
 | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	Postings(name, value string) (Postings, error) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	// SortedPostings returns a postings list that is reordered to be sorted
 | 
					
						
							|  |  |  | 	// by the label set of the underlying series.
 | 
					
						
							|  |  |  | 	SortedPostings(Postings) Postings | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Series populates the given labels and chunk metas for the series identified
 | 
					
						
							|  |  |  | 	// by the reference.
 | 
					
						
							| 
									
										
										
										
											2017-09-04 22:08:38 +08:00
										 |  |  | 	Series(ref uint64, lset *labels.Labels, chks *[]ChunkMeta) error | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// LabelIndices returns the label pairs for which indices exist.
 | 
					
						
							|  |  |  | 	LabelIndices() ([][]string, error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Close released the underlying resources of the reader.
 | 
					
						
							|  |  |  | 	Close() error | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // StringTuples provides access to a sorted list of string tuples.
 | 
					
						
							|  |  |  | type StringTuples interface { | 
					
						
							|  |  |  | 	// Total number of tuples in the list.
 | 
					
						
							|  |  |  | 	Len() int | 
					
						
							|  |  |  | 	// At returns the tuple at position i.
 | 
					
						
							|  |  |  | 	At(i int) ([]string, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type indexReader struct { | 
					
						
							|  |  |  | 	// The underlying byte slice holding the encoded series data.
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	b   []byte | 
					
						
							|  |  |  | 	toc indexTOC | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Close that releases the underlying resources of the byte slice.
 | 
					
						
							|  |  |  | 	c io.Closer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Cached hashmaps of section offsets.
 | 
					
						
							|  |  |  | 	labels   map[string]uint32 | 
					
						
							|  |  |  | 	postings map[string]uint32 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	errInvalidSize = fmt.Errorf("invalid size") | 
					
						
							|  |  |  | 	errInvalidFlag = fmt.Errorf("invalid flag") | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // newIndexReader returns a new indexReader on the given directory.
 | 
					
						
							|  |  |  | func newIndexReader(dir string) (*indexReader, error) { | 
					
						
							|  |  |  | 	f, err := openMmapFile(filepath.Join(dir, "index")) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	r := &indexReader{b: f.b, c: f} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Verify magic number.
 | 
					
						
							|  |  |  | 	if len(f.b) < 4 { | 
					
						
							|  |  |  | 		return nil, errors.Wrap(errInvalidSize, "index header") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if m := binary.BigEndian.Uint32(r.b[:4]); m != MagicIndex { | 
					
						
							|  |  |  | 		return nil, errors.Errorf("invalid magic number %x", m) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	if err := r.readTOC(); err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrap(err, "read TOC") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	r.labels, err = r.readOffsetTable(r.toc.labelIndicesTable) | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 		return nil, errors.Wrap(err, "read label index table") | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	r.postings, err = r.readOffsetTable(r.toc.postingsTable) | 
					
						
							| 
									
										
										
										
											2017-05-17 11:06:56 +08:00
										 |  |  | 	return r, errors.Wrap(err, "read postings table") | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | func (r *indexReader) readTOC() error { | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 	d := r.decbufAt(len(r.b) - indexTOCLen) | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 	r.toc.symbols = d.be64() | 
					
						
							|  |  |  | 	r.toc.series = d.be64() | 
					
						
							|  |  |  | 	r.toc.labelIndices = d.be64() | 
					
						
							|  |  |  | 	r.toc.labelIndicesTable = d.be64() | 
					
						
							|  |  |  | 	r.toc.postings = d.be64() | 
					
						
							|  |  |  | 	r.toc.postingsTable = d.be64() | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	// TODO(fabxc): validate checksum.
 | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *indexReader) decbufAt(off int) decbuf { | 
					
						
							|  |  |  | 	if len(r.b) < off { | 
					
						
							|  |  |  | 		return decbuf{e: errInvalidSize} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return decbuf{b: r.b[off:]} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // readOffsetTable reads an offset table at the given position and returns a map
 | 
					
						
							|  |  |  | // with the key strings concatenated by the 0xff unicode non-character.
 | 
					
						
							|  |  |  | func (r *indexReader) readOffsetTable(off uint64) (map[string]uint32, error) { | 
					
						
							|  |  |  | 	// A table might not have been written at all, in which case the position
 | 
					
						
							|  |  |  | 	// is zeroed out.
 | 
					
						
							|  |  |  | 	if off == 0 { | 
					
						
							|  |  |  | 		return nil, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const sep = "\xff" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		d1  = r.decbufAt(int(off)) | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 		cnt = d1.be32() | 
					
						
							|  |  |  | 		d2  = d1.decbuf(d1.be32int()) | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res := make(map[string]uint32, 512) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for d2.err() == nil && d2.len() > 0 && cnt > 0 { | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 		keyCount := int(d2.uvarint()) | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 		keys := make([]string, 0, keyCount) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for i := 0; i < keyCount; i++ { | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 			keys = append(keys, d2.uvarintStr()) | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 		res[strings.Join(keys, sep)] = uint32(d2.uvarint()) | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 		cnt-- | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	// TODO(fabxc): verify checksum from remainer of d1.
 | 
					
						
							|  |  |  | 	return res, d2.err() | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *indexReader) Close() error { | 
					
						
							|  |  |  | 	return r.c.Close() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *indexReader) section(o uint32) (byte, []byte, error) { | 
					
						
							|  |  |  | 	b := r.b[o:] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(b) < 5 { | 
					
						
							|  |  |  | 		return 0, nil, errors.Wrap(errInvalidSize, "read header") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	flag := b[0] | 
					
						
							|  |  |  | 	l := binary.BigEndian.Uint32(b[1:5]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	b = b[5:] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// b must have the given length plus 4 bytes for the CRC32 checksum.
 | 
					
						
							|  |  |  | 	if len(b) < int(l)+4 { | 
					
						
							|  |  |  | 		return 0, nil, errors.Wrap(errInvalidSize, "section content") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return flag, b[:l], nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *indexReader) lookupSymbol(o uint32) (string, error) { | 
					
						
							| 
									
										
										
										
											2017-04-27 02:32:34 +08:00
										 |  |  | 	d := r.decbufAt(int(o)) | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 02:32:34 +08:00
										 |  |  | 	s := d.uvarintStr() | 
					
						
							|  |  |  | 	if d.err() != nil { | 
					
						
							|  |  |  | 		return "", errors.Wrapf(d.err(), "read symbol at %d", o) | 
					
						
							| 
									
										
										
										
											2017-04-26 01:40:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-27 02:32:34 +08:00
										 |  |  | 	return s, nil | 
					
						
							| 
									
										
										
										
											2017-04-26 01:40:52 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | func (r *indexReader) Symbols() (map[string]struct{}, error) { | 
					
						
							|  |  |  | 	d1 := r.decbufAt(int(r.toc.symbols)) | 
					
						
							|  |  |  | 	d2 := d1.decbuf(d1.be32int()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	count := d2.be32int() | 
					
						
							|  |  |  | 	sym := make(map[string]struct{}, count) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for ; count > 0; count-- { | 
					
						
							|  |  |  | 		s := d2.uvarintStr() | 
					
						
							|  |  |  | 		sym[s] = struct{}{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return sym, d2.err() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | func (r *indexReader) LabelValues(names ...string) (StringTuples, error) { | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	const sep = "\xff" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	key := strings.Join(names, sep) | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	off, ok := r.labels[key] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2017-03-20 18:37:06 +08:00
										 |  |  | 		// XXX(fabxc): hot fix. Should return a partial data error and handle cases
 | 
					
						
							|  |  |  | 		// where the entire block has no data gracefully.
 | 
					
						
							|  |  |  | 		return emptyStringTuples{}, nil | 
					
						
							|  |  |  | 		//return nil, fmt.Errorf("label index doesn't exist")
 | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 	d1 := r.decbufAt(int(off)) | 
					
						
							| 
									
										
										
										
											2017-04-28 20:17:53 +08:00
										 |  |  | 	d2 := d1.decbuf(d1.be32int()) | 
					
						
							| 
									
										
										
										
											2017-04-26 01:01:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 20:17:53 +08:00
										 |  |  | 	nc := d2.be32int() | 
					
						
							|  |  |  | 	d2.be32() // consume unused value entry count.
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if d2.err() != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrap(d2.err(), "read label value index") | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 	// TODO(fabxc): verify checksum in 4 remaining bytes of d1.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	st := &serializedStringTuples{ | 
					
						
							| 
									
										
										
										
											2017-04-28 20:17:53 +08:00
										 |  |  | 		l:      nc, | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 		b:      d2.get(), | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 		lookup: r.lookupSymbol, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return st, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-20 18:37:06 +08:00
										 |  |  | type emptyStringTuples struct{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (emptyStringTuples) At(i int) ([]string, error) { return nil, nil } | 
					
						
							|  |  |  | func (emptyStringTuples) Len() int                   { return 0 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | func (r *indexReader) LabelIndices() ([][]string, error) { | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	const sep = "\xff" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	res := [][]string{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for s := range r.labels { | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 		res = append(res, strings.Split(s, sep)) | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return res, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-04 22:08:38 +08:00
										 |  |  | func (r *indexReader) Series(ref uint64, lbls *labels.Labels, chks *[]ChunkMeta) error { | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 	d1 := r.decbufAt(int(ref)) | 
					
						
							|  |  |  | 	d2 := d1.decbuf(int(d1.uvarint())) | 
					
						
							| 
									
										
										
										
											2017-04-25 22:45:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	*lbls = (*lbls)[:0] | 
					
						
							|  |  |  | 	*chks = (*chks)[:0] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 	k := int(d2.uvarint()) | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 	for i := 0; i < k; i++ { | 
					
						
							|  |  |  | 		lno := uint32(d2.uvarint()) | 
					
						
							|  |  |  | 		lvo := uint32(d2.uvarint()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if d2.err() != nil { | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 			return errors.Wrap(d2.err(), "read series label offsets") | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 		ln, err := r.lookupSymbol(lno) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 			return errors.Wrap(err, "lookup label name") | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 		lv, err := r.lookupSymbol(lvo) | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 			return errors.Wrap(err, "lookup label value") | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 		*lbls = append(*lbls, labels.Label{Name: ln, Value: lv}) | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Read the chunks meta data.
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 	k = int(d2.uvarint()) | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 	for i := 0; i < k; i++ { | 
					
						
							|  |  |  | 		mint := d2.varint64() | 
					
						
							|  |  |  | 		maxt := d2.varint64() | 
					
						
							|  |  |  | 		off := d2.uvarint64() | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 		if d2.err() != nil { | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 			return errors.Wrapf(d2.err(), "read meta for chunk %d", i) | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-25 22:45:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-07 02:41:24 +08:00
										 |  |  | 		*chks = append(*chks, ChunkMeta{ | 
					
						
							| 
									
										
										
										
											2017-04-27 00:03:44 +08:00
										 |  |  | 			Ref:     off, | 
					
						
							|  |  |  | 			MinTime: mint, | 
					
						
							|  |  |  | 			MaxTime: maxt, | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-28 20:17:53 +08:00
										 |  |  | 	// TODO(fabxc): verify CRC32.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *indexReader) Postings(name, value string) (Postings, error) { | 
					
						
							| 
									
										
										
										
											2017-04-27 00:01:13 +08:00
										 |  |  | 	const sep = "\xff" | 
					
						
							| 
									
										
										
										
											2017-04-27 02:32:34 +08:00
										 |  |  | 	key := strings.Join([]string{name, value}, sep) | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	off, ok := r.postings[key] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2017-04-05 20:14:30 +08:00
										 |  |  | 		return emptyPostings, nil | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 02:32:34 +08:00
										 |  |  | 	d1 := r.decbufAt(int(off)) | 
					
						
							| 
									
										
										
										
											2017-04-28 20:17:53 +08:00
										 |  |  | 	d2 := d1.decbuf(d1.be32int()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	d2.be32() // consume unused postings list length.
 | 
					
						
							| 
									
										
										
										
											2017-04-27 02:32:34 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if d2.err() != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrap(d2.err(), "get postings bytes") | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-25 22:45:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-27 02:32:34 +08:00
										 |  |  | 	// TODO(fabxc): read checksum from 4 remainer bytes of d1 and verify.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return newBigEndianPostings(d2.get()), nil | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-05 19:31:48 +08:00
										 |  |  | func (r *indexReader) SortedPostings(p Postings) Postings { | 
					
						
							|  |  |  | 	return p | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-07 19:47:49 +08:00
										 |  |  | type stringTuples struct { | 
					
						
							|  |  |  | 	l int      // tuple length
 | 
					
						
							|  |  |  | 	s []string // flattened tuple entries
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newStringTuples(s []string, l int) (*stringTuples, error) { | 
					
						
							|  |  |  | 	if len(s)%l != 0 { | 
					
						
							|  |  |  | 		return nil, errors.Wrap(errInvalidSize, "string tuple list") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return &stringTuples{s: s, l: l}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t *stringTuples) Len() int                   { return len(t.s) / t.l } | 
					
						
							|  |  |  | func (t *stringTuples) At(i int) ([]string, error) { return t.s[i : i+t.l], nil } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t *stringTuples) Swap(i, j int) { | 
					
						
							|  |  |  | 	c := make([]string, t.l) | 
					
						
							|  |  |  | 	copy(c, t.s[i:i+t.l]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for k := 0; k < t.l; k++ { | 
					
						
							|  |  |  | 		t.s[i+k] = t.s[j+k] | 
					
						
							|  |  |  | 		t.s[j+k] = c[k] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t *stringTuples) Less(i, j int) bool { | 
					
						
							|  |  |  | 	for k := 0; k < t.l; k++ { | 
					
						
							|  |  |  | 		d := strings.Compare(t.s[i+k], t.s[j+k]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if d < 0 { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if d > 0 { | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type serializedStringTuples struct { | 
					
						
							|  |  |  | 	l      int | 
					
						
							|  |  |  | 	b      []byte | 
					
						
							|  |  |  | 	lookup func(uint32) (string, error) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t *serializedStringTuples) Len() int { | 
					
						
							|  |  |  | 	return len(t.b) / (4 * t.l) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t *serializedStringTuples) At(i int) ([]string, error) { | 
					
						
							|  |  |  | 	if len(t.b) < (i+t.l)*4 { | 
					
						
							|  |  |  | 		return nil, errInvalidSize | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	res := make([]string, 0, t.l) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for k := 0; k < t.l; k++ { | 
					
						
							|  |  |  | 		offset := binary.BigEndian.Uint32(t.b[(i+k)*4:]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		s, err := t.lookup(offset) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrap(err, "symbol lookup") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		res = append(res, s) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return res, nil | 
					
						
							|  |  |  | } |