blob: 84e13c827b74dcf92e1b19f4816a99f37641e74b [file] [log] [blame]
// Copyright 2014 Google Inc. All rights reserved.
//
// 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 fountain
import (
"math"
"math/rand"
"sort"
)
// The RU10 fountain is an unsystematic(*) fountain code which uses a degree
// distribution and intermediate block generation scheme similar to the
// R10 (Raptor) code. The unsystematic code has much lower independent block
// generation setup cost -- it requires only generating the auxiliary blocks,
// and does not require the full decode run of the algorithm to generate the
// intermediate encoding. This makes it cheaper in two ways for parallel
// execution. If all receivers can be expected to have the decoder in place,
// there is no decoding advantage to requiring the source blocks to appear in the
// code block set. But more to the point, if multiple encoders are working from
// the same source blocks, they can generate code blocks independently without
// having to worry if they are re-transmitting source blocks. Since the encoder
// state is cheaper and code blocks interchangeable, we sacrifice a little
// performance for that parallelizability and statelessness. We also gain a little
// in that this encoder variant needn't rely on the systematic number table
// of the R10 codec, and without that constraint, the number of possible code block
// ESI IDs is basically unlimited.
//
// (*) Well, not by design at least.
// This triple generator uses the Mersenne Twister to generate random seeds.
// k is the number of source symbols.
// x is the (random) code symbol ID.
// The generator creates values (d, a, b) to be used in constructing intermediate blocks.
func ru10TripleGenerator(k int, x int64) (int, uint32, uint32) {
l, _, _ := intermediateSymbols(k)
lprime := smallestPrimeGreaterOrEqual(l)
// TODO(gbillock): nudge x as a function of k to get better overhead-failure curve?
rand := rand.New(NewMersenneTwister64(x))
v := uint32(rand.Int63() % 1048576)
a := uint32(1 + (rand.Int63() % int64(lprime - 1)))
b := uint32(rand.Int63() % int64(lprime))
d := deg(v)
return d, a, b
}
// ru10Codec implements the Raptor-alike fountain code.
// Implements fountain.Codec.
type ru10Codec struct {
numSourceSymbols int
symbolAlignmentSize int
}
// NewRU10Codec creates an unsystematic raptor-like fountain codec which uses an
// intermediate block generation algorithm similar to the Raptor R10 codec.
func NewRU10Codec(numSourceSymbols int, symbolAlignmentSize int) Codec {
return &ru10Codec{
numSourceSymbols: numSourceSymbols,
symbolAlignmentSize: symbolAlignmentSize}
}
// SourceBlocks returns the number of source blocks the codec uses in the
// source message plus intermediate blocks added.
func (c *ru10Codec) SourceBlocks() int {
return c.numSourceSymbols
}
// PickIndices uses the R10 distribution function to pick indices. It gets
// numbers from the triple generator.
func (c *ru10Codec) PickIndices(codeBlockIndex int64) []int {
d, a, b := ru10TripleGenerator(c.numSourceSymbols, codeBlockIndex)
l, _, _ := intermediateSymbols(c.numSourceSymbols)
lprime := uint32(smallestPrimeGreaterOrEqual(l))
if d > l {
d = l
}
indices := make([]int, 0)
for b >= uint32(l) {
b = (b + a) % lprime
}
indices = append(indices, int(b))
for j := 1; j < d; j++ {
b = (b + a) % lprime
for b >= uint32(l) {
b = (b + a) % lprime
}
indices = append(indices, int(b))
}
sort.Ints(indices)
return indices
}
// RU10 intermediate encoding consists of the source symbols plus additional
// intermediate symbols consisting of exactly the S and H blocks the R10 code
// uses. The difference is that the code is unsystematic -- the source blocks
// aren't necessarily going to be represented at the output -- so when we do
// the decode we don't need to translate from the intermediate symbols back to
// the source symbols: the source symbols are just the first K intermediate symbols.
func (c *ru10Codec) GenerateIntermediateBlocks(message []byte, numBlocks int) []block {
sourceLong, sourceShort := partitionBytes(message, c.numSourceSymbols)
source := equalizeBlockLengths(sourceLong, sourceShort)
_, s, h := intermediateSymbols(c.numSourceSymbols)
k := c.numSourceSymbols
compositions := make([][]int, s)
for i := 0; i < k; i++ {
a := 1 + (int(math.Floor(float64(i)/float64(s))) % (s - 1))
b := i % s
compositions[b] = append(compositions[b], i)
b = (b + a) % s
compositions[b] = append(compositions[b], i)
b = (b + a) % s
compositions[b] = append(compositions[b], i)
}
for i := 0; i < s; i++ {
b := generateLubyTransformBlock(source, compositions[i])
source = append(source, b)
}
hprime := int(math.Ceil(float64(h) / 2))
m := buildGraySequence(k+s, hprime)
for i := 0; i < h; i++ {
hcomposition := make([]int, 0)
for j := 0; j < k+s; j++ {
if bitSet(uint(m[j]), uint(i)) {
hcomposition = append(hcomposition, j)
}
}
b := generateLubyTransformBlock(source, hcomposition)
source = append(source, b)
}
return source
}
// NewDecoder creates a new RU10 decoder
func (c *ru10Codec) NewDecoder(messageLength int) Decoder {
return newRU10Decoder(c, messageLength)
}
// ru10Decoder is the corresponding decoder for fountain codes using the RU10 encoder.
type ru10Decoder struct {
decoder *raptorDecoder
}
// newRU10Decoder creates a new raptor decoder for a given message. The
// codec supplied must be the same one as the message was encoded with.
func newRU10Decoder(c *ru10Codec, length int) *ru10Decoder {
return &ru10Decoder{
decoder: newRaptorDecoder(&raptorCodec{
SymbolAlignmentSize: c.symbolAlignmentSize,
NumSourceSymbols: c.numSourceSymbols},
length),
}
}
func (d *ru10Decoder) AddBlocks(blocks []LTBlock) bool {
c := ru10Codec{
symbolAlignmentSize: d.decoder.codec.SymbolAlignmentSize,
numSourceSymbols: d.decoder.codec.NumSourceSymbols}
for i := range blocks {
indices := c.PickIndices(blocks[i].BlockCode)
d.decoder.matrix.addEquation(indices, block{data: blocks[i].Data})
}
return d.decoder.matrix.determined()
}
func (d *ru10Decoder) Decode() []byte {
if !d.decoder.matrix.determined() {
return nil
}
d.decoder.matrix.reduce()
// Now the intermediate blocks are held in d.decoder.matrix.v. The source
// blocks are the first K intermediate blocks.
intermediate := d.decoder.matrix.v
lenLong, lenShort, numLong, numShort :=
partition(d.decoder.messageLength, d.decoder.codec.NumSourceSymbols)
out := make([]byte, d.decoder.messageLength)
out = out[0:0]
for i := 0; i < numLong; i++ {
out = append(out, intermediate[i].data[0:lenLong]...)
}
for i := numLong; i < numLong+numShort; i++ {
out = append(out, intermediate[i].data[0:lenShort]...)
}
return out
}