diff --git a/internal/contentenc/offsets.go b/internal/contentenc/offsets.go index 7487baf..3a0abf3 100644 --- a/internal/contentenc/offsets.go +++ b/internal/contentenc/offsets.go @@ -76,20 +76,20 @@ func (be *ContentEnc) CipherSizeToPlainSize(cipherSize uint64) uint64 { return cipherSize - overhead } -// PlainSizeToCipherSize calculates the ciphertext size from a plaintext size +// PlainSizeToCipherSize calculates the ciphertext size from a plaintext size. func (be *ContentEnc) PlainSizeToCipherSize(plainSize uint64) uint64 { // Zero-sized files stay zero-sized if plainSize == 0 { return 0 } + return be.PlainOffToCipherOff(plainSize-1) + 1 +} - // Block number at last byte - blockNo := be.PlainOffToBlockNo(plainSize - 1) - blockCount := blockNo + 1 - - overhead := be.BlockOverhead()*blockCount + HeaderLen - - return plainSize + overhead +// PlainOffToCipherOff tells you the highest ciphertext offset that is +// *guaranteed* to be written/read when you write/read at `plainOff`. +func (be *ContentEnc) PlainOffToCipherOff(plainOff uint64) uint64 { + startOfBlock := be.BlockNoToCipherOff(be.PlainOffToBlockNo(plainOff)) + return startOfBlock + plainOff%be.PlainBS() + be.BlockOverhead() } // ExplodePlainRange splits a plaintext byte range into (possibly partial) blocks diff --git a/internal/contentenc/offsets_test.go b/internal/contentenc/offsets_test.go index 4bc3762..c3b8fcd 100644 --- a/internal/contentenc/offsets_test.go +++ b/internal/contentenc/offsets_test.go @@ -15,38 +15,39 @@ func TestSizeToSize(t *testing.T) { const rangeMax = 10000 - var c2p [rangeMax]uint64 - var p2c [rangeMax]uint64 + // y values in this order: + // 0 ... CipherSizeToPlainSize + // 1 ... PlainSizeToCipherSize + // 2 ... PlainOffToCipherOff + var yTable [rangeMax][3]uint64 // Calculate values - for i := range c2p { - c2p[i] = ce.CipherSizeToPlainSize(uint64(i)) - p2c[i] = ce.PlainSizeToCipherSize(uint64(i)) + for x := range yTable { + yTable[x][0] = ce.CipherSizeToPlainSize(uint64(x)) + yTable[x][1] = ce.PlainSizeToCipherSize(uint64(x)) + yTable[x][2] = ce.PlainOffToCipherOff(uint64(x)) } // Print data table - fmt.Print("x\tToPlainSize\tToCipherSize\n") - for i := range c2p { - if i > 1 && i < rangeMax-1 { + fmt.Print("x\tCipherSizeToPlainSize\tPlainSizeToCipherSize\tPlainOffToCipherOff\n") + for x := range yTable { + if x > 1 && x < rangeMax-1 { // If the point before has value-1 and the point after has value+1, // it is not interesting. Don't print it out. - if c2p[i] == c2p[i-1]+1 && p2c[i] == p2c[i-1]+1 && c2p[i+1] == c2p[i]+1 && p2c[i+1] == p2c[i]+1 { + interesting := false + for i := 0; i <= 2; i++ { + if yTable[x-1][i]+1 != yTable[x][i] && yTable[x][i]+1 != yTable[x+1][i]+1 { + interesting = true + } + // Monotonicity check + if yTable[x][i] < yTable[x-1][i] { + t.Errorf("column %d is non-monotonic!", i) + } + } + if !interesting { continue } } - fmt.Printf("%d\t%d\t%d\n", i, c2p[i], p2c[i]) - } - - // Monotonicity check - for i := range c2p { - if i < 1 { - continue - } - if c2p[i-1] > c2p[i] { - t.Errorf("error: c2p is non-monotonic: c2p[%d]=%d c2p[%d]=%d ", i-1, c2p[i-1], i, c2p[i]) - } - if p2c[i-1] > p2c[i] { - t.Errorf("error: p2c is non-monotonic: p2c[%d]=%d p2c[%d]=%d ", i-1, p2c[i-1], i, p2c[i]) - } + fmt.Printf("%d\t%d\t%d\t%d\n", x, yTable[x][0], yTable[x][1], yTable[x][2]) } }