zfec: finish up some renaming of pyfec to zfec
authorzooko <zooko@zooko.com>
Thu, 19 Apr 2007 00:11:13 +0000 (05:41 +0530)
committerzooko <zooko@zooko.com>
Thu, 19 Apr 2007 00:11:13 +0000 (05:41 +0530)
darcs-hash:7f21826c8576fba7d680dfbcfd6e0a3625368734

zfec/README.txt
zfec/zfec/_fecmodule.c
zfec/zfec/test/bench_pyfec.py [deleted file]
zfec/zfec/test/test_pyfec.py [deleted file]
zfec/zfec/test/test_zfec.py [new file with mode: 0755]

index 70d1b5b40b3daed6a25fe0ebaaf87001e7101ac9..f7c76da47589d9149b8b20fe3316fb2f53c6f499 100644 (file)
@@ -19,7 +19,7 @@ which is a mature and optimized implementation of erasure coding.  The zfec
 package makes several changes from the original "fec" package, including
 addition of the Python API, refactoring of the C API to support zero-copy
 operation, a few clean-ups and micro-optimizations of the core code itself, 
-and the addition of a command-line tool named "fec".
+and the addition of a command-line tool named "zfec".
 
 
  * Community
@@ -52,7 +52,7 @@ and k is required to be at least 1 and at most m.
 degenerates to the equivalent of the Unix "split" utility which simply splits
 the input into successive segments.  Similarly, when k == 1 it degenerates to
 the equivalent of the unix "cp" utility -- each block is a complete copy of the
-input data.  The "fec" command-line tool does not implement these degenerate 
+input data.  The "zfec" command-line tool does not implement these degenerate 
 cases.)
 
 Note that each "primary block" is a segment of the original data, so its size
@@ -63,9 +63,9 @@ the same size as all the others).  In addition to the data contained in the
 blocks themselves there are also a few pieces of metadata which are necessary 
 for later reconstruction.  Those pieces are: 1.  the value of K, 2.  the value 
 of M,  3.  the sharenum of each block,  4.  the number of bytes of padding 
-that were used.  The "fec" command-line tool compresses these pieces of data 
+that were used.  The "zfec" command-line tool compresses these pieces of data 
 and prepends them to the beginning of each share, so each the sharefile 
-produced by the "fec" command-line tool is between one and four bytes larger 
+produced by the "zfec" command-line tool is between one and four bytes larger 
 than the share data alone.
 
 The decoding step requires as input k of the blocks which were produced by the
@@ -75,12 +75,12 @@ input to the encoding step.
 
  * Command-Line Tool
 
-The bin/ directory contains two Unix-style, command-line tools "fec" and 
-"unfec".  Execute "fec --help" or "unfec --help" for usage instructions.
+The bin/ directory contains two Unix-style, command-line tools "zfec" and 
+"zunfec".  Execute "zfec --help" or "zunfec --help" for usage instructions.
 
-Note: a Unix-style tool like "fec" does only one thing -- in this case
+Note: a Unix-style tool like "zfec" does only one thing -- in this case
 erasure coding -- and leaves other tasks to other tools.  Other Unix-style
-tools that go well with fec include "GNU tar" for archiving multiple files and
+tools that go well with zfec include "GNU tar" for archiving multiple files and
 directories into one file, "rzip" for compression, "GNU Privacy Guard" for
 encryption, and "sha256sum" for integrity.  It is important to do things in
 order: first archive, then compress, then either encrypt or sha256sum, then
@@ -90,10 +90,10 @@ also ensure integrity, so the use of sha256sum is unnecessary in that case.
 
  * Performance Measurements
 
-On my Athlon 64 2.4 GHz workstation (running Linux), the "fec" command-line
+On my Athlon 64 2.4 GHz workstation (running Linux), the "zfec" command-line
 tool encoded a 160 MB file with m=100, k=94 (about 6% redundancy) in 3.9
 seconds, where the "par2" tool encoded the file with about 6% redundancy in
