// EncodeRing takes a Sector and encodes it as a Ring: a set of common.QuorumSize Segments that include redundancy. // The encoding parameters are stored in params. // k is the number of non-redundant segments, and b is the size of each segment. b is calculated from k. // The erasure-coding algorithm requires that the original data must be k*b in size, so it is padded here as needed. // // The return value is a Ring. // The first k Segments of the Ring are the original data split up. // The remaining Segments are newly generated redundant data. func EncodeRing(sec *common.Sector, params *common.EncodingParams) (ring [common.QuorumSize]common.Segment, err error) { k, b, length := params.GetValues() // check for legal size of k if k <= 0 || k >= common.QuorumSize { err = fmt.Errorf("k must be greater than 0 and smaller than %v", common.QuorumSize) return } // check for legal size of b if b < common.MinSegmentSize || b > common.MaxSegmentSize { err = fmt.Errorf("b must be greater than %v and smaller than %v", common.MinSegmentSize, common.MaxSegmentSize) return } // check for legal size of length if length != len(sec.Data) { err = fmt.Errorf("length mismatch: sector length %v != parameter length %v", len(sec.Data), length) return } else if length > common.MaxSegmentSize*common.QuorumSize { err = fmt.Errorf("length must be smaller than %v", common.MaxSegmentSize*common.QuorumSize) } // pad data as needed padding := k*b - len(sec.Data) paddedData := append(sec.Data, bytes.Repeat([]byte{0x00}, padding)...) // call the encoding function m := common.QuorumSize - k redundantChunk := C.encodeRedundancy(C.int(k), C.int(m), C.int(b), (*C.char)(unsafe.Pointer(&paddedData[0]))) redundantBytes := C.GoBytes(unsafe.Pointer(redundantChunk), C.int(m*b)) // split paddedData into ring for i := 0; i < k; i++ { ring[i] = common.Segment{ paddedData[i*b : (i+1)*b], uint8(i), } } // split redundantString into ring for i := k; i < common.QuorumSize; i++ { ring[i] = common.Segment{ redundantBytes[(i-k)*b : (i-k+1)*b], uint8(i), } } // free the memory allocated by the C file C.free(unsafe.Pointer(redundantChunk)) return }
// RebuildSector takes a Ring and returns a Sector containing the original data. // The encoding parameters are stored in params. // k must be equal to the number of non-redundant segments when the file was originally built. // Because recovery is just a bunch of matrix operations, there is no way to tell if the data has been corrupted // or if an incorrect value of k has been chosen. This error checking must happen before calling RebuildSector. // Each Segment's Data must have the correct Index from when it was encoded. func RebuildSector(ring []common.Segment, params *common.EncodingParams) (sec *common.Sector, err error) { k, b, length := params.GetValues() if k == 0 && b == 0 { err = fmt.Errorf("could not rebuild using uninitialized encoding parameters") return } // check for legal size of k if k > common.QuorumSize || k < 1 { err = fmt.Errorf("k must be greater than 0 but smaller than %v", common.QuorumSize) return } // check for legal size of b if b < common.MinSegmentSize || b > common.MaxSegmentSize { err = fmt.Errorf("b must be greater than %v and smaller than %v", common.MinSegmentSize, common.MaxSegmentSize) return } // check for legal size of length if length > common.MaxSegmentSize*common.QuorumSize { err = fmt.Errorf("length must be smaller than %v", common.MaxSegmentSize*common.QuorumSize) } // check for correct number of segments if len(ring) < k { err = fmt.Errorf("insufficient segments: expected at least %v, got %v", k, len(ring)) return } // move all data into a single slice var segmentData []byte var segmentIndices []uint8 for i := 0; i < k; i++ { // verify that each segment is the correct length // TODO: skip bad segments and continue rebuilding if possible if len(ring[i].Data) != b { err = fmt.Errorf("at least 1 Segment's Data field is the wrong length") return } segmentData = append(segmentData, ring[i].Data...) segmentIndices = append(segmentIndices, ring[i].Index) } // call the recovery function C.recoverData(C.int(k), C.int(common.QuorumSize-k), C.int(b), (*C.uchar)(unsafe.Pointer(&segmentData[0])), (*C.uchar)(unsafe.Pointer(&segmentIndices[0]))) // remove padding introduced by EncodeRing() sec, err = common.NewSector(segmentData[:length]) return }