// Code generated by file_store.gen.go.tmpl. DO NOT EDIT.

package tsm1

import (
	"github.com/influxdata/influxdb/v2/tsdb/cursors"
)

// ReadFloatArrayBlock reads the next block as a set of float values.
func (c *KeyCursor) ReadFloatArrayBlock(values *cursors.FloatArray) (*cursors.FloatArray, error) {
LOOP:
	// No matching blocks to decode
	if len(c.current) == 0 {
		values.Timestamps = values.Timestamps[:0]
		values.Values = values.Values[:0]
		return values, nil
	}

	// First block is the oldest block containing the points we're searching for.
	first := c.current[0]
	err := first.r.ReadFloatArrayBlockAt(&first.entry, values)
	if err != nil {
		return nil, err
	}
	if c.col != nil {
		c.col.GetCounter(floatBlocksDecodedCounter).Add(1)
		c.col.GetCounter(floatBlocksSizeCounter).Add(int64(first.entry.Size))
	}

	// Remove values we already read
	values.Exclude(first.readMin, first.readMax)

	// Remove any tombstones
	c.trbuf = first.r.TombstoneRange(c.key, c.trbuf[:0])
	excludeTombstonesFloatArray(c.trbuf, values)
	// If there are no values in this first block (all tombstoned or previously read) and
	// we have more potential blocks too search.  Try again.
	if values.Len() == 0 && len(c.current) > 0 {
		c.current = c.current[1:]
		goto LOOP
	}

	// Only one block with this key and time range so return it
	if len(c.current) == 1 {
		if values.Len() > 0 {
			first.markRead(values.MinTime(), values.MaxTime())
		}
		return values, nil
	}

	// Use the current block time range as our overlapping window
	minT, maxT := first.readMin, first.readMax
	if values.Len() > 0 {
		minT, maxT = values.MinTime(), values.MaxTime()
	}
	if c.ascending {
		// Blocks are ordered by generation, we may have values in the past in later blocks, if so,
		// expand the window to include the min time range to ensure values are returned in ascending
		// order
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.MinTime < minT && !cur.read() {
				minT = cur.entry.MinTime
			}
		}

		// Find first block that overlaps our window
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.OverlapsTimeRange(minT, maxT) && !cur.read() {
				// Shrink our window so it's the intersection of the first overlapping block and the
				// first block.  We do this to minimize the region that overlaps and needs to
				// be merged.
				if cur.entry.MaxTime > maxT {
					maxT = cur.entry.MaxTime
				}
				values.Include(minT, maxT)
				break
			}
		}

		// Search the remaining blocks that overlap our window and append their values so we can
		// merge them.
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			// Skip this block if it doesn't contain points we looking for or they have already been read
			if !cur.entry.OverlapsTimeRange(minT, maxT) || cur.read() {
				cur.markRead(minT, maxT)
				continue
			}

			v := &cursors.FloatArray{}
			err := cur.r.ReadFloatArrayBlockAt(&cur.entry, v)
			if err != nil {
				return nil, err
			}
			if c.col != nil {
				c.col.GetCounter(floatBlocksDecodedCounter).Add(1)
				c.col.GetCounter(floatBlocksSizeCounter).Add(int64(cur.entry.Size))
			}

			c.trbuf = cur.r.TombstoneRange(c.key, c.trbuf[:0])
			// Remove any tombstoned values
			excludeTombstonesFloatArray(c.trbuf, v)

			// Remove values we already read
			v.Exclude(cur.readMin, cur.readMax)

			if v.Len() > 0 {
				// Only use values in the overlapping window
				v.Include(minT, maxT)
				// Merge the remaining values with the existing
				values.Merge(v)
			}
			cur.markRead(minT, maxT)
		}

	} else {
		// Blocks are ordered by generation, we may have values in the past in later blocks, if so,
		// expand the window to include the max time range to ensure values are returned in descending
		// order
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.MaxTime > maxT && !cur.read() {
				maxT = cur.entry.MaxTime
			}
		}

		// Find first block that overlaps our window
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.OverlapsTimeRange(minT, maxT) && !cur.read() {
				// Shrink our window so it's the intersection of the first overlapping block and the
				// first block.  We do this to minimize the region that overlaps and needs to
				// be merged.
				if cur.entry.MinTime < minT {
					minT = cur.entry.MinTime
				}
				values.Include(minT, maxT)
				break
			}
		}

		// Search the remaining blocks that overlap our window and append their values so we can
		// merge them.
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			// Skip this block if it doesn't contain points we looking for or they have already been read
			if !cur.entry.OverlapsTimeRange(minT, maxT) || cur.read() {
				cur.markRead(minT, maxT)
				continue
			}

			v := &cursors.FloatArray{}
			err := cur.r.ReadFloatArrayBlockAt(&cur.entry, v)
			if err != nil {
				return nil, err
			}
			if c.col != nil {
				c.col.GetCounter(floatBlocksDecodedCounter).Add(1)
				c.col.GetCounter(floatBlocksSizeCounter).Add(int64(cur.entry.Size))
			}
			c.trbuf = cur.r.TombstoneRange(c.key, c.trbuf[:0])
			// Remove any tombstoned values
			excludeTombstonesFloatArray(c.trbuf, v)

			// Remove values we already read
			v.Exclude(cur.readMin, cur.readMax)

			// If the block we decoded should have all of it's values included, mark it as read so we
			// don't use it again.
			if v.Len() > 0 {
				v.Include(minT, maxT)
				// Merge the remaining values with the existing
				v.Merge(values)
				*values = *v
			}
			cur.markRead(minT, maxT)
		}
	}

	first.markRead(minT, maxT)

	return values, err
}

