rclone/vendor/github.com/vivint/infectious/fec.go
2020-05-12 15:56:50 +00:00

319 lines
8.1 KiB
Go

// (C) 1996-1998 Luigi Rizzo (luigi@iet.unipi.it)
// 2009-2010 Jack Lloyd (lloyd@randombit.net)
// 2011 Billy Brumley (billy.brumley@aalto.fi)
// 2016-2017 Vivint, Inc. (jeff.wendling@vivint.com)
//
// Portions derived from code by Phil Karn (karn@ka9q.ampr.org),
// Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari
// Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
package infectious
import "sort"
// FEC represents operations performed on a Reed-Solomon-based
// forward error correction code. Make sure to construct using NewFEC.
type FEC struct {
k int
n int
enc_matrix []byte
vand_matrix []byte
}
// NewFEC creates a *FEC using k required pieces and n total pieces.
// Encoding data with this *FEC will generate n pieces, and decoding
// data requires k uncorrupted pieces. If during decode more than k pieces
// exist, corrupted data can be detected and recovered from.
func NewFEC(k, n int) (*FEC, error) {
if k <= 0 || n <= 0 || k > 256 || n > 256 || k > n {
return nil, Error.New("requires 1 <= k <= n <= 256")
}
enc_matrix := make([]byte, n*k)
temp_matrix := make([]byte, n*k)
createInvertedVdm(temp_matrix, k)
for i := k * k; i < len(temp_matrix); i++ {
temp_matrix[i] = gf_exp[((i/k)*(i%k))%255]
}
for i := 0; i < k; i++ {
enc_matrix[i*(k+1)] = 1
}
for row := k * k; row < n*k; row += k {
for col := 0; col < k; col++ {
pa := temp_matrix[row:]
pb := temp_matrix[col:]
acc := byte(0)
for i := 0; i < k; i, pa, pb = i+1, pa[1:], pb[k:] {
acc ^= gf_mul_table[pa[0]][pb[0]]
}
enc_matrix[row+col] = acc
}
}
// vand_matrix has more columns than rows
// k rows, n columns.
vand_matrix := make([]byte, k*n)
vand_matrix[0] = 1
g := byte(1)
for row := 0; row < k; row++ {
a := byte(1)
for col := 1; col < n; col++ {
vand_matrix[row*n+col] = a // 2.pow(i * j) FIGURE IT OUT
a = gf_mul_table[g][a]
}
g = gf_mul_table[2][g]
}
return &FEC{
k: k,
n: n,
enc_matrix: enc_matrix,
vand_matrix: vand_matrix,
}, nil
}
// Required returns the number of required pieces for reconstruction. This is
// the k value passed to NewFEC.
func (f *FEC) Required() int {
return f.k
}
// Total returns the number of total pieces that will be generated during
// encoding. This is the n value passed to NewFEC.
func (f *FEC) Total() int {
return f.n
}
// Encode will take input data and encode to the total number of pieces n this
// *FEC is configured for. It will call the callback output n times.
//
// The input data must be a multiple of the required number of pieces k.
// Padding to this multiple is up to the caller.
//
// Note that the byte slices in Shares passed to output may be reused when
// output returns.
func (f *FEC) Encode(input []byte, output func(Share)) error {
size := len(input)
k := f.k
n := f.n
enc_matrix := f.enc_matrix
if size%k != 0 {
return Error.New("input length must be a multiple of %d", k)
}
block_size := size / k
for i := 0; i < k; i++ {
output(Share{
Number: i,
Data: input[i*block_size : i*block_size+block_size]})
}
fec_buf := make([]byte, block_size)
for i := k; i < n; i++ {
for j := range fec_buf {
fec_buf[j] = 0
}
for j := 0; j < k; j++ {
addmul(fec_buf, input[j*block_size:j*block_size+block_size],
enc_matrix[i*k+j])
}
output(Share{
Number: i,
Data: fec_buf})
}
return nil
}
// EncodeSingle will take input data and encode it to output only for the num
// piece.
//
// The input data must be a multiple of the required number of pieces k.
// Padding to this multiple is up to the caller.
//
// The output must be exactly len(input) / k bytes.
//
// The num must be 0 <= num < n.
func (f *FEC) EncodeSingle(input, output []byte, num int) error {
size := len(input)
k := f.k
n := f.n
enc_matrix := f.enc_matrix
if num < 0 {
return Error.New("num must be non-negative")
}
if num >= n {
return Error.New("num must be less than %d", n)
}
if size%k != 0 {
return Error.New("input length must be a multiple of %d", k)
}
block_size := size / k
if len(output) != block_size {
return Error.New("output length must be %d", block_size)
}
if num < k {
copy(output, input[num*block_size:])
return nil
}
for i := range output {
output[i] = 0
}
for i := 0; i < k; i++ {
addmul(output, input[i*block_size:i*block_size+block_size],
enc_matrix[num*k+i])
}
return nil
}
// A Share represents a piece of the FEC-encoded data.
// Both fields are required.
type Share struct {
Number int
Data []byte
}
// DeepCopy makes getting a deep copy of a Share easier. It will return an
// identical Share that uses all new memory locations.
func (s *Share) DeepCopy() (c Share) {
c.Number = s.Number
c.Data = append([]byte(nil), s.Data...)
return c
}
type byNumber []Share
func (b byNumber) Len() int { return len(b) }
func (b byNumber) Less(i int, j int) bool { return b[i].Number < b[j].Number }
func (b byNumber) Swap(i int, j int) { b[i], b[j] = b[j], b[i] }
// Rebuild will take a list of corrected shares (pieces) and a callback output.
// output will be called k times ((*FEC).Required() times) with 1/k of the
// original data each time and the index of that data piece.
// Decode is usually preferred.
//
// Note that the data is not necessarily sent to output ordered by the piece
// number.
//
// Note that the byte slices in Shares passed to output may be reused when
// output returns.
//
// Rebuild assumes that you have already called Correct or did not need to.
func (f *FEC) Rebuild(shares []Share, output func(Share)) error {
k := f.k
n := f.n
enc_matrix := f.enc_matrix
if len(shares) < k {
return NotEnoughShares.New("")
}
share_size := len(shares[0].Data)
sort.Sort(byNumber(shares))
m_dec := make([]byte, k*k)
indexes := make([]int, k)
sharesv := make([][]byte, k)
shares_b_iter := 0
shares_e_iter := len(shares) - 1
for i := 0; i < k; i++ {
var share_id int
var share_data []byte
if share := shares[shares_b_iter]; share.Number == i {
share_id = share.Number
share_data = share.Data
shares_b_iter++
} else {
share := shares[shares_e_iter]
share_id = share.Number
share_data = share.Data
shares_e_iter--
}
if share_id >= n {
return Error.New("invalid share id: %d", share_id)
}
if share_id < k {
m_dec[i*(k+1)] = 1
if output != nil {
output(Share{
Number: share_id,
Data: share_data})
}
} else {
copy(m_dec[i*k:i*k+k], enc_matrix[share_id*k:])
}
sharesv[i] = share_data
indexes[i] = share_id
}
if err := invertMatrix(m_dec, k); err != nil {
return err
}
buf := make([]byte, share_size)
for i := 0; i < len(indexes); i++ {
if indexes[i] >= k {
for j := range buf {
buf[j] = 0
}
for col := 0; col < k; col++ {
addmul(buf, sharesv[col], m_dec[i*k+col])
}
if output != nil {
output(Share{
Number: i,
Data: buf})
}
}
}
return nil
}