blob: e7a46d0a7aaa9367245c52e68fae1b7dcfc93e63 [file] [log] [blame]
// Copyright (C) 2015 The Android Open Source Project
//
// 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 store
import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"android.googlesource.com/platform/tools/gpu/binary"
"android.googlesource.com/platform/tools/gpu/binary/cyclic"
"android.googlesource.com/platform/tools/gpu/binary/vle"
"android.googlesource.com/platform/tools/gpu/config"
"android.googlesource.com/platform/tools/gpu/log"
)
type unboundedArchive struct {
worker chan<- func()
index *os.File
data *os.File
records map[binary.ID]span
}
// CreateUnboundedArchive returns a store that appends new resources, and never
// attempts to reclaim obsolete ones.
func CreateUnboundedArchive(path string) Store {
os.MkdirAll(path, 0755)
index, err := os.OpenFile(filepath.Join(path, "index"), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
panic(err)
}
data, err := os.OpenFile(filepath.Join(path, "data"), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
panic(err)
}
records := make(map[binary.ID]span)
// Use a buffered reader to quickly read the active records
reader := vle.Reader(bufio.NewReaderSize(index, 256<<10))
for {
var r record
if err := r.decode(cyclic.Decoder(reader)); err != io.EOF {
if err != nil {
panic(err)
}
records[r.id] = r.span
} else {
break
}
}
worker := make(chan func(), 64)
go func() {
for f := range worker {
f()
}
}()
return &unboundedArchive{
worker: worker,
index: index,
data: data,
records: records,
}
}
// Store compliance
func (s unboundedArchive) Store(id binary.ID, _ binary.Object, data []byte, logger log.Logger) (err error) {
if logger != nil {
logger = logger.Enter("unboundedArchive.Store")
if config.DebugDatabaseStores {
logger.Infof("id: %s size: %d", id, len(data))
}
}
s.worker <- func() {
offset, err := s.data.Seek(0, os.SEEK_END)
if err != nil {
panic(err)
}
n, err := s.data.Write(data)
if err != nil {
panic(err)
}
if n != len(data) {
panic(fmt.Errorf("Byte count written was not as expected. (%d/%d)", n, len(data)))
}
record := record{
id: id,
span: span{
offset: offset,
size: int64(len(data)),
},
}
e := cyclic.Encoder(vle.Writer(s.index))
record.encode(e)
s.records[id] = record.span
}
return nil
}
func (s unboundedArchive) Load(id binary.ID, logger log.Logger, out binary.Object) (size int, err error) {
if logger != nil {
logger = logger.Enter("unboundedArchive.Load")
if config.DebugDatabaseStores {
logger.Infof("Loading from unboundedArchive: %s", id)
}
defer func() {
if config.DebugDatabaseStores {
logger.Infof("↪ size: %d, err: %v", size, err)
}
if err := recover(); err != nil {
logger.Errorf("Panic when loading %v: %v", id, err)
panic(err)
}
}()
}
done := make(chan struct{})
s.worker <- func() {
defer close(done)
span, found := s.records[id]
if !found {
size, err = 0, fmt.Errorf("Resource %v not found", id)
return
}
data := make([]byte, span.size)
if n, e := s.data.ReadAt(data, span.offset); n != len(data) {
size, err = 0, e
return
}
d := cyclic.Decoder(vle.Reader(bytes.NewBuffer(data)))
size, err = len(data), d.Value(out)
return
}
<-done
return
}
func (s unboundedArchive) Contains(id binary.ID) (found bool) {
done := make(chan struct{})
s.worker <- func() {
_, found = s.records[id]
close(done)
}
<-done
return
}
func (s unboundedArchive) Close() {
done := make(chan struct{})
s.worker <- func() {
s.index.Close()
s.data.Close()
close(done)
close(s.worker)
}
<-done
}