func excludeTombstonesFloatArray(t []TimeRange, values *cursors.FloatArray) {
	for i := range t {
		values.Exclude(t[i].Min, t[i].Max)
	}
}

// ReadIntegerArrayBlock reads the next block as a set of integer values.
func (c *KeyCursor) ReadIntegerArrayBlock(values *cursors.IntegerArray) (*cursors.IntegerArray, error) {
LOOP:
	// No matching blocks to decode
	if len(c.current) == 0 {
		values.Timestamps = values.Timestamps[:0]
		values.Values = values.Values[:0]
		return values, nil
	}

	// First block is the oldest block containing the points we're searching for.
	first := c.current[0]
	err := first.r.ReadIntegerArrayBlockAt(&first.entry, values)
	if err != nil {
		return nil, err
	}
	if c.col != nil {
		c.col.GetCounter(integerBlocksDecodedCounter).Add(1)
		c.col.GetCounter(integerBlocksSizeCounter).Add(int64(first.entry.Size))
	}

	// Remove values we already read
	values.Exclude(first.readMin, first.readMax)

	// Remove any tombstones
	c.trbuf = first.r.TombstoneRange(c.key, c.trbuf[:0])
	excludeTombstonesIntegerArray(c.trbuf, values)
	// If there are no values in this first block (all tombstoned or previously read) and
	// we have more potential blocks too search.  Try again.
	if values.Len() == 0 && len(c.current) > 0 {
		c.current = c.current[1:]
		goto LOOP
	}

	// Only one block with this key and time range so return it
	if len(c.current) == 1 {
		if values.Len() > 0 {
			first.markRead(values.MinTime(), values.MaxTime())
		}
		return values, nil
	}

	// Use the current block time range as our overlapping window
	minT, maxT := first.readMin, first.readMax
	if values.Len() > 0 {
		minT, maxT = values.MinTime(), values.MaxTime()
	}
	if c.ascending {
		// Blocks are ordered by generation, we may have values in the past in later blocks, if so,
		// expand the window to include the min time range to ensure values are returned in ascending
		// order
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.MinTime < minT && !cur.read() {
				minT = cur.entry.MinTime
			}
		}

		// Find first block that overlaps our window
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.OverlapsTimeRange(minT, maxT) && !cur.read() {
				// Shrink our window so it's the intersection of the first overlapping block and the
				// first block.  We do this to minimize the region that overlaps and needs to
				// be merged.
				if cur.entry.MaxTime > maxT {
					maxT = cur.entry.MaxTime
				}
				values.Include(minT, maxT)
				break
			}
		}

		// Search the remaining blocks that overlap our window and append their values so we can
		// merge them.
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			// Skip this block if it doesn't contain points we looking for or they have already been read
			if !cur.entry.OverlapsTimeRange(minT, maxT) || cur.read() {
				cur.markRead(minT, maxT)
				continue
			}

			v := &cursors.IntegerArray{}
			err := cur.r.ReadIntegerArrayBlockAt(&cur.entry, v)
			if err != nil {
				return nil, err
			}
			if c.col != nil {
				c.col.GetCounter(integerBlocksDecodedCounter).Add(1)
				c.col.GetCounter(integerBlocksSizeCounter).Add(int64(cur.entry.Size))
			}

			c.trbuf = cur.r.TombstoneRange(c.key, c.trbuf[:0])
			// Remove any tombstoned values
			excludeTombstonesIntegerArray(c.trbuf, v)

			// Remove values we already read
			v.Exclude(cur.readMin, cur.readMax)

			if v.Len() > 0 {
				// Only use values in the overlapping window
				v.Include(minT, maxT)
				// Merge the remaining values with the existing
				values.Merge(v)
			}
			cur.markRead(minT, maxT)
		}

	} else {
		// Blocks are ordered by generation, we may have values in the past in later blocks, if so,
		// expand the window to include the max time range to ensure values are returned in descending
		// order
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.MaxTime > maxT && !cur.read() {
				maxT = cur.entry.MaxTime
			}
		}

		// Find first block that overlaps our window
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.OverlapsTimeRange(minT, maxT) && !cur.read() {
				// Shrink our window so it's the intersection of the first overlapping block and the
				// first block.  We do this to minimize the region that overlaps and needs to
				// be merged.
				if cur.entry.MinTime < minT {
					minT = cur.entry.MinTime
				}
				values.Include(minT, maxT)
				break
			}
		}

		// Search the remaining blocks that overlap our window and append their values so we can
		// merge them.
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			// Skip this block if it doesn't contain points we looking for or they have already been read
			if !cur.entry.OverlapsTimeRange(minT, maxT) || cur.read() {
				cur.markRead(minT, maxT)
				continue
			}

			v := &cursors.IntegerArray{}
			err := cur.r.ReadIntegerArrayBlockAt(&cur.entry, v)
			if err != nil {
				return nil, err
			}
			if c.col != nil {
				c.col.GetCounter(integerBlocksDecodedCounter).Add(1)
				c.col.GetCounter(integerBlocksSizeCounter).Add(int64(cur.entry.Size))
			}
			c.trbuf = cur.r.TombstoneRange(c.key, c.trbuf[:0])
			// Remove any tombstoned values
			excludeTombstonesIntegerArray(c.trbuf, v)

			// Remove values we already read
			v.Exclude(cur.readMin, cur.readMax)

			// If the block we decoded should have all of it's values included, mark it as read so we
			// don't use it again.
			if v.Len() > 0 {
				v.Include(minT, maxT)
				// Merge the remaining values with the existing
				v.Merge(values)
				*values = *v
			}
			cur.markRead(minT, maxT)
		}
	}

	first.markRead(minT, maxT)

	return values, err
}

