pkg/resultset/resultset.go (91 lines of code) (raw):
// Copyright 2022 Google LLC
//
// 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.
package resultset
import (
"context"
"io"
"sort"
"github.com/googlecloudplatform/pi-delivery/pkg/obj"
"github.com/googlecloudplatform/pi-delivery/pkg/ycd"
)
// ResultSet is a list of YCD files consisting the same pi calculation result.
type ResultSet []*ycd.YCDFile
var _ sort.Interface = new(ResultSet)
// Len is the number of elements in the collection.
func (s ResultSet) Len() int {
return len(s)
}
// Less reports whether the element with index i must sort before the element with index j.
func (s ResultSet) Less(i, j int) bool {
return s[i].Header.BlockID < s[j].Header.BlockID
}
// Swap swaps the elements with indexes i and j.
func (s ResultSet) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// NewReader returns a new ResultSetReader with bucket.
func (s ResultSet) NewReader(ctx context.Context, bucket obj.Bucket) *Reader {
return &Reader{
bucket: bucket,
set: s,
}
}
// TotalDigits returns the total number of digits in the array.
// y-cruncher doesn't seem to set TotalDigits unless a particular ycd file has
// a smaller number of digits smaller than the block size.
// If any file has the total digit field set, this simply returns the value of the field.
// Otherwise, it returns the sum of block sizes.
func (s ResultSet) TotalDigits() int64 {
total := int64(0)
for _, v := range s {
if v.Header.TotalDigits != 0 {
return v.Header.TotalDigits
}
total += v.Header.BlockSize
}
return total
}
// BlockSize returns the number of digits in each block.
func (s ResultSet) BlockSize() int64 {
if len(s) == 0 {
return 0
}
return s[0].Header.BlockSize
}
// BlockByteLength returns the byte length of each block.
func (s ResultSet) BlockByteLength() int64 {
if len(s) == 0 {
return 0
}
return s[0].BlockByteLength()
}
// OffSetToBlockPos converts the byte offset off to a set of blockID and blockOff.
func (s ResultSet) OffsetToBlockPos(off int64) (blockID, blockOff int64) {
if s.BlockSize() == 0 {
return 0, off
}
return off / s.BlockByteLength(), off % s.BlockByteLength()
}
// TotalByteLength returns the total byte length of the digits.
// It doesn't include headers.
func (s ResultSet) TotalByteLength() int64 {
if len(s) == 0 {
return 0
}
return s[0].BlockByteLength() * int64(len(s))
}
// DigitsPerWord returns the number of digits per word.
func (s ResultSet) DigitsPerWord() int {
if len(s) == 0 {
return 0
}
return ycd.DigitsPerWord(s.Radix())
}
// Radix returns the base of the result set.
func (s ResultSet) Radix() int {
if len(s) == 0 {
return 0
}
return s[0].Header.Radix
}
// FirstDigit returns the first digit of the result.
func (s ResultSet) FirstDigit() byte {
if len(s) == 0 {
return 0
}
return s[0].Header.FirstDigits[0]
}
// newRangeReader returns a io.ReadCloser for section [off, off+length) in the resultset.
func newRangeReader(ctx context.Context, set ResultSet, bucket obj.Bucket, off, length int64) (io.ReadCloser, error) {
if off >= set.TotalByteLength() {
return nil, io.EOF
}
block, blockOff := set.OffsetToBlockPos(off)
obj := bucket.Object(set[block].Name)
blockByteLen := set[block].BlockByteLength()
if length < 0 {
length = blockByteLen - blockOff
} else if blockByteLen < blockOff+length {
length = blockByteLen - blockOff
}
return obj.NewRangeReader(ctx, blockOff+int64(set[block].FirstDigitOffset), length)
}