| 
									
										
										
										
											2019-07-22 12:17:27 +03:00
										 |  |  | // Copyright 2018 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Package memorydb implements the key-value database layer based on memory maps. | 
					
						
							|  |  |  | package memorydb | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"sort" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/ethdb" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							|  |  |  | 	// errMemorydbClosed is returned if a memory database was already closed at the | 
					
						
							|  |  |  | 	// invocation of a data access operation. | 
					
						
							|  |  |  | 	errMemorydbClosed = errors.New("database closed") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// errMemorydbNotFound is returned if a key is requested that is not found in | 
					
						
							|  |  |  | 	// the provided memory database. | 
					
						
							|  |  |  | 	errMemorydbNotFound = errors.New("not found") | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | // Database is an ephemeral key-value store. Apart from basic data storage | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | // functionality it also supports batch writes and iterating over the keyspace in | 
					
						
							|  |  |  | // binary-alphabetical order. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | type Database struct { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	db   map[string][]byte | 
					
						
							|  |  |  | 	lock sync.RWMutex | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // New returns a wrapped map with all the required database interface methods | 
					
						
							|  |  |  | // implemented. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func New() *Database { | 
					
						
							|  |  |  | 	return &Database{ | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 		db: make(map[string][]byte), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewWithCap returns a wrapped map pre-allocated to the provided capcity with | 
					
						
							|  |  |  | // all the required database interface methods implemented. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func NewWithCap(size int) *Database { | 
					
						
							|  |  |  | 	return &Database{ | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 		db: make(map[string][]byte, size), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Close deallocates the internal map and ensures any consecutive data access op | 
					
						
							|  |  |  | // failes with an error. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (db *Database) Close() error { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	db.lock.Lock() | 
					
						
							|  |  |  | 	defer db.lock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	db.db = nil | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Has retrieves if a key is present in the key-value store. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (db *Database) Has(key []byte) (bool, error) { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	db.lock.RLock() | 
					
						
							|  |  |  | 	defer db.lock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if db.db == nil { | 
					
						
							|  |  |  | 		return false, errMemorydbClosed | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	_, ok := db.db[string(key)] | 
					
						
							|  |  |  | 	return ok, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get retrieves the given key if it's present in the key-value store. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (db *Database) Get(key []byte) ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	db.lock.RLock() | 
					
						
							|  |  |  | 	defer db.lock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if db.db == nil { | 
					
						
							|  |  |  | 		return nil, errMemorydbClosed | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if entry, ok := db.db[string(key)]; ok { | 
					
						
							|  |  |  | 		return common.CopyBytes(entry), nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil, errMemorydbNotFound | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Put inserts the given value into the key-value store. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (db *Database) Put(key []byte, value []byte) error { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	db.lock.Lock() | 
					
						
							|  |  |  | 	defer db.lock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if db.db == nil { | 
					
						
							|  |  |  | 		return errMemorydbClosed | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	db.db[string(key)] = common.CopyBytes(value) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Delete removes the key from the key-value store. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (db *Database) Delete(key []byte) error { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	db.lock.Lock() | 
					
						
							|  |  |  | 	defer db.lock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if db.db == nil { | 
					
						
							|  |  |  | 		return errMemorydbClosed | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	delete(db.db, string(key)) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewBatch creates a write-only key-value store that buffers changes to its host | 
					
						
							|  |  |  | // database until a final write is called. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (db *Database) NewBatch() ethdb.Batch { | 
					
						
							|  |  |  | 	return &batch{ | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 		db: db, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-15 13:08:53 +02:00
										 |  |  | // NewIterator creates a binary-alphabetical iterator over a subset | 
					
						
							|  |  |  | // of database content with a particular key prefix, starting at a particular | 
					
						
							|  |  |  | // initial key (or after, if it does not exist). | 
					
						
							|  |  |  | func (db *Database) NewIterator(prefix []byte, start []byte) ethdb.Iterator { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	db.lock.RLock() | 
					
						
							|  |  |  | 	defer db.lock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		pr     = string(prefix) | 
					
						
							| 
									
										
										
										
											2020-04-15 13:08:53 +02:00
										 |  |  | 		st     = string(append(prefix, start...)) | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 		keys   = make([]string, 0, len(db.db)) | 
					
						
							|  |  |  | 		values = make([][]byte, 0, len(db.db)) | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	// Collect the keys from the memory database corresponding to the given prefix | 
					
						
							| 
									
										
										
										
											2020-04-15 13:08:53 +02:00
										 |  |  | 	// and start | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	for key := range db.db { | 
					
						
							| 
									
										
										
										
											2020-04-15 13:08:53 +02:00
										 |  |  | 		if !strings.HasPrefix(key, pr) { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if key >= st { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 			keys = append(keys, key) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Sort the items and retrieve the associated values | 
					
						
							|  |  |  | 	sort.Strings(keys) | 
					
						
							|  |  |  | 	for _, key := range keys { | 
					
						
							|  |  |  | 		values = append(values, db.db[key]) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | 	return &iterator{ | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 		keys:   keys, | 
					
						
							|  |  |  | 		values: values, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Stat returns a particular internal stat of the database. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (db *Database) Stat(property string) (string, error) { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	return "", errors.New("unknown property") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-01 11:00:56 +03:00
										 |  |  | // Compact is not supported on a memory database, but there's no need either as | 
					
						
							|  |  |  | // a memory database doesn't waste space anyway. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (db *Database) Compact(start []byte, limit []byte) error { | 
					
						
							| 
									
										
										
										
											2019-08-01 11:00:56 +03:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Len returns the number of entries currently present in the memory database. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Note, this method is only used for testing (i.e. not public in general) and | 
					
						
							|  |  |  | // does not have explicit checks for closed-ness to allow simpler testing code. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (db *Database) Len() int { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	db.lock.RLock() | 
					
						
							|  |  |  | 	defer db.lock.RUnlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return len(db.db) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // keyvalue is a key-value tuple tagged with a deletion field to allow creating | 
					
						
							|  |  |  | // memory-database write batches. | 
					
						
							|  |  |  | type keyvalue struct { | 
					
						
							|  |  |  | 	key    []byte | 
					
						
							|  |  |  | 	value  []byte | 
					
						
							|  |  |  | 	delete bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | // batch is a write-only memory batch that commits changes to its host | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | // database when Write is called. A batch cannot be used concurrently. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | type batch struct { | 
					
						
							|  |  |  | 	db     *Database | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	writes []keyvalue | 
					
						
							|  |  |  | 	size   int | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Put inserts the given value into the batch for later committing. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (b *batch) Put(key, value []byte) error { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	b.writes = append(b.writes, keyvalue{common.CopyBytes(key), common.CopyBytes(value), false}) | 
					
						
							|  |  |  | 	b.size += len(value) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Delete inserts the a key removal into the batch for later committing. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (b *batch) Delete(key []byte) error { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	b.writes = append(b.writes, keyvalue{common.CopyBytes(key), nil, true}) | 
					
						
							|  |  |  | 	b.size += 1 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ValueSize retrieves the amount of data queued up for writing. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (b *batch) ValueSize() int { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	return b.size | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Write flushes any accumulated data to the memory database. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (b *batch) Write() error { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	b.db.lock.Lock() | 
					
						
							|  |  |  | 	defer b.db.lock.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, keyvalue := range b.writes { | 
					
						
							|  |  |  | 		if keyvalue.delete { | 
					
						
							|  |  |  | 			delete(b.db.db, string(keyvalue.key)) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		b.db.db[string(keyvalue.key)] = keyvalue.value | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Reset resets the batch for reuse. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (b *batch) Reset() { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	b.writes = b.writes[:0] | 
					
						
							|  |  |  | 	b.size = 0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-26 15:48:31 +01:00
										 |  |  | // Replay replays the batch contents. | 
					
						
							| 
									
										
										
											
												all: integrate the freezer with fast sync
* all: freezer style syncing
core, eth, les, light: clean up freezer relative APIs
core, eth, les, trie, ethdb, light: clean a bit
core, eth, les, light: add unit tests
core, light: rewrite setHead function
core, eth: fix downloader unit tests
core: add receipt chain insertion test
core: use constant instead of hardcoding table name
core: fix rollback
core: fix setHead
core/rawdb: remove canonical block first and then iterate side chain
core/rawdb, ethdb: add hasAncient interface
eth/downloader: calculate ancient limit via cht first
core, eth, ethdb: lots of fixes
* eth/downloader: print ancient disable log only for fast sync
											
										 
											2019-04-25 22:59:48 +08:00
										 |  |  | func (b *batch) Replay(w ethdb.KeyValueWriter) error { | 
					
						
							| 
									
										
										
										
											2019-03-26 15:48:31 +01:00
										 |  |  | 	for _, keyvalue := range b.writes { | 
					
						
							|  |  |  | 		if keyvalue.delete { | 
					
						
							|  |  |  | 			if err := w.Delete(keyvalue.key); err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err := w.Put(keyvalue.key, keyvalue.value); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | // iterator can walk over the (potentially partial) keyspace of a memory key | 
					
						
							|  |  |  | // value store. Internally it is a deep copy of the entire iterated state, | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | // sorted by keys. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | type iterator struct { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	inited bool | 
					
						
							|  |  |  | 	keys   []string | 
					
						
							|  |  |  | 	values [][]byte | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Next moves the iterator to the next key/value pair. It returns whether the | 
					
						
							|  |  |  | // iterator is exhausted. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (it *iterator) Next() bool { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	// If the iterator was not yet initialized, do it now | 
					
						
							|  |  |  | 	if !it.inited { | 
					
						
							|  |  |  | 		it.inited = true | 
					
						
							|  |  |  | 		return len(it.keys) > 0 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Iterator already initialize, advance it | 
					
						
							|  |  |  | 	if len(it.keys) > 0 { | 
					
						
							|  |  |  | 		it.keys = it.keys[1:] | 
					
						
							|  |  |  | 		it.values = it.values[1:] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return len(it.keys) > 0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Error returns any accumulated error. Exhausting all the key/value pairs | 
					
						
							|  |  |  | // is not considered to be an error. A memory iterator cannot encounter errors. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (it *iterator) Error() error { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Key returns the key of the current key/value pair, or nil if done. The caller | 
					
						
							|  |  |  | // should not modify the contents of the returned slice, and its contents may | 
					
						
							|  |  |  | // change on the next call to Next. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (it *iterator) Key() []byte { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	if len(it.keys) > 0 { | 
					
						
							|  |  |  | 		return []byte(it.keys[0]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Value returns the value of the current key/value pair, or nil if done. The | 
					
						
							|  |  |  | // caller should not modify the contents of the returned slice, and its contents | 
					
						
							|  |  |  | // may change on the next call to Next. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (it *iterator) Value() []byte { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	if len(it.values) > 0 { | 
					
						
							|  |  |  | 		return it.values[0] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Release releases associated resources. Release should always succeed and can | 
					
						
							|  |  |  | // be called multiple times without causing error. | 
					
						
							| 
									
										
										
										
											2019-03-11 17:01:47 +02:00
										 |  |  | func (it *iterator) Release() { | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	it.keys, it.values = nil, nil | 
					
						
							|  |  |  | } |