func excludeTombstonesIntegerArray(t []TimeRange, values *cursors.IntegerArray) {
	for i := range t {
		values.Exclude(t[i].Min, t[i].Max)
	}
}

// ReadUnsignedArrayBlock reads the next block as a set of unsigned values.
func (c *KeyCursor) ReadUnsignedArrayBlock(values *cursors.UnsignedArray) (*cursors.UnsignedArray, error) {
LOOP:
	// No matching blocks to decode
	if len(c.current) == 0 {
		values.Timestamps = values.Timestamps[:0]
		values.Values = values.Values[:0]
		return values, nil
	}

	// First block is the oldest block containing the points we're searching for.
	first := c.current[0]
	err := first.r.ReadUnsignedArrayBlockAt(&first.entry, values)
	if err != nil {
		return nil, err
	}
	if c.col != nil {
		c.col.GetCounter(unsignedBlocksDecodedCounter).Add(1)
		c.col.GetCounter(unsignedBlocksSizeCounter).Add(int64(first.entry.Size))
	}

	// Remove values we already read
	values.Exclude(first.readMin, first.readMax)

	// Remove any tombstones
	c.trbuf = first.r.TombstoneRange(c.key, c.trbuf[:0])
	excludeTombstonesUnsignedArray(c.trbuf, values)
	// If there are no values in this first block (all tombstoned or previously read) and
	// we have more potential blocks too search.  Try again.
	if values.Len() == 0 && len(c.current) > 0 {
		c.current = c.current[1:]
		goto LOOP
	}

	// Only one block with this key and time range so return it
	if len(c.current) == 1 {
		if values.Len() > 0 {
			first.markRead(values.MinTime(), values.MaxTime())
		}
		return values, nil
	}

	// Use the current block time range as our overlapping window
	minT, maxT := first.readMin, first.readMax
	if values.Len() > 0 {
		minT, maxT = values.MinTime(), values.MaxTime()
	}
	if c.ascending {
		// Blocks are ordered by generation, we may have values in the past in later blocks, if so,
		// expand the window to include the min time range to ensure values are returned in ascending
		// order
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.MinTime < minT && !cur.read() {
				minT = cur.entry.MinTime
			}
		}

		// Find first block that overlaps our window
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.OverlapsTimeRange(minT, maxT) && !cur.read() {
				// Shrink our window so it's the intersection of the first overlapping block and the
				// first block.  We do this to minimize the region that overlaps and needs to
				// be merged.
				if cur.entry.MaxTime > maxT {
					maxT = cur.entry.MaxTime
				}
				values.Include(minT, maxT)
				break
			}
		}

		// Search the remaining blocks that overlap our window and append their values so we can
		// merge them.
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			// Skip this block if it doesn't contain points we looking for or they have already been read
			if !cur.entry.OverlapsTimeRange(minT, maxT) || cur.read() {
				cur.markRead(minT, maxT)
				continue
			}

			v := &cursors.UnsignedArray{}
			err := cur.r.ReadUnsignedArrayBlockAt(&cur.entry, v)
			if err != nil {
				return nil, err
			}
			if c.col != nil {
				c.col.GetCounter(unsignedBlocksDecodedCounter).Add(1)
				c.col.GetCounter(unsignedBlocksSizeCounter).Add(int64(cur.entry.Size))
			}

			c.trbuf = cur.r.TombstoneRange(c.key, c.trbuf[:0])
			// Remove any tombstoned values
			excludeTombstonesUnsignedArray(c.trbuf, v)

			// Remove values we already read
			v.Exclude(cur.readMin, cur.readMax)

			if v.Len() > 0 {
				// Only use values in the overlapping window
				v.Include(minT, maxT)
				// Merge the remaining values with the existing
				values.Merge(v)
			}
			cur.markRead(minT, maxT)
		}

	} else {
		// Blocks are ordered by generation, we may have values in the past in later blocks, if so,
		// expand the window to include the max time range to ensure values are returned in descending
		// order
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.MaxTime > maxT && !cur.read() {
				maxT = cur.entry.MaxTime
			}
		}

		// Find first block that overlaps our window
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.OverlapsTimeRange(minT, maxT) && !cur.read() {
				// Shrink our window so it's the intersection of the first overlapping block and the
				// first block.  We do this to minimize the region that overlaps and needs to
				// be merged.
				if cur.entry.MinTime < minT {
					minT = cur.entry.MinTime
				}
				values.Include(minT, maxT)
				break
			}
		}

		// Search the remaining blocks that overlap our window and append their values so we can
		// merge them.
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			// Skip this block if it doesn't contain points we looking for or they have already been read
			if !cur.entry.OverlapsTimeRange(minT, maxT) || cur.read() {
				cur.markRead(minT, maxT)
				continue
			}

			v := &cursors.UnsignedArray{}
			err := cur.r.ReadUnsignedArrayBlockAt(&cur.entry, v)
			if err != nil {
				return nil, err
			}
			if c.col != nil {
				c.col.GetCounter(unsignedBlocksDecodedCounter).Add(1)
				c.col.GetCounter(unsignedBlocksSizeCounter).Add(int64(cur.entry.Size))
			}
			c.trbuf = cur.r.TombstoneRange(c.key, c.trbuf[:0])
			// Remove any tombstoned values
			excludeTombstonesUnsignedArray(c.trbuf, v)

			// Remove values we already read
			v.Exclude(cur.readMin, cur.readMax)

			// If the block we decoded should have all of it's values included, mark it as read so we
			// don't use it again.
			if v.Len() > 0 {
				v.Include(minT, maxT)
				// Merge the remaining values with the existing
				v.Merge(values)
				*values = *v
			}
			cur.markRead(minT, maxT)
		}
	}

	first.markRead(minT, maxT)

	return values, err
}