-27 seconds.  "fec" encoded the same file with m=12, k=6 (100% redundancy) in
+27 seconds.  zfec encoded the same file with m=12, k=6 (100% redundancy) in
 4.1 seconds, where par2 encoded it with about 100% redundancy in 7 minutes
 and 56 seconds.
 
@@ -182,8 +182,8 @@ objects (e.g. Python strings) to hold the data that you pass to zfec.
  * Utilities
 
 The filefec.py module has a utility function for efficiently reading a file
-and encoding it piece by piece.  This module is used by the "fec" and "unfec"
-command-line tools from the bin/ directory.
+and encoding it piece by piece.  This module is used by the "zfec" and 
+"zunfec" command-line tools from the bin/ directory.
 
 
  * Dependencies
index c437e8be79de244806b282c47a713871f05a87fe..01e57c5e88103a52ebe166fd6c5404ab31781656 100644 (file)
@@ -277,7 +277,7 @@ static PyMemberDef Encoder_members[] = {
 static PyTypeObject Encoder_type = {
     PyObject_HEAD_INIT(NULL)
     0,                         /*ob_size*/
-    "fec.Encoder", /*tp_name*/
+    "_fec.Encoder", /*tp_name*/
     sizeof(Encoder),             /*tp_basicsize*/
     0,                         /*tp_itemsize*/
     (destructor)Encoder_dealloc, /*tp_dealloc*/
@@ -541,7 +541,7 @@ static PyMemberDef Decoder_members[] = {
 static PyTypeObject Decoder_type = {
     PyObject_HEAD_INIT(NULL)
     0,                         /*ob_size*/
-    "fec.Decoder", /*tp_name*/
+    "_fec.Decoder", /*tp_name*/
     sizeof(Decoder),             /*tp_basicsize*/
     0,                         /*tp_itemsize*/
     (destructor)Decoder_dealloc, /*tp_dealloc*/
diff --git a/zfec/zfec/test/bench_pyfec.py b/zfec/zfec/test/bench_pyfec.py
deleted file mode 100644 (file)
index 6b86e8c..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-# zfec -- fast forward error correction library with Python interface
-# 
-# Copyright (C) 2007 Allmydata, Inc.
-# Author: Zooko Wilcox-O'Hearn
-# mailto:zooko@zooko.com
-# 
-# This file is part of zfec.
-# 
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.  This program also comes with the added permission that, in the case
-# that you are obligated to release a derived work under this licence (as per
-# section 2.b of the GPL), you may delay the fulfillment of this obligation for
-# up to 12 months.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-
-import fec
-
-import array, random
-
-def f_easyfec(filesize):
-    return bench_encode_to_files_shuffle_decode_from_files(filesize, verbose=False, encodefunc=fec.filefec.encode_to_files_easyfec)
-    
-def f_fec_stringy(filesize):
-    return bench_encode_to_files_shuffle_decode_from_files(filesize, verbose=False, encodefunc=fec.filefec.encode_to_files_stringy)
-    
-def f_fec(filesize):
-    return bench_encode_to_files_shuffle_decode_from_files(filesize, verbose=False, encodefunc=fec.filefec.encode_to_files)
-    
-def bench_encode_to_files_shuffle_decode_from_files(filesize=1000000, verbose=False, encodefunc=fec.filefec.encode_to_files):
-    CHUNKSIZE=4096
-    PREFIX="testshare"
-    K=25
-    M=100
-    import os, time
-    left=filesize
-    outfile = open("tmpranddata", "wb")
-    try:
-        while left:
-            d = os.urandom(min(left, CHUNKSIZE))
-            outfile.write(d)
-            left -= len(d)
-        outfile.flush()
-        outfile = None
-        infile = open("tmpranddata", "rb")
-        st = time.time()
-        encodefunc(infile, PREFIX, K, M)
-        so = time.time()
-        if verbose:
-            print "Encoded %s byte file into %d share files in %0.2f seconds, or %0.2f million bytes per second" % (filesize, M, so-st, filesize/((so-st)*filesize),)
-        enctime = so-st
-        # Now delete m-k of the tempfiles at random.
-        tempfs = [ f for f in os.listdir(".") if f.startswith(PREFIX) ]
-        random.shuffle(tempfs)
-        for victimtempf in tempfs[:M-K]:
-            os.remove(victimtempf)
-        recoveredfile = open("tmpranddata-recovered", "wb")
-        st = time.time()
-        fec.filefec.decode_from_files(recoveredfile, filesize, PREFIX, K, M)
-        so = time.time()
-        if verbose:
-            print "Decoded %s byte file from %d share files in %0.2f seconds, or %0.2f million bytes per second" % (filesize, K, so-st, filesize/((so-st)*filesize),)
-        return enctime + (so-st)
-    finally:
-        # os.remove("tmpranddata")
-        pass
-
-def bench_read_encode_and_drop():
-    FILESIZE=1000000
-    CHUNKSIZE=4096
-    import os, time
-    left=FILESIZE
-    outfile = open("tmpranddata", "wb")
-    try:
-        while left:
-            d = os.urandom(min(left, CHUNKSIZE))
-            outfile.write(d)
-            left -= len(d)
-        outfile.flush()
-        outfile = None
-        infile = open("tmpranddata", "rb")
-        def cb(s, l):
-            pass
-        st = time.time()
-        fec.filefec.encode_file(infile, cb, 25, 100, 4096)
-        so = time.time()
-        print "Encoded %s byte file in %0.2f seconds, or %0.2f million bytes per second" % (FILESIZE, so-st, FILESIZE/((so-st)*1000000),)
-        return so-st
-    finally:
-        os.remove("tmpranddata")
-
-if __name__ == "__main__":
-    bench_encode_to_files_shuffle_decode_from_files()
-
diff --git a/zfec/zfec/test/test_pyfec.py b/zfec/zfec/test/test_pyfec.py
deleted file mode 100755 (executable)
index e080e83..0000000
+++ /dev/null
@@ -1,218 +0,0 @@
-#!/usr/bin/env python
-
-# import bindann
-# import bindann.monkeypatch.all
-
-# zfec -- fast forward error correction library with Python interface
-#
-# Copyright (C) 2007 Allmydata, Inc.
-# Author: Zooko Wilcox-O'Hearn
-# mailto:zooko@zooko.com
-#
-# This file is part of zfec.
-#
-# This program is free software; you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation; either version 2 of the License, or (at your option) any later
-# version.  This program also comes with the added permission that, in the case
-# that you are obligated to release a derived work under this licence (as per
-# section 2.b of the GPL), you may delay the fulfillment of this obligation for
-# up to 12 months.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-
-import cStringIO, os, random, re, sys
-
-import zfec
-
-try:
-    from twisted.trial import unittest
-except ImportError:
-    # trial is unavailable, oh well
-    import unittest
-
-global VERBOSE
-VERBOSE=False
-if '-v' in sys.argv:
-    sys.argv.pop(sys.argv.index('-v'))
-    VERBOSE=True
-
-from base64 import b32encode
-def ab(x): # debuggery
-    if len(x) >= 3:
-        return "%s:%s" % (len(x), b32encode(x[-3:]),)
-    elif len(x) == 2:
-        return "%s:%s" % (len(x), b32encode(x[-2:]),)
-    elif len(x) == 1:
-        return "%s:%s" % (len(x), b32encode(x[-1:]),)
-    elif len(x) == 0:
-        return "%s:%s" % (len(x), "--empty--",)
-
-def _h(k, m, ss):
-    encer = zfec.Encoder(k, m)
-    nums_and_blocks = list(enumerate(encer.encode(ss)))
-    assert isinstance(nums_and_blocks, list), nums_and_blocks
-    assert len(nums_and_blocks) == m, (len(nums_and_blocks), m,)
-    nums_and_blocks = random.sample(nums_and_blocks, k)
-    blocks = [ x[1] for x in nums_and_blocks ]
-    nums = [ x[0] for x in nums_and_blocks ]
-    decer = zfec.Decoder(k, m)
-    decoded = decer.decode(blocks, nums)
-    assert len(decoded) == len(ss), (len(decoded), len(ss),)
-    assert tuple([str(s) for s in decoded]) == tuple([str(s) for s in ss]), (tuple([ab(str(s)) for s in decoded]), tuple([ab(str(s)) for s in ss]),)
-
-def randstr(n):
-    return ''.join(map(chr, map(random.randrange, [0]*n, [256]*n)))
-
-def _help_test_random():
-    m = random.randrange(1, 257)
-    k = random.randrange(1, m+1)
-    l = random.randrange(0, 2**10)
-    ss = [ randstr(l/k) for x in range(k) ]
-    _h(k, m, ss)
-
-def _help_test_random_with_l(l):
-    m = 83
-    k = 19
-    ss = [ randstr(l/k) for x in range(k) ]
-    _h(k, m, ss)
-
-class ZFec(unittest.TestCase):
-    def test_random(self):
-        for i in range(3):
-            _help_test_random()
-        if VERBOSE:
-            print "%d randomized tests pass." % (i+1)
-
-    def test_bad_args_enc(self):
-        encer = zfec.Encoder(2, 4)
-        try:
-            encer.encode(["a", "b", ], ["c", "I am not an integer blocknum",])
-        except zfec.Error, e:
-            assert "Precondition violation: second argument is required to contain int" in str(e), e
-        else:
-            raise "Should have gotten zfec.Error for wrong type of second argument."
-
-        try:
-            encer.encode(["a", "b", ], 98) # not a sequence at all
-        except TypeError, e:
-            assert "Second argument (optional) was not a sequence" in str(e), e
-        else:
-            raise "Should have gotten TypeError for wrong type of second argument."
-
-    def test_bad_args_dec(self):
-        decer = zfec.Decoder(2, 4)
-
-        try:
-            decer.decode(98, [0, 1]) # first argument is not a sequence
-        except TypeError, e:
-            assert "First argument was not a sequence" in str(e), e
-        else:
-            raise "Should have gotten TypeError for wrong type of second argument."
-
-        try:
-            decer.decode(["a", "b", ], ["c", "d",])
-        except zfec.Error, e:
-            assert "Precondition violation: second argument is required to contain int" in str(e), e
-        else:
-            raise "Should have gotten zfec.Error for wrong type of second argument."
-
-        try:
-            decer.decode(["a", "b", ], 98) # not a sequence at all
-        except TypeError, e:
-            assert "Second argument was not a sequence" in str(e), e
-        else:
-            raise "Should have gotten TypeError for wrong type of second argument."
-
-class FileFec(unittest.TestCase):
-    def test_filefec_header(self):
-        for m in [3, 5, 7, 9, 11, 17, 19, 33, 35, 65, 66, 67, 129, 130, 131, 254, 255, 256,]:
-            for k in [2, 3, 5, 9, 17, 33, 65, 129, 255,]:
-                if k >= m:
-                    continue
-                for pad in [0, 1, k-1,]:
-                    if pad >= k:
-                        continue
-                    for sh in [0, 1, m-1,]:
-                        if sh >= m:
-                            continue
-                        h = zfec.filefec._build_header(m, k, pad, sh)
-                        hio = cStringIO.StringIO(h)
-                        (rm, rk, rpad, rsh,) = zfec.filefec._parse_header(hio)
-                        assert (rm, rk, rpad, rsh,) == (m, k, pad, sh,), h
-
-    def _help_test_filefec(self, teststr, k, m, numshs=None):
-        if numshs == None:
-            numshs = m
-
-        TESTFNAME = "testfile.txt"
-        PREFIX = "test"
-        SUFFIX = ".fec"
-
-        tempdir = zfec.util.fileutil.NamedTemporaryDirectory(cleanup=False)
-        try:
-            tempfn = os.path.join(tempdir.name, TESTFNAME)
-            tempf = open(tempfn, 'wb')
-            tempf.write(teststr)
-            tempf.close()
-            fsize = os.path.getsize(tempfn)
-            assert fsize == len(teststr)
-
-            # encode the file
-            zfec.filefec.encode_to_files(open(tempfn, 'rb'), fsize, tempdir.name, PREFIX, k, m, SUFFIX, verbose=VERBOSE)
-
-            # select some share files
-            RE=re.compile(zfec.filefec.RE_FORMAT % (PREFIX, SUFFIX,))
-            fns = os.listdir(tempdir.name)
-            sharefs = [ open(os.path.join(tempdir.name, fn), "rb") for fn in fns if RE.match(fn) ]
-            random.shuffle(sharefs)
-            del sharefs[numshs:]
-
-            # decode from the share files
-            outf = open(os.path.join(tempdir.name, 'recovered-testfile.txt'), 'wb')
-            zfec.filefec.decode_from_files(outf, sharefs, verbose=VERBOSE)
-            outf.close()
-
-            tempfn = open(os.path.join(tempdir.name, 'recovered-testfile.txt'), 'rb')
-            recovereddata = tempfn.read()
-            assert recovereddata == teststr
-        finally:
-            tempdir.shutdown()
-
-    def test_filefec_all_shares(self):
-        return self._help_test_filefec("Yellow Whirled!", 3, 8)
-
-    def test_filefec_all_shares_with_padding(self, noisy=VERBOSE):
-        return self._help_test_filefec("Yellow Whirled!A", 3, 8)
-
-    def test_filefec_min_shares_with_padding(self, noisy=VERBOSE):
-        return self._help_test_filefec("Yellow Whirled!A", 3, 8, numshs=3)
-
-if __name__ == "__main__":
-    if hasattr(unittest, 'main'):
-        unittest.main()
-    else:
-        sys.path.append(os.getcwd())
-        mods = []
-        fullname = os.path.realpath(os.path.abspath(__file__))
-        for pathel in sys.path:
-            fullnameofpathel = os.path.realpath(os.path.abspath(pathel))
-            if fullname.startswith(fullnameofpathel):
-                relname = fullname[len(fullnameofpathel):]
-                mod = (os.path.splitext(relname)[0]).replace(os.sep, '.').strip('.')
-                mods.append(mod)
-
-        mods.sort(cmp=lambda x, y: cmp(len(x), len(y)))
-        mods.reverse()
-        for mod in mods:
-            cmdstr = "trial %s %s" % (' '.join(sys.argv[1:]), mod)
-            print cmdstr
-            if os.system(cmdstr) == 0:
-                break
diff --git a/zfec/zfec/test/test_zfec.py b/zfec/zfec/test/test_zfec.py
new file mode 100755 (executable)
index 0000000..e080e83
--- /dev/null
@@ -0,0 +1,218 @@
+#!/usr/bin/env python
+
+# import bindann
+# import bindann.monkeypatch.all
+
+# zfec -- fast forward error correction library with Python interface
+#
+# Copyright (C) 2007 Allmydata, Inc.
+# Author: Zooko Wilcox-O'Hearn
+# mailto:zooko@zooko.com
+#
+# This file is part of zfec.
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.  This program also comes with the added permission that, in the case
+# that you are obligated to release a derived work under this licence (as per
+# section 2.b of the GPL), you may delay the fulfillment of this obligation for
+# up to 12 months.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+import cStringIO, os, random, re, sys
+
+import zfec
+
+try:
+    from twisted.trial import unittest
+except ImportError:
+    # trial is unavailable, oh well
+    import unittest
+
+global VERBOSE
+VERBOSE=False
+if '-v' in sys.argv:
+    sys.argv.pop(sys.argv.index('-v'))
+    VERBOSE=True
+
+from base64 import b32encode
+def ab(x): # debuggery
+    if len(x) >= 3:
+        return "%s:%s" % (len(x), b32encode(x[-3:]),)
+    elif len(x) == 2:
+        return "%s:%s" % (len(x), b32encode(x[-2:]),)
+    elif len(x) == 1:
+        return "%s:%s" % (len(x), b32encode(x[-1:]),)
+    elif len(x) == 0:
+        return "%s:%s" % (len(x), "--empty--",)
+
+def _h(k, m, ss):
+    encer = zfec.Encoder(k, m)
+    nums_and_blocks = list(enumerate(encer.encode(ss)))
+    assert isinstance(nums_and_blocks, list), nums_and_blocks
+    assert len(nums_and_blocks) == m, (len(nums_and_blocks), m,)
+    nums_and_blocks = random.sample(nums_and_blocks, k)
+    blocks = [ x[1] for x in nums_and_blocks ]
+    nums = [ x[0] for x in nums_and_blocks ]
+    decer = zfec.Decoder(k, m)
+    decoded = decer.decode(blocks, nums)
+    assert len(decoded) == len(ss), (len(decoded), len(ss),)
+    assert tuple([str(s) for s in decoded]) == tuple([str(s) for s in ss]), (tuple([ab(str(s)) for s in decoded]), tuple([ab(str(s)) for s in ss]),)
+
+def randstr(n):
+    return ''.join(map(chr, map(random.randrange, [0]*n, [256]*n)))
+
+def _help_test_random():
+    m = random.randrange(1, 257)
+    k = random.randrange(1, m+1)
+    l = random.randrange(0, 2**10)
+    ss = [ randstr(l/k) for x in range(k) ]
+    _h(k, m, ss)
+
+def _help_test_random_with_l(l):
+    m = 83
+    k = 19
+    ss = [ randstr(l/k) for x in range(k) ]
+    _h(k, m, ss)
+
+class ZFec(unittest.TestCase):
+    def test_random(self):
+        for i in range(3):
+            _help_test_random()
+        if VERBOSE:
+            print "%d randomized tests pass." % (i+1)
+
+    def test_bad_args_enc(self):
+        encer = zfec.Encoder(2, 4)
+        try:
+            encer.encode(["a", "b", ], ["c", "I am not an integer blocknum",])
+        except zfec.Error, e:
+            assert "Precondition violation: second argument is required to contain int" in str(e), e
+        else:
+            raise "Should have gotten zfec.Error for wrong type of second argument."
+
+        try:
+            encer.encode(["a", "b", ], 98) # not a sequence at all
+        except TypeError, e:
+            assert "Second argument (optional) was not a sequence" in str(e), e
+        else:
+            raise "Should have gotten TypeError for wrong type of second argument."
+
+    def test_bad_args_dec(self):
+        decer = zfec.Decoder(2, 4)
+
+        try:
+            decer.decode(98, [0, 1]) # first argument is not a sequence
+        except TypeError, e:
+            assert "First argument was not a sequence" in str(e), e
+        else:
+            raise "Should have gotten TypeError for wrong type of second argument."
+
+        try:
+            decer.decode(["a", "b", ], ["c", "d",])
+        except zfec.Error, e:
+            assert "Precondition violation: second argument is required to contain int" in str(e), e
+        else:
+            raise "Should have gotten zfec.Error for wrong type of second argument."
+
+        try:
+            decer.decode(["a", "b", ], 98) # not a sequence at all
+        except TypeError, e:
+            assert "Second argument was not a sequence" in str(e), e
+        else:
+            raise "Should have gotten TypeError for wrong type of second argument."
+
+class FileFec(unittest.TestCase):
+    def test_filefec_header(self):
+        for m in [3, 5, 7, 9, 11, 17, 19, 33, 35, 65, 66, 67, 129, 130, 131, 254, 255, 256,]:
+            for k in [2, 3, 5, 9, 17, 33, 65, 129, 255,]:
+                if k >= m:
+                    continue
+                for pad in [0, 1, k-1,]:
+                    if pad >= k:
+                        continue
+                    for sh in [0, 1, m-1,]:
+                        if sh >= m:
+                            continue
+                        h = zfec.filefec._build_header(m, k, pad, sh)
+                        hio = cStringIO.StringIO(h)
+                        (rm, rk, rpad, rsh,) = zfec.filefec._parse_header(hio)
+                        assert (rm, rk, rpad, rsh,) == (m, k, pad, sh,), h
+
+    def _help_test_filefec(self, teststr, k, m, numshs=None):
+        if numshs == None:
+            numshs = m
+
+        TESTFNAME = "testfile.txt"
+        PREFIX = "test"
+        SUFFIX = ".fec"
+
+        tempdir = zfec.util.fileutil.NamedTemporaryDirectory(cleanup=False)
+        try:
+            tempfn = os.path.join(tempdir.name, TESTFNAME)
+            tempf = open(tempfn, 'wb')
+            tempf.write(teststr)
+            tempf.close()
+            fsize = os.path.getsize(tempfn)
+            assert fsize == len(teststr)
+
+            # encode the file
+            zfec.filefec.encode_to_files(open(tempfn, 'rb'), fsize, tempdir.name, PREFIX, k, m, SUFFIX, verbose=VERBOSE)
+
+            # select some share files
+            RE=re.compile(zfec.filefec.RE_FORMAT % (PREFIX, SUFFIX,))
+            fns = os.listdir(tempdir.name)
+            sharefs = [ open(os.path.join(tempdir.name, fn), "rb") for fn in fns if RE.match(fn) ]
+            random.shuffle(sharefs)
+            del sharefs[numshs:]
+
+            # decode from the share files
+            outf = open(os.path.join(tempdir.name, 'recovered-testfile.txt'), 'wb')
+            zfec.filefec.decode_from_files(outf, sharefs, verbose=VERBOSE)
+            outf.close()
+
+            tempfn = open(os.path.join(tempdir.name, 'recovered-testfile.txt'), 'rb')
+            recovereddata = tempfn.read()
+            assert recovereddata == teststr
+        finally:
+            tempdir.shutdown()
+
+    def test_filefec_all_shares(self):
+        return self._help_test_filefec("Yellow Whirled!", 3, 8)
+
+    def test_filefec_all_shares_with_padding(self, noisy=VERBOSE):
+        return self._help_test_filefec("Yellow Whirled!A", 3, 8)
+
+    def test_filefec_min_shares_with_padding(self, noisy=VERBOSE):
+        return self._help_test_filefec("Yellow Whirled!A", 3, 8, numshs=3)
+
+if __name__ == "__main__":
+    if hasattr(unittest, 'main'):
+        unittest.main()
+    else:
+        sys.path.append(os.getcwd())
+        mods = []
+        fullname = os.path.realpath(os.path.abspath(__file__))
+        for pathel in sys.path:
+            fullnameofpathel = os.path.realpath(os.path.abspath(pathel))
+            if fullname.startswith(fullnameofpathel):
+                relname = fullname[len(fullnameofpathel):]
+                mod = (os.path.splitext(relname)[0]).replace(os.sep, '.').strip('.')
+                mods.append(mod)
+
+        mods.sort(cmp=lambda x, y: cmp(len(x), len(y)))
+        mods.reverse()
+        for mod in mods:
+            cmdstr = "trial %s %s" % (' '.join(sys.argv[1:]), mod)
+            print cmdstr
+            if os.system(cmdstr) == 0:
+                break