wp
This commit is contained in:
@@ -3,7 +3,6 @@ package database
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
@@ -12,8 +11,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
bin "gordenko.dev/dima/bin/little"
|
||||
"gordenko.dev/dima/qb"
|
||||
"gordenko.dev/dima/qb/atree"
|
||||
"gordenko.dev/dima/qb/bin"
|
||||
"gordenko.dev/dima/qb/freelist"
|
||||
"gordenko.dev/dima/qb/txlog"
|
||||
)
|
||||
@@ -35,17 +35,18 @@ type Database struct {
|
||||
rLocksToRelease []uint32
|
||||
metrics map[uint32]*_metric
|
||||
//metricLockEntries map[uint32]*metricLockEntry
|
||||
dataFreeList *freelist.FreeList
|
||||
indexFreeList *freelist.FreeList
|
||||
dir string
|
||||
databaseName string
|
||||
txlog *txlog.Writer
|
||||
atree *atree.Atree
|
||||
tcpPort int
|
||||
logfile *os.File
|
||||
logger *log.Logger
|
||||
exitCh chan struct{}
|
||||
waitGroup *sync.WaitGroup
|
||||
dataFreeList *freelist.FreeList
|
||||
indexFreeList *freelist.FreeList
|
||||
dir string
|
||||
databaseName string
|
||||
snapshotNumber int
|
||||
txlog *txlog.Writer
|
||||
atree *atree.Atree
|
||||
tcpPort int
|
||||
logfile *os.File
|
||||
logger *log.Logger
|
||||
exitCh chan struct{}
|
||||
waitGroup *sync.WaitGroup
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
@@ -237,107 +238,9 @@ func (s *Database) recovery() {
|
||||
// }
|
||||
}
|
||||
|
||||
// func (s *Database) searchREDOFiles() ([]string, error) {
|
||||
// var (
|
||||
// reREDO = regexp.MustCompile(`a\d+\.redo`)
|
||||
// fileNames []string
|
||||
// )
|
||||
//
|
||||
|
||||
// entries, err := os.ReadDir(s.redoDir)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// for _, entry := range entries {
|
||||
// if entry.Type().IsRegular() {
|
||||
// baseName := entry.Name()
|
||||
// if reREDO.MatchString(baseName) {
|
||||
// fileNames = append(fileNames, filepath.Join(s.redoDir, baseName))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return fileNames, nil
|
||||
// }
|
||||
|
||||
// func (s *Database) replayREDOFile(fileName string) error {
|
||||
// redoFile, err := redo.ReadREDOFile(redo.ReadREDOFileReq{
|
||||
// FileName: fileName,
|
||||
// DataPageSize: atree.DataPageSize,
|
||||
// IndexPageSize: atree.IndexPageSize,
|
||||
// })
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("can't read REDO file %s: %s", fileName, err)
|
||||
// }
|
||||
|
||||
// metric, ok := s.metrics[redoFile.MetricID]
|
||||
// if !ok {
|
||||
// return fmt.Errorf("has REDOFile, metric %d not found", redoFile.MetricID)
|
||||
// }
|
||||
|
||||
// if metric.Until < redoFile.Timestamp {
|
||||
// waitCh := make(chan struct{})
|
||||
// s.atree.ApplyREDO(atree.WriteTask{
|
||||
// DataPage: redoFile.DataPage,
|
||||
// IndexPages: redoFile.IndexPages,
|
||||
// })
|
||||
// <-waitCh
|
||||
|
||||
// waitCh = s.txlog.WriteAppendedMeasureWithOverflow(
|
||||
// txlog.AppendedMeasureWithOverflow{
|
||||
// MetricID: redoFile.MetricID,
|
||||
// Timestamp: redoFile.Timestamp,
|
||||
// Value: redoFile.Value,
|
||||
// IsDataPageReused: redoFile.IsDataPageReused,
|
||||
// DataPageNo: redoFile.DataPage.PageNo,
|
||||
// IsRootChanged: redoFile.IsRootChanged,
|
||||
// RootPageNo: redoFile.RootPageNo,
|
||||
// ReusedIndexPages: redoFile.ReusedIndexPages,
|
||||
// },
|
||||
// fileName,
|
||||
// false,
|
||||
// )
|
||||
// <-waitCh
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (s *Database) verifySnapshot(fileName string) (_ bool, err error) {
|
||||
file, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if stat.Size() <= 4 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var (
|
||||
payloadSize = stat.Size() - 4
|
||||
hash = crc32.NewIEEE()
|
||||
)
|
||||
|
||||
_, err = io.CopyN(hash, file, payloadSize)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
calculatedCRC := hash.Sum32()
|
||||
|
||||
storedCRC, err := bin.ReadUint32(file)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if storedCRC != calculatedCRC {
|
||||
return false, fmt.Errorf("strored CRC %d not equal calculated CRC %d",
|
||||
storedCRC, calculatedCRC)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
// зробити object?
|
||||
|
||||
func (s *Database) replayChanges(fileName string) error {
|
||||
walReader, err := txlog.NewReader(txlog.ReaderOptions{
|
||||
@@ -348,120 +251,391 @@ func (s *Database) replayChanges(fileName string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var metrics map[uint32]*ReplayMetric
|
||||
|
||||
for {
|
||||
lsn, records, done, err := walReader.ReadPacket()
|
||||
records, isLast, err := walReader.NextPacket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = lsn
|
||||
|
||||
if done {
|
||||
return nil
|
||||
if isLast {
|
||||
if len(records) > 0 {
|
||||
var appendRecords []txlog.WALRecordAppendMeasures
|
||||
for _, untyped := range records {
|
||||
rec, ok := untyped.(txlog.WALRecordAppendMeasures)
|
||||
if ok {
|
||||
appendRecords = append(appendRecords, rec)
|
||||
}
|
||||
}
|
||||
// prepare pages and rewrite
|
||||
indexPages, dataPages := pagesToWriteFromWALRecords(metrics, appendRecords)
|
||||
|
||||
err = s.txlog.WritePagesToAtree(indexPages, dataPages)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// для останнього rec - повторюємо запис сторінок
|
||||
// rewrite write pages
|
||||
//s.txlog.
|
||||
//return nil
|
||||
}
|
||||
|
||||
for _, record := range records {
|
||||
if err = s.replayChangesRecord(record); err != nil {
|
||||
if err = s.replayWALRecord(record); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Database) replayChangesRecord(untyped any) error {
|
||||
// switch rec := untyped.(type) {
|
||||
// case txlog.AddedMetric:
|
||||
// var (
|
||||
// values qb.ValueCompressor
|
||||
// timestampsBuf = conbuf.New(nil)
|
||||
// valuesBuf = conbuf.New(nil)
|
||||
// )
|
||||
func (s *Database) replayWALRecord(untyped any) error {
|
||||
switch rec := untyped.(type) {
|
||||
case txlog.AddedMetric:
|
||||
s.addMetric(rec)
|
||||
|
||||
// if rec.MetricType == qb.Cumulative {
|
||||
// values = chunkenc.NewReverseCumulativeDeltaCompressor(
|
||||
// valuesBuf, 0, byte(rec.FracDigits))
|
||||
// } else {
|
||||
// values = chunkenc.NewReverseInstantDeltaCompressor(
|
||||
// valuesBuf, 0, byte(rec.FracDigits))
|
||||
case txlog.DeletedMetric:
|
||||
// pages add and delete to tmp file
|
||||
|
||||
// delete(s.metrics, rec.MetricID)
|
||||
// if len(rec.FreePageNumbers) > 0 {
|
||||
// s.freeList.AddPages(rec.FreePageNumbers)
|
||||
// }
|
||||
|
||||
// case txlog.AppendedMeasure:
|
||||
// metric, ok := s.metrics[rec.MetricID]
|
||||
// if ok {
|
||||
// metric.Timestamps.Append(rec.Timestamp)
|
||||
// metric.Values.Append(rec.Value)
|
||||
|
||||
// if metric.Since == 0 {
|
||||
// metric.Since = rec.Timestamp
|
||||
// metric.SinceValue = rec.Value
|
||||
// }
|
||||
|
||||
// s.metrics[rec.MetricID] = &_metric{
|
||||
// MetricType: rec.MetricType,
|
||||
// FracDigits: byte(rec.FracDigits),
|
||||
// TimestampsBuf: timestampsBuf,
|
||||
// ValuesBuf: valuesBuf,
|
||||
// Timestamps: chunkenc.NewReverseTimeDeltaCompressor(timestampsBuf, 0),
|
||||
// Values: values,
|
||||
// }
|
||||
// metric.Until = rec.Timestamp
|
||||
// metric.UntilValue = rec.Value
|
||||
// }
|
||||
|
||||
// case txlog.DeletedMetric:
|
||||
// delete(s.metrics, rec.MetricID)
|
||||
case txlog.WALRecordAppendMeasures:
|
||||
metric, ok := s.metrics[rec.MetricID]
|
||||
if !ok {
|
||||
qb.Abort(qb.MetricNotFoundDuringReplay, nil)
|
||||
}
|
||||
//metric.ReplayAppendedMeasures(rec) FIX
|
||||
|
||||
// case txlog.DeletedMeasures:
|
||||
// metric, ok := s.metrics[rec.MetricID]
|
||||
// if ok {
|
||||
// metric.DeleteMeasures()
|
||||
// if len(rec.FreePageNumbers) > 0 {
|
||||
// s.freeList.AddPages(rec.FreePageNumbers)
|
||||
// }
|
||||
// }
|
||||
|
||||
// case txlog.AppendedMeasure:
|
||||
default:
|
||||
qb.Abort(qb.UnknownTxLogRecordTypeBug,
|
||||
fmt.Errorf("bug: unknown record type %T in TransactionLog", rec))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ReplayMetric struct {
|
||||
metricType qb.MetricType
|
||||
fracDigits byte
|
||||
lastPageNo uint32
|
||||
buf []byte
|
||||
databuf []byte // якщо розмір buf == DataPageSize, то databuf буде менше (мінус футер)
|
||||
tSize int
|
||||
vSize int
|
||||
indexLevelTails []txlog.IndexLevelTail // root - last element
|
||||
}
|
||||
|
||||
func (s *ReplayMetric) ReadFrom(r io.Reader) (err error) {
|
||||
metricType, err := bin.ReadByte(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.metricType = qb.MetricType(metricType)
|
||||
s.fracDigits, err = bin.ReadByte(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.lastPageNo, err = bin.ReadUint32(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.tSize, err = bin.ReadUint16AsInt(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.vSize, err = bin.ReadUint16AsInt(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.buf, s.databuf = allocateBuffers(s.tSize + s.vSize)
|
||||
//
|
||||
err = bin.ReadNInto(r, s.databuf[len(s.databuf)-s.tSize:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = bin.ReadNInto(r, s.databuf[:s.vSize])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//
|
||||
levelsCount, err := bin.ReadVarSize(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for range levelsCount {
|
||||
var (
|
||||
buf = make([]byte, atree.IndexPageSize)
|
||||
count int
|
||||
)
|
||||
count, err = bin.ReadVarSize(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = bin.ReadNInto(r, buf[:count*indexRecordSize])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.indexLevelTails = append(s.indexLevelTails, txlog.IndexLevelTail{
|
||||
Buffer: buf,
|
||||
RecordsCount: count,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *ReplayMetric) ReplayAppendedMeasures(rec txlog.WALRecordAppendMeasures) {
|
||||
// Додати в index level tails недостаючі дані, або замінити
|
||||
for levelIdx, change := range rec.ChangedIndexLevels {
|
||||
if levelIdx < len(s.indexLevelTails) {
|
||||
level := s.indexLevelTails[levelIdx]
|
||||
if change.CompletedIndexPage != nil {
|
||||
// попередній хвіст індексного рівня перетворився на сторінку,
|
||||
// отже TailRecords - це новий хвіст
|
||||
// або копіюю дані, або алокую більший буфер?
|
||||
copy(level.Buffer, change.TailRecords)
|
||||
level.RecordsCount = len(change.TailRecords) / indexRecordSize
|
||||
} else {
|
||||
// TailRecords - це нові дані, які треба додати
|
||||
pos := level.RecordsCount * indexRecordSize
|
||||
copy(level.Buffer[pos:], change.TailRecords)
|
||||
}
|
||||
s.indexLevelTails[levelIdx] = level
|
||||
} else {
|
||||
var (
|
||||
recordsCount = len(change.TailRecords) / indexRecordSize
|
||||
buffer = make([]byte, atree.IndexPageSize)
|
||||
)
|
||||
copy(buffer, change.TailRecords)
|
||||
s.indexLevelTails = append(s.indexLevelTails, txlog.IndexLevelTail{
|
||||
Buffer: buffer,
|
||||
RecordsCount: recordsCount,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Перезаписати в buffer timestamps та values. Якщо розмір буферу недостатній -
|
||||
// спочатку створити більший
|
||||
|
||||
var (
|
||||
// CompletedDataPage є полюбому, оскільки це Record із мінімум одною заповненою
|
||||
// data сторінкою. Отже TailTimestamps і TailValues - це нові хвости data рівня.
|
||||
|
||||
requiredSpace = len(rec.TailTimestamps) + len(rec.TailValues)
|
||||
databuf []byte
|
||||
)
|
||||
if requiredSpace < len(s.buf) {
|
||||
databuf = s.buf
|
||||
// fix - update sizes?
|
||||
} else {
|
||||
var buf []byte
|
||||
buf, databuf = allocateBuffers(requiredSpace)
|
||||
copy(databuf, rec.TailValues)
|
||||
pos := len(databuf) - len(rec.TailTimestamps)
|
||||
copy(databuf[pos:], rec.TailTimestamps)
|
||||
// fix - replace and remeber sizes?
|
||||
|
||||
s.buf = buf
|
||||
}
|
||||
|
||||
//rec.TailTimestamps
|
||||
//rec.TailValues
|
||||
}
|
||||
|
||||
func (s *Database) rewritePagesFromLastWALPacket(list []any) error {
|
||||
// var (
|
||||
// indexPages []txlog.PageToWrite
|
||||
// dataPages []txlog.PageToWrite
|
||||
// )
|
||||
// for _, untyped := range list {
|
||||
// switch rec := untyped.(type) {
|
||||
// case txlog.WALRecordAppendMeasures:
|
||||
// metric, ok := s.metrics[rec.MetricID]
|
||||
// if ok {
|
||||
// metric.Timestamps.Append(rec.Timestamp)
|
||||
// metric.Values.Append(rec.Value)
|
||||
|
||||
// if metric.Since == 0 {
|
||||
// metric.Since = rec.Timestamp
|
||||
// metric.SinceValue = rec.Value
|
||||
// }
|
||||
|
||||
// metric.Until = rec.Timestamp
|
||||
// metric.UntilValue = rec.Value
|
||||
// }
|
||||
|
||||
// case txlog.AppendedMeasures:
|
||||
// metric, ok := s.metrics[rec.MetricID]
|
||||
// if ok {
|
||||
// for _, measure := range rec.Measures {
|
||||
// metric.Timestamps.Append(measure.Timestamp)
|
||||
// metric.Values.Append(measure.Value)
|
||||
|
||||
// if metric.Since == 0 {
|
||||
// metric.Since = measure.Timestamp
|
||||
// metric.SinceValue = measure.Value
|
||||
// }
|
||||
|
||||
// metric.Until = measure.Timestamp
|
||||
// metric.UntilValue = measure.Value
|
||||
// }
|
||||
// }
|
||||
|
||||
// // case txlog.AppendedMeasureWithOverflow:
|
||||
// // metric, ok := s.metrics[rec.MetricID]
|
||||
// // if ok {
|
||||
// // metric.ReinitBy(rec.Timestamp, rec.Value)
|
||||
// // if rec.IsRootChanged {
|
||||
// // metric.RootPageNo = rec.RootPageNo
|
||||
// // }
|
||||
// // metric.LastPageNo = rec.DataPageNo
|
||||
// // // delete free pages
|
||||
// // if rec.IsDataPageReused {
|
||||
// // s.freeList.DeleteReservedPages([]uint32{
|
||||
// // rec.DataPageNo,
|
||||
// // })
|
||||
// // }
|
||||
// // if len(rec.ReusedIndexPages) > 0 {
|
||||
// // s.freeList.DeleteReservedPages(rec.ReusedIndexPages)
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// case txlog.DeletedMeasures:
|
||||
// metric, ok := s.metrics[rec.MetricID]
|
||||
// if ok {
|
||||
// metric.DeleteMeasures()
|
||||
// if len(rec.FreePageNumbers) > 0 {
|
||||
// s.freeList.AddPages(rec.FreePageNumbers)
|
||||
// }
|
||||
// if !ok {
|
||||
// qb.Abort(qb.MetricNotFoundDuringReplay, nil)
|
||||
// }
|
||||
// //metric.ReplayAppendedMeasures(rec)
|
||||
|
||||
// default:
|
||||
// qb.Abort(qb.UnknownTxLogRecordTypeBug,
|
||||
// fmt.Errorf("bug: unknown record type %T in TransactionLog", rec))
|
||||
// }
|
||||
// }
|
||||
return nil
|
||||
}
|
||||
|
||||
func pagesToWriteFromWALRecords(metrics map[uint32]*ReplayMetric, records []txlog.WALRecordAppendMeasures) (indexPages []txlog.PageToWrite, dataPages []txlog.PageToWrite) {
|
||||
for _, rec := range records {
|
||||
metric, ok := metrics[rec.MetricID]
|
||||
if !ok {
|
||||
qb.Abort(qb.MetricNotFoundDuringReplay, nil)
|
||||
}
|
||||
p := rec.CompletedDataPage
|
||||
// буфер має бути розміру сторінки
|
||||
if len(metric.buf) != atree.DataPageSize {
|
||||
metric.buf, metric.databuf = growBuffers(growBuffersIn{
|
||||
databuf: metric.databuf,
|
||||
tSize: metric.tSize,
|
||||
vSize: metric.vSize,
|
||||
requiredSpace: atree.DataPageSize,
|
||||
})
|
||||
}
|
||||
// дописую дані по зміщенню
|
||||
pos := len(metric.databuf) - p.TimestampsOffset - len(p.Timestamps)
|
||||
copy(metric.databuf[pos:], p.Timestamps)
|
||||
copy(metric.databuf[p.ValuesOffset:], p.Values)
|
||||
//
|
||||
metric.tSize = p.TimestampsOffset + len(p.Timestamps)
|
||||
metric.tSize = p.ValuesOffset + len(p.Values)
|
||||
|
||||
// fix - заполнить страницу
|
||||
// fix - check checksum
|
||||
|
||||
dataPages = append(dataPages, txlog.PageToWrite{
|
||||
PageNo: p.PageNo,
|
||||
Content: metric.buf,
|
||||
})
|
||||
|
||||
for _, x := range rec.DataPages {
|
||||
dataPages = append(dataPages, txlog.PageToWrite{
|
||||
PageNo: x.PageNo,
|
||||
Content: x.Content,
|
||||
})
|
||||
}
|
||||
|
||||
for levelIdx, changedLevel := range rec.ChangedIndexLevels {
|
||||
p := changedLevel.CompletedIndexPage
|
||||
if p != nil {
|
||||
level := metric.indexLevelTails[levelIdx]
|
||||
copy(level.Buffer[level.RecordsCount*indexRecordSize:], p.Records)
|
||||
|
||||
// fix - заполнить страницу
|
||||
// fix - check checksum
|
||||
|
||||
indexPages = append(dataPages, txlog.PageToWrite{
|
||||
PageNo: p.PageNo,
|
||||
Content: level.Buffer,
|
||||
})
|
||||
}
|
||||
|
||||
for _, x := range changedLevel.IndexPages {
|
||||
indexPages = append(dataPages, txlog.PageToWrite{
|
||||
PageNo: x.PageNo,
|
||||
Content: x.Content,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FIX
|
||||
// УВАГА!
|
||||
// Якщо буфер metric.buffer < розміру сторінки - для timestamps доступний весь буфер.
|
||||
// Якщо буфер досяг розміру сторінки - в timestamps я передаю buffer[:DataPagePayloadSize]
|
||||
|
||||
var dataBufferSizes = []int{
|
||||
1024,
|
||||
2048,
|
||||
4096,
|
||||
8192,
|
||||
}
|
||||
|
||||
func calculateDataBufferSize(payloadSize int) int {
|
||||
for _, bufferSize := range dataBufferSizes {
|
||||
if payloadSize <= bufferSize {
|
||||
return bufferSize
|
||||
}
|
||||
}
|
||||
return atree.DataPageSize
|
||||
}
|
||||
|
||||
func allocateBuffers(requiredSpace int) (buf, databuf []byte) {
|
||||
bufferSize := calculateDataBufferSize(requiredSpace)
|
||||
buf = make([]byte, bufferSize)
|
||||
if bufferSize == atree.DataPageSize {
|
||||
databuf = buf[:atree.DataPagePayloadSize]
|
||||
} else {
|
||||
databuf = buf
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// src - databuf
|
||||
type growBuffersIn struct {
|
||||
databuf []byte
|
||||
tSize int
|
||||
vSize int
|
||||
requiredSpace int
|
||||
}
|
||||
|
||||
func growBuffers(in growBuffersIn) (buf, databuf []byte) {
|
||||
buf, databuf = allocateBuffers(in.requiredSpace)
|
||||
copy(databuf[len(databuf)-in.tSize:], in.databuf[len(in.databuf)-in.tSize:])
|
||||
copy(databuf, in.databuf[:in.vSize])
|
||||
return
|
||||
}
|
||||
|
||||
//func (s *Database) verifySnapshot(fileName string) (_ bool, err error) {
|
||||
// file, err := os.Open(fileName)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// defer file.Close()
|
||||
|
||||
// stat, err := file.Stat()
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
// if stat.Size() <= 4 {
|
||||
// return false, nil
|
||||
// }
|
||||
|
||||
// var (
|
||||
// payloadSize = stat.Size() - 4
|
||||
// hash = crc32.NewIEEE()
|
||||
// )
|
||||
|
||||
// _, err = io.CopyN(hash, file, payloadSize)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// calculatedCRC := hash.Sum32()
|
||||
|
||||
// storedCRC, err := bin.ReadUint32(file)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// if storedCRC != calculatedCRC {
|
||||
// return false, fmt.Errorf("strored CRC %d not equal calculated CRC %d",
|
||||
// storedCRC, calculatedCRC)
|
||||
// }
|
||||
// return true, nil
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user