func excludeTombstonesUnsignedArray(t []TimeRange, values *cursors.UnsignedArray) {
	for i := range t {
		values.Exclude(t[i].Min, t[i].Max)
	}
}

// ReadStringArrayBlock reads the next block as a set of string values.
func (c *KeyCursor) ReadStringArrayBlock(values *cursors.StringArray) (*cursors.StringArray, error) {
LOOP:
	// No matching blocks to decode
	if len(c.current) == 0 {
		values.Timestamps = values.Timestamps[:0]
		values.Values = values.Values[:0]
		return values, nil
	}

	// First block is the oldest block containing the points we're searching for.
	first := c.current[0]
	err := first.r.ReadStringArrayBlockAt(&first.entry, values)
	if err != nil {
		return nil, err
	}
	if c.col != nil {
		c.col.GetCounter(stringBlocksDecodedCounter).Add(1)
		c.col.GetCounter(stringBlocksSizeCounter).Add(int64(first.entry.Size))
	}

	// Remove values we already read
	values.Exclude(first.readMin, first.readMax)

	// Remove any tombstones
	c.trbuf = first.r.TombstoneRange(c.key, c.trbuf[:0])
	excludeTombstonesStringArray(c.trbuf, values)
	// If there are no values in this first block (all tombstoned or previously read) and
	// we have more potential blocks too search.  Try again.
	if values.Len() == 0 && len(c.current) > 0 {
		c.current = c.current[1:]
		goto LOOP
	}

	// Only one block with this key and time range so return it
	if len(c.current) == 1 {
		if values.Len() > 0 {
			first.markRead(values.MinTime(), values.MaxTime())
		}
		return values, nil
	}

	// Use the current block time range as our overlapping window
	minT, maxT := first.readMin, first.readMax
	if values.Len() > 0 {
		minT, maxT = values.MinTime(), values.MaxTime()
	}
	if c.ascending {
		// Blocks are ordered by generation, we may have values in the past in later blocks, if so,
		// expand the window to include the min time range to ensure values are returned in ascending
		// order
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.MinTime < minT && !cur.read() {
				minT = cur.entry.MinTime
			}
		}

		// Find first block that overlaps our window
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.OverlapsTimeRange(minT, maxT) && !cur.read() {
				// Shrink our window so it's the intersection of the first overlapping block and the
				// first block.  We do this to minimize the region that overlaps and needs to
				// be merged.
				if cur.entry.MaxTime > maxT {
					maxT = cur.entry.MaxTime
				}
				values.Include(minT, maxT)
				break
			}
		}

		// Search the remaining blocks that overlap our window and append their values so we can
		// merge them.
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			// Skip this block if it doesn't contain points we looking for or they have already been read
			if !cur.entry.OverlapsTimeRange(minT, maxT) || cur.read() {
				cur.markRead(minT, maxT)
				continue
			}

			v := &cursors.StringArray{}
			err := cur.r.ReadStringArrayBlockAt(&cur.entry, v)
			if err != nil {
				return nil, err
			}
			if c.col != nil {
				c.col.GetCounter(stringBlocksDecodedCounter).Add(1)
				c.col.GetCounter(stringBlocksSizeCounter).Add(int64(cur.entry.Size))
			}

			c.trbuf = cur.r.TombstoneRange(c.key, c.trbuf[:0])
			// Remove any tombstoned values
			excludeTombstonesStringArray(c.trbuf, v)

			// Remove values we already read
			v.Exclude(cur.readMin, cur.readMax)

			if v.Len() > 0 {
				// Only use values in the overlapping window
				v.Include(minT, maxT)
				// Merge the remaining values with the existing
				values.Merge(v)
			}
			cur.markRead(minT, maxT)
		}

	} else {
		// Blocks are ordered by generation, we may have values in the past in later blocks, if so,
		// expand the window to include the max time range to ensure values are returned in descending
		// order
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.MaxTime > maxT && !cur.read() {
				maxT = cur.entry.MaxTime
			}
		}

		// Find first block that overlaps our window
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.OverlapsTimeRange(minT, maxT) && !cur.read() {
				// Shrink our window so it's the intersection of the first overlapping block and the
				// first block.  We do this to minimize the region that overlaps and needs to
				// be merged.
				if cur.entry.MinTime < minT {
					minT = cur.entry.MinTime
				}
				values.Include(minT, maxT)
				break
			}
		}

		// Search the remaining blocks that overlap our window and append their values so we can
		// merge them.
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			// Skip this block if it doesn't contain points we looking for or they have already been read
			if !cur.entry.OverlapsTimeRange(minT, maxT) || cur.read() {
				cur.markRead(minT, maxT)
				continue
			}

			v := &cursors.StringArray{}
			err := cur.r.ReadStringArrayBlockAt(&cur.entry, v)
			if err != nil {
				return nil, err
			}
			if c.col != nil {
				c.col.GetCounter(stringBlocksDecodedCounter).Add(1)
				c.col.GetCounter(stringBlocksSizeCounter).Add(int64(cur.entry.Size))
			}
			c.trbuf = cur.r.TombstoneRange(c.key, c.trbuf[:0])
			// Remove any tombstoned values
			excludeTombstonesStringArray(c.trbuf, v)

			// Remove values we already read
			v.Exclude(cur.readMin, cur.readMax)

			// If the block we decoded should have all of it's values included, mark it as read so we
			// don't use it again.
			if v.Len() > 0 {
				v.Include(minT, maxT)
				// Merge the remaining values with the existing
				v.Merge(values)
				*values = *v
			}
			cur.markRead(minT, maxT)
		}
	}

	first.markRead(minT, maxT)

	return values, err
}

func excludeTombstonesStringArray(t []TimeRange, values *cursors.StringArray) {
	for i := range t {
		values.Exclude(t[i].Min, t[i].Max)
	}
}

// ReadBooleanArrayBlock reads the next block as a set of boolean values.
func (c *KeyCursor) ReadBooleanArrayBlock(values *cursors.BooleanArray) (*cursors.BooleanArray, error) {
LOOP:
	// No matching blocks to decode
	if len(c.current) == 0 {
		values.Timestamps = values.Timestamps[:0]
		values.Values = values.Values[:0]
		return values, nil
	}

	// First block is the oldest block containing the points we're searching for.
	first := c.current[0]
	err := first.r.ReadBooleanArrayBlockAt(&first.entry, values)
	if err != nil {
		return nil, err
	}
	if c.col != nil {
		c.col.GetCounter(booleanBlocksDecodedCounter).Add(1)
		c.col.GetCounter(booleanBlocksSizeCounter).Add(int64(first.entry.Size))
	}

	// Remove values we already read
	values.Exclude(first.readMin, first.readMax)

	// Remove any tombstones
	c.trbuf = first.r.TombstoneRange(c.key, c.trbuf[:0])
	excludeTombstonesBooleanArray(c.trbuf, values)
	// If there are no values in this first block (all tombstoned or previously read) and
	// we have more potential blocks too search.  Try again.
	if values.Len() == 0 && len(c.current) > 0 {
		c.current = c.current[1:]
		goto LOOP
	}

	// Only one block with this key and time range so return it
	if len(c.current) == 1 {
		if values.Len() > 0 {
			first.markRead(values.MinTime(), values.MaxTime())
		}
		return values, nil
	}

	// Use the current block time range as our overlapping window
	minT, maxT := first.readMin, first.readMax
	if values.Len() > 0 {
		minT, maxT = values.MinTime(), values.MaxTime()
	}
	if c.ascending {
		// Blocks are ordered by generation, we may have values in the past in later blocks, if so,
		// expand the window to include the min time range to ensure values are returned in ascending
		// order
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.MinTime < minT && !cur.read() {
				minT = cur.entry.MinTime
			}
		}

		// Find first block that overlaps our window
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.OverlapsTimeRange(minT, maxT) && !cur.read() {
				// Shrink our window so it's the intersection of the first overlapping block and the
				// first block.  We do this to minimize the region that overlaps and needs to
				// be merged.
				if cur.entry.MaxTime > maxT {
					maxT = cur.entry.MaxTime
				}
				values.Include(minT, maxT)
				break
			}
		}

		// Search the remaining blocks that overlap our window and append their values so we can
		// merge them.
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			// Skip this block if it doesn't contain points we looking for or they have already been read
			if !cur.entry.OverlapsTimeRange(minT, maxT) || cur.read() {
				cur.markRead(minT, maxT)
				continue
			}

			v := &cursors.BooleanArray{}
			err := cur.r.ReadBooleanArrayBlockAt(&cur.entry, v)
			if err != nil {
				return nil, err
			}
			if c.col != nil {
				c.col.GetCounter(booleanBlocksDecodedCounter).Add(1)
				c.col.GetCounter(booleanBlocksSizeCounter).Add(int64(cur.entry.Size))
			}

			c.trbuf = cur.r.TombstoneRange(c.key, c.trbuf[:0])
			// Remove any tombstoned values
			excludeTombstonesBooleanArray(c.trbuf, v)

			// Remove values we already read
			v.Exclude(cur.readMin, cur.readMax)

			if v.Len() > 0 {
				// Only use values in the overlapping window
				v.Include(minT, maxT)
				// Merge the remaining values with the existing
				values.Merge(v)
			}
			cur.markRead(minT, maxT)
		}

	} else {
		// Blocks are ordered by generation, we may have values in the past in later blocks, if so,
		// expand the window to include the max time range to ensure values are returned in descending
		// order
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.MaxTime > maxT && !cur.read() {
				maxT = cur.entry.MaxTime
			}
		}

		// Find first block that overlaps our window
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			if cur.entry.OverlapsTimeRange(minT, maxT) && !cur.read() {
				// Shrink our window so it's the intersection of the first overlapping block and the
				// first block.  We do this to minimize the region that overlaps and needs to
				// be merged.
				if cur.entry.MinTime < minT {
					minT = cur.entry.MinTime
				}
				values.Include(minT, maxT)
				break
			}
		}

		// Search the remaining blocks that overlap our window and append their values so we can
		// merge them.
		for i := 1; i < len(c.current); i++ {
			cur := c.current[i]
			// Skip this block if it doesn't contain points we looking for or they have already been read
			if !cur.entry.OverlapsTimeRange(minT, maxT) || cur.read() {
				cur.markRead(minT, maxT)
				continue
			}

			v := &cursors.BooleanArray{}
			err := cur.r.ReadBooleanArrayBlockAt(&cur.entry, v)
			if err != nil {
				return nil, err
			}
			if c.col != nil {
				c.col.GetCounter(booleanBlocksDecodedCounter).Add(1)
				c.col.GetCounter(booleanBlocksSizeCounter).Add(int64(cur.entry.Size))
			}
			c.trbuf = cur.r.TombstoneRange(c.key, c.trbuf[:0])
			// Remove any tombstoned values
			excludeTombstonesBooleanArray(c.trbuf, v)

			// Remove values we already read
			v.Exclude(cur.readMin, cur.readMax)

			// If the block we decoded should have all of it's values included, mark it as read so we
			// don't use it again.
			if v.Len() > 0 {
				v.Include(minT, maxT)
				// Merge the remaining values with the existing
				v.Merge(values)
				*values = *v
			}
			cur.markRead(minT, maxT)
		}
	}

	first.markRead(minT, maxT)

	return values, err
}

func excludeTombstonesBooleanArray(t []TimeRange, values *cursors.BooleanArray) {
	for i := range t {
		values.Exclude(t[i].Min, t[i].Max)
	}
}
