diff --git a/NEWS.rst b/NEWS.rst index bb713635..eb11657b 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -4,7 +4,46 @@ News for Kodo This file lists the major changes between versions. For a more detailed list of every change, see the Git log. -Latest +13.0.0 +------ +* Major: Replaced the linear_block_decoder with the + bidirectional_linear_block_decoder layer. The bidirectional linear + block decoder layer uses a direction policy to determine whether to + perform Gaussian elimination from left-to-right or + right-to-left. Certain newer network coding algorithms can be + implemented efficiently utilizing this flexibility. Based on the + bidirectional layer we have added the forward and backwards linear + block decoder. +* Minor: Added support for specifying the number of nonzero symbols in the + sparse codes (this extends the API which previously only supported a + fraction of nonzero symbols to be specified). +* Minor: Added generic functions for printing debug information from codec + stacks where this functionality is supported. + +12.0.0 +------ +* Major: Changed the partial_decoding_tracker to only provide the + functionality needed to detect "early" or partial decoding. The + monitor functionality has been moved to the largest_nonzero_index_decoder + layer. +* Minor: Added the payload_rank_encoder and payload_rank_decoder layers + which will explicitly exchange the rank of the encoder matrix and the + decoder matrix to support partial decoding. + +11.2.0 +------ +* Minor: Added the partial_decoding_tracker layer which "monitors" the + coding vectors being passed to a decoder in order to detect early + decoding opportunities. This means that although not all packets have + yet been sent from the encoder, it might happen that we can decode + anyway. This kind of functionality is useful especially for applications + which require low delay. +* Minor: Added on-the-fly encoding and decoding stacks in + src/kodo/rlnc/on_he_fly_codes.hpp the on-the-fly stacks have the advantage + that they allow encoding and decoding to proceed even without having all + encoding symbols available. + +11.1.0 ------ * Minor: Added new cached_symbol_decoder layer, this layer does not perform any decoding on the incoming symbol, but provides access to the encoded @@ -13,6 +52,7 @@ Latest * Minor: Added new example showing some one way to use some of the debug layers in kodo. The example is in the examples folder called use_debug_layers +* Bug: Fix missing return in the payload_recoder recode() function. 11.0.0 ------ diff --git a/README.rst b/README.rst index 5dc71f3d..803ae83e 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,13 @@ +License +------- + +To obtain a valid Kodo license **you must fil out the license request** form_. + +Kodo is available under a research and educational friendly licensee, see the details in the LICENSE.rst file. + +.. _form: http://steinwurf.com/license/ + + Introduction ------------ @@ -10,11 +20,6 @@ a multitude of build blocks and parameters that can be combined in order to create codes. To ensure ease of use several codes are predefined, and high level API's provided. -License -------- -Kodo is available under a research and educational friendly licensee, -see the details in the LICENSE.rst file. - Documentation ------------- To get started see the manual here: diff --git a/benchmark/count_operations/codes.hpp b/benchmark/count_operations/codes.hpp index 94a9c5ce..39ec5371 100644 --- a/benchmark/count_operations/codes.hpp +++ b/benchmark/count_operations/codes.hpp @@ -56,7 +56,7 @@ namespace kodo plain_symbol_id_reader< // Codec API aligned_coefficients_decoder< - linear_block_decoder< + forward_linear_block_decoder< // Coefficient Storage API coefficient_storage< coefficient_info< @@ -87,7 +87,7 @@ namespace kodo // Codec API aligned_coefficients_decoder< linear_block_decoder_delayed< - linear_block_decoder< + forward_linear_block_decoder< // Coefficient Storage API coefficient_storage< coefficient_info< diff --git a/benchmark/count_operations/wscript_build b/benchmark/count_operations/wscript_build index 1c1922f5..096cc386 100644 --- a/benchmark/count_operations/wscript_build +++ b/benchmark/count_operations/wscript_build @@ -2,7 +2,7 @@ # encoding: utf-8 bld.program( - features = 'cxx', + features = 'cxx benchmark', source = ['main.cpp'], target = 'kodo_count_operations', use = ['kodo_includes', 'fifi_includes', 'sak_includes', diff --git a/benchmark/decoding_probability/codes.hpp b/benchmark/decoding_probability/codes.hpp index 2b0add78..3180f789 100644 --- a/benchmark/decoding_probability/codes.hpp +++ b/benchmark/decoding_probability/codes.hpp @@ -26,7 +26,7 @@ namespace kodo // Codec API aligned_coefficients_decoder< linear_block_decoder_delayed< - linear_block_decoder< + forward_linear_block_decoder< // Coefficient Storage API coefficient_storage< coefficient_info< @@ -56,7 +56,7 @@ namespace kodo // Codec API aligned_coefficients_decoder< linear_block_decoder_delayed< - linear_block_decoder< + forward_linear_block_decoder< // Coefficient Storage API coefficient_storage< coefficient_info< diff --git a/benchmark/decoding_probability/wscript_build b/benchmark/decoding_probability/wscript_build index 7376c81c..35ec60d2 100644 --- a/benchmark/decoding_probability/wscript_build +++ b/benchmark/decoding_probability/wscript_build @@ -2,7 +2,7 @@ # encoding: utf-8 bld.program( - features = 'cxx', + features = 'cxx benchmark', source = ['main.cpp'], target = 'kodo_decoding_probability', use = ['kodo_includes', 'fifi_includes', 'sak_includes', diff --git a/benchmark/overhead/codes.hpp b/benchmark/overhead/codes.hpp index 44c0283b..b29a16d2 100644 --- a/benchmark/overhead/codes.hpp +++ b/benchmark/overhead/codes.hpp @@ -26,7 +26,7 @@ namespace kodo // Codec API aligned_coefficients_decoder< linear_block_decoder_delayed< - linear_block_decoder< + forward_linear_block_decoder< // Coefficient Storage API coefficient_storage< coefficient_info< @@ -56,7 +56,7 @@ namespace kodo // Codec API aligned_coefficients_decoder< linear_block_decoder_delayed< - linear_block_decoder< + forward_linear_block_decoder< // Coefficient Storage API coefficient_storage< coefficient_info< diff --git a/benchmark/overhead/wscript_build b/benchmark/overhead/wscript_build index f6c203cc..697a5e1d 100644 --- a/benchmark/overhead/wscript_build +++ b/benchmark/overhead/wscript_build @@ -2,7 +2,7 @@ # encoding: utf-8 bld.program( - features = 'cxx', + features = 'cxx benchmark', source = ['main.cpp'], target = 'kodo_overhead', use = ['kodo_includes', 'fifi_includes', 'sak_includes', diff --git a/benchmark/throughput/codes.hpp b/benchmark/throughput/codes.hpp index 85dddcf1..16e17e0e 100644 --- a/benchmark/throughput/codes.hpp +++ b/benchmark/throughput/codes.hpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace kodo @@ -30,7 +31,7 @@ namespace kodo // Codec API aligned_coefficients_decoder< linear_block_decoder_delayed< - linear_block_decoder< + forward_linear_block_decoder< // Coefficient Storage API coefficient_storage< coefficient_info< @@ -83,6 +84,37 @@ namespace kodo > > > > > > > > > > > > > > > > > { }; + /// RLNC decoder which uses the policy based linear block decoder + template + class backward_full_rlnc_decoder + : public // Payload API + payload_recoder::type, + finite_field_info + > > > > > > > > > > > > > > > + { }; + } diff --git a/benchmark/throughput/main.cpp b/benchmark/throughput/main.cpp index 57ffa405..46ce830f 100644 --- a/benchmark/throughput/main.cpp +++ b/benchmark/throughput/main.cpp @@ -340,7 +340,8 @@ struct sparse_throughput_benchmark : /// The type of the base benchmark typedef throughput_benchmark Super; - /// We need access to the encoder built to adjust the density + /// We need access to the encoder built to adjust the number of + /// nonzero symbols using Super::m_encoder; public: @@ -350,12 +351,13 @@ struct sparse_throughput_benchmark : auto symbols = options["symbols"].as >(); auto symbol_size = options["symbol_size"].as >(); auto types = options["type"].as >(); - auto density = options["density"].as >(); + auto nonzero_symbols = + options["nonzero_symbols"].as >(); assert(symbols.size() > 0); assert(symbol_size.size() > 0); assert(types.size() > 0); - assert(density.size() > 0); + assert(nonzero_symbols.size() > 0); for(const auto& s : symbols) { @@ -363,13 +365,13 @@ struct sparse_throughput_benchmark : { for(const auto& t : types) { - for(const auto& d: density) + for(const auto& n: nonzero_symbols) { gauge::config_set cs; cs.set_value("symbols", s); cs.set_value("symbol_size", p); cs.set_value("type", t); - cs.set_value("density", d); + cs.set_value("nonzero_symbols", n); Super::add_configuration(cs); } @@ -384,8 +386,8 @@ struct sparse_throughput_benchmark : gauge::config_set cs = Super::get_current_configuration(); - double density = cs.get_value("density"); - m_encoder->set_density(density); + uint32_t symbols = cs.get_value("nonzero_symbols"); + m_encoder->set_nonzero_symbols(symbols); } }; @@ -436,27 +438,32 @@ BENCHMARK_OPTION(throughput_options) gauge::runner::instance().register_options(options); } -BENCHMARK_OPTION(throughput_density_options) +BENCHMARK_OPTION(throughput_nonzero_symbols_options) { gauge::po::options_description options; - std::vector density; - density.push_back(0.1); - density.push_back(0.2); - density.push_back(0.3); - density.push_back(0.4); - density.push_back(0.5); + std::vector nonzero_symbols; + nonzero_symbols.push_back(1); + nonzero_symbols.push_back(2); + nonzero_symbols.push_back(3); + nonzero_symbols.push_back(4); + nonzero_symbols.push_back(5); - auto default_density = - gauge::po::value >()->default_value( - density, "")->multitoken(); + auto default_nonzero_symbols = + gauge::po::value >()->default_value( + nonzero_symbols, "")->multitoken(); options.add_options() - ("density", default_density, "Set the density of the sparse codes"); + ("nonzero_symbols", + default_nonzero_symbols, + "Set the number of nonzero symbols of the sparse codes"); gauge::runner::instance().register_options(options); } +//------------------------------------------------------------------ +// FullRLNC +//------------------------------------------------------------------ typedef throughput_benchmark< kodo::full_rlnc_encoder, @@ -494,6 +501,55 @@ BENCHMARK_F(setup_rlnc_throughput2325, FullRLNC, Prime2325, 5) run_benchmark(); } +//------------------------------------------------------------------ +// BackwardFullRLNC +//------------------------------------------------------------------ + +typedef throughput_benchmark< + kodo::full_rlnc_encoder, + kodo::backward_full_rlnc_decoder > + setup_backward_rlnc_throughput; + +BENCHMARK_F(setup_backward_rlnc_throughput, BackwardFullRLNC, Binary, 5) +{ + run_benchmark(); +} + +typedef throughput_benchmark< + kodo::full_rlnc_encoder, + kodo::backward_full_rlnc_decoder > + setup_backward_rlnc_throughput8; + +BENCHMARK_F(setup_backward_rlnc_throughput8, BackwardFullRLNC, Binary8, 5) +{ + run_benchmark(); +} + +typedef throughput_benchmark< + kodo::full_rlnc_encoder, + kodo::backward_full_rlnc_decoder > + setup_backward_rlnc_throughput16; + +BENCHMARK_F(setup_backward_rlnc_throughput16, BackwardFullRLNC, Binary16, 5) +{ + run_benchmark(); +} + +typedef throughput_benchmark< + kodo::full_rlnc_encoder, + kodo::backward_full_rlnc_decoder > + setup_backward_rlnc_throughput2325; + +BENCHMARK_F(setup_backward_rlnc_throughput2325, BackwardFullRLNC, Prime2325, 5) +{ + run_benchmark(); +} + + +//------------------------------------------------------------------ +// FullDelayedRLNC +//------------------------------------------------------------------ + typedef throughput_benchmark< kodo::full_rlnc_encoder, kodo::full_delayed_rlnc_decoder > diff --git a/benchmark/throughput/plot_throughput.py b/benchmark/throughput/plot_throughput.py index fa31304f..63e82168 100644 --- a/benchmark/throughput/plot_throughput.py +++ b/benchmark/throughput/plot_throughput.py @@ -10,22 +10,17 @@ import pandas as pd import pylab as plt import numpy as np +from matplotlib.backends.backend_pdf import PdfPages as pp -def plot_throughput(csvfile): +def plot_throughput(args): - df = pd.read_csv(csvfile) - - def density_to_string(density): - if not np.isnan(density): - return " density {}".format(density) - else: - return "" - - # df['density'] = df['density'].map(density_to_string) - # df['test'] = df['testcase'].map(str) + '.' + df['benchmark'] + df['density'] + df = pd.read_csv(args.csvfile) plot_groups = list(df.groupby(by=['testcase', 'symbol_size', 'type'])) + if args.outfile: + pdf = pp(args.outfile) + # Group the plots for (test, symbol_size, type), df in plot_groups: @@ -35,9 +30,18 @@ def density_to_string(density): else: return "" - df['benchmark'] = df['benchmark'] + ' ' + df['density'].map(density_to_string) + # Combine the testcase and benchmark columns into one (used for labels) + if not 'density' in df: + df['test'] = df['testcase'].map(str) + '.' + df['benchmark'] - group = df.groupby(by = ['benchmark', 'symbols']) + df = df.drop(['testcase','benchmark'], axis = 1) + else: + df['test'] = df['testcase'].map(str) + '.' + df['benchmark'] +\ + ' ' + df['density'].map(density_to_string) + df = df.drop(['testcase','benchmark', 'density'], axis = 1) + + + group = df.groupby(by = ['test', 'symbols']) def compute_throughput(group): s = group['throughput'] @@ -47,14 +51,16 @@ def compute_throughput(group): df = group.apply(compute_throughput) df = df.unstack(level=0) - df['mean'].plot(title="Throughput {} {} p={}B".format(test, type, symbol_size), - kind='bar') + df['mean'].plot(title="Throughput {} {} p={}B".format( + test, type, symbol_size), kind='bar') + if args.outfile: + pdf.savefig(transparent=True) - plt.show() - - print encoder - print decoder + if args.outfile: + pdf.close() + else: + plt.show() if __name__ == '__main__': @@ -64,7 +70,11 @@ def compute_throughput(group): '--csv_file', dest='csvfile', action='store', help='the .csv file written by gauge benchmark', default='out.csv') + parser.add_argument( + '--out_file', dest='outfile', action='store', + help='the pdf to save the plots to', + default=None) args = parser.parse_args() - plot_throughput(args.csvfile) + plot_throughput(args) diff --git a/benchmark/throughput/wscript_build b/benchmark/throughput/wscript_build index 39353234..385dabd2 100644 --- a/benchmark/throughput/wscript_build +++ b/benchmark/throughput/wscript_build @@ -2,7 +2,7 @@ # encoding: utf-8 bld.program( - features = 'cxx', + features = 'cxx benchmark', source = ['main.cpp'], target = 'kodo_throughput', use = ['kodo_includes', 'fifi_includes', 'sak_includes', diff --git a/docs/source/c_bindings.rst b/docs/source/c_bindings.rst new file mode 100644 index 00000000..90de7e85 --- /dev/null +++ b/docs/source/c_bindings.rst @@ -0,0 +1,104 @@ +.. _c_bindings: + +Kodo C bindings +=============== + +This repository C high-level bindings for the Kodo Network Coding library. +The bindings provide access to basic functionality provided by Kodo, such +as encoding and decoding of data. The examples folder provides sample +applications showing usage of the C API. + +Getting started +--------------- +The source code for the Kodo C bindings are available at our github.com +repository: + +* https://github.com/steinwurf/kodo-c-bindings + +To obtain the source code you can either clone the repository with +git or download a released version. + +To build the library you need the tools described on the Kodo +:ref:`getting_started` page. + +Building +-------- +To use the Kodo C bindings you first have to fetch the necessary +dependencies. There are two ways of getting the dependencies: + +1. Allowing the build scripts to automatically download the + dependencies via git (this requires an Internet connection). +2. Downloading the dependencies manually. + +For option 1 you may proceed directly to the `Build (automatically +downloading dependencies)`_ section which will do the automatic download. + +For option 2 we have to first download the dependencies needed described in +the `Build (manually downloaded dependencies)`_ section. + + +Build (manually downloaded dependencies) +........................................ +The dependencies required to build the Kodo C bindings are Kodo itself + +the dependencies of Kodo. Kodo's dependencies are specified in the +:ref:`kodo-dependencies` section. On that page you will also +find the description of how to build Kodo with manually downloaded +dependencies. This procedure is the same for the Kodo C-bindings. + +Be sure to read the :ref:`selecting-the-correct-versions` section to see +which versions you need to download (here you should use the ``wscript`` +in the kodo-c-bindings repository to obtain the version numbers). + +Following the instructions in the :ref:`download-kodo-dependencies` section +you should have downloaded the following dependencies: Fifi, Sak, Boost, +Waf-tools, Gtest and Gauge. You should download Kodo in the same way. After +this we are ready to build. + +To configure the project run: +:: + + python waf configure --bundle=NONE --kodo-path=insert-path-to/kodo --fifi-path=insert-path-to/fifi --sak-path=insert-path-to/sak/ --boost-path=insert-path-to/external-boost-light/ --waf-tools-path=insert-path-to/external-waf-tools/ --gtest-path=insert-path-to/external-gtest/ --gauge-path=insert-path-to/cxx-gauge/ + +After configure run the following command to build the static library: +:: + + python waf build + +Build (automatically downloading dependencies) +.............................................. +If you wish to let the waf build scripts automatically download the +source code you can use the steps outline here: + +To configure the project run: +:: + + python waf configure --bundle=ALL + +This will configure the project and download all the dependencies needed +(the dependencies will be located in a folder called bundle_depencies) in +the folder where you run the command. + +After configure run the following command to build the static library: +:: + + python waf build + + +Linking with an application +--------------------------- +Running the ``waf build`` step will produce a static library in +the ``build`` folder called ``libckodo.a`` on Linux and Mac and +``ckodo.lib`` on Windows. The following section will show you how to +link with the library in your application. To do this you need to +include ``ckodo.h`` in your application and link with ``libckodo``. +Here is a typical gcc link command: + +:: + + gcc myapp.c -o myapp -Ipath_to_ckodo_h -Wl,-Bstatic -Lpath_to_libkodo_a -lckodo -Wl,-Bdynamic -lstdc++ + +Substitute the ``path_to_ckodo_h`` with the path of ``ckodo.h`` similarly +Substitute the ``path_to_libkodo_a`` with the path of the ``libkodo.a`` +library. + + diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 4b9246de..a733a220 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -1,7 +1,9 @@ +.. _getting_started: + Getting Started =============== -.. _getting_started +.. _tools-needed: Tools Needed ------------ @@ -211,7 +213,9 @@ Provided that you have the `Tools Needed`_ installed. .. note:: The ``waf configure`` step might take several minutes depending on the speed of your Internet connection. This would be a good time to grab a coffee or similar while the dependencies are - downloaded. + downloaded. If you do not have an Internet connection you can see + the :ref:`using-kodo-in-your-application` section which shows how + to manually download and specify the Kodo dependencies. .. note:: If you downloaded the library as a zip archive and you have not setup git to automatically authenticate against github.com you diff --git a/docs/source/getting_the_sourcecode.rst b/docs/source/getting_the_sourcecode.rst index bc1dce76..83b63c6c 100644 --- a/docs/source/getting_the_sourcecode.rst +++ b/docs/source/getting_the_sourcecode.rst @@ -4,8 +4,19 @@ Kodo Dependencies .. _sourcecode: + +Obtain a license for KODO +------------------------- + +Before you download or use KODO **you MUST obtain a valid license**. +This can be done by filling out the license request form_. + +.. _form: http://steinwurf.com/license/ + + Download the Kodo sourcecode ---------------------------- + There are several ways in which you may get the Kodo library and its dependencies. Which approach you prefer might depend on your intended use-case for Kodo. @@ -16,7 +27,7 @@ use-case for Kodo. 2. If you wish to use Kodo in a separate project, possibly your own build tools. You may wish to download Kodo's dependencies as separate git repositories. For more information about this see the - section `Using Kodo in Your Application`_. + section :ref:`using-kodo-in-your-application`. In the following we will only describe option 1 (using the Kodo buildscripts). @@ -76,11 +87,12 @@ Quick Start (building Kodo examples and unit tests) If you are primarily interested in quickly trying some Kodo examples or building the unit-tests, we have tried to make that really easy. -Provided that you have the `Tools Needed`_ installed. +Provided that you have the :ref:`tools-needed` installed. .. note:: We recommend trying to build and run the unit-tests, before using Kodo in your own project. However, if you want to skip - this step you may jump directly to :ref:include-kodo-in-project + this step you may jump directly to + :ref:`using-kodo-in-your-application` 1. Navigate to the directory containing the Kodo sources: diff --git a/docs/source/index.rst b/docs/source/index.rst index 6a9e3dc1..147f0eb9 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -43,6 +43,7 @@ Documentation hacking_kodo howto misc + c_bindings nc_glossary diff --git a/docs/source/using_kodo_in_your_application.rst b/docs/source/using_kodo_in_your_application.rst index 1b02ade4..8860f31b 100644 --- a/docs/source/using_kodo_in_your_application.rst +++ b/docs/source/using_kodo_in_your_application.rst @@ -6,6 +6,7 @@ In the following we will describe what you need to do to use Kodo in your application / project. .. _kodo-dependencies: + Kodo dependencies ----------------- In Kodo we rely on a number of external libraries, these must be available @@ -29,32 +30,96 @@ them. The libraries are: of C++ utilities. We use only a subset of these functionalities such as smart-pointers. - * http://boost.org + * http://github.com/steinwurf/external-boost-light If you have tried to use the Kodo build scripts you will notice that these download a number of additional libraries. These libraries are only needed when/if you want to compile the Kodo unit-tests, examples or benchmarks. +4. **Waf-tools**: This repository contains additional tools used by + out build system. These tools adds functionality to waf which are + used e.g. by our continuous-integration build system. + + * http://github.com/steinwurf/external-waf-tools + +5. **Gtest**: The Google C++ Unit Testing Framework is used by all the + Kodo unit tests to ensure the library functions correctly. + + * http://github.com/steinwurf/external-gtest + +6. **Gauge**: Gauge is a C++ benchmarking tool which we use in Kodo to + profile the implemented algorithms. + + * http://github.com/steinwurf/cxx-gauge + +.. _selecting-the-correct-versions: + +Selecting the correct versions +------------------------------ +If you use the automatic approach by letting the build scripts download the +dependencies, they will select the latest compatible version. If you download +the dependencies manually you will have to select a compatible version. This +information is stored in the ``wscript`` file found in Kodo's root folder. + +Within that file you will find all Kodo's dependencies specified in the +following way: + +:: + + bundle.add_dependency(opt, + resolve.ResolveGitMajorVersion( + name = 'fifi', + git_repository = 'github.com/steinwurf/fifi.git', + major_version = 9)) + +The above command sets up a dependency for the Fifi library. The version +required is specified in the ``major_version = 9`` line. This means that Kodo +requires version ``9.x.y`` of the Fifi library, where ``x.y`` should be +selected to pick the newest available version. Visiting the download page +at github.com for the Fifi library: + +* https://github.com/steinwurf/fifi/releases + +We get a list of available versions. At the time of writing this would be +version ``9.1.0``. These version numbers are available as ``git tags`` if you +choose to manually checkout the git repositories. + +.. _download-kodo-dependencies: + Download Kodo Dependencies -------------------------- -All dependencies required by Kodo are hosted on github.com and may be found at -github.com/steinwurf. +All dependencies required by Kodo are hosted on github.com and may be found +at http://github.com/steinwurf. There are several ways in which you may get the Kodo library and its dependencies. Which approach you prefer might depend on your intended use-case for Kodo. -1. As shown in :ref:`quick-start` the Kodo build scripts supports downloading - the dependency repositories. The build script with do a git clone and - checkout the latest compatible tagged version of the dependency. +1. As shown in :ref:`getting_started` the Kodo build scripts supports + downloading the dependency repositories automatically. The build + script with do a ``git clone`` and checkout the latest compatible tagged + version of the dependency. 2. You may wish to manually download Kodo's dependencies as separate git repositories. In the following we describe this option. -Clone the git repository (recommended) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +3. You can also download the Kodo dependencies as zip or tar.gz archives + from the dependencies corresponding github.com page. + +Before moving on it is important to stress that downloading all +dependencies is only necessary if you wish to build the Kodo unit tests +and benchmarks using the Kodo build system. If you simply want to use Kodo +in your application you only need to download the Fifi, Sak and Boost +dependencies and you do not need to build the Kodo library (since it is +header-only). +If that is your goal you can skip to the `Example application using +makefile`_ section after downloading the three required libraries.. + + +Download using Git +.................. 1. Create a suitable directory for the projects (optional) @@ -63,7 +128,7 @@ Clone the git repository (recommended) mkdir dev cd dev -2. Clone and download the Fifi libraries by running: +2. Clone or download the Fifi libraries by running: :: @@ -85,9 +150,100 @@ Clone the git repository (recommended) For example many Linux distributions support installing Boost via the package manager. Alternatively Boost also provides its own version control repositories, if you - wish, you may also use download Boost using those repositories. + wish, you may also download Boost using those repositories. + +5. Clone and download the extra Waf-tools: + + :: + + git clone git://github.com/steinwurf/external-waf-tools.git + +6. Clone and download the Gtest library. + + :: + + git clone git://github.com/steinwurf/external-gtest.git + -Example using makefile / command-line +7. Clone and download the Gauge library. + + :: + + git clone git://github.com/steinwurf/cxx-gauge.git + +Now we have to visit the downloaded repositories and select the correct +versions e.g. for Fifi, first list the available tags: +:: + + cd fifi + git tag -l + +Using the information from the ``wscript`` (described in +`Selecting the correct versions`_) we can checkout a tagged version: + +:: + + git checkout 9.1.0 + +We now do this for all the downloaded repositories. + +Download as zip/tar.gz archives +............................... + +Here we have to visit the download pages of the different dependencies +and download the correct versions (described in `Selecting the correct +versions`_): + +1. Fifi: + https://github.com/steinwurf/fifi/releases +2. Sak: + https://github.com/steinwurf/sak/releases +3. Boost: + https://github.com/steinwurf/external-boost-light/releases +4. Waf-tools: + https://github.com/steinwurf/external-waf-tools/releases +5. Gtest: + https://github.com/steinwurf/external-gtest/releases +6. Gauge: + https://github.com/steinwurf/cxx-gauge/releases + + +Configuring Kodo with manually downloaded dependencies +------------------------------------------------------ + +After downloading all the dependencies manually we have to inform the +Kodo build scripts to use those instead of trying to automatically +downloading them. This is done using the following command: + +:: + + python waf configure --bundle=NONE --fifi-path=insert-path-to/fifi --sak-path=insert-path-to/sak/ --boost-path=insert-path-to/external-boost-light/ --waf-tools-path=insert-path-to/external-waf-tools/ --gtest-path=insert-path-to/external-gtest/ --gauge-path=insert-path-to/cxx-gauge/ + +The bundle options supports a number of different use-cases. The following +will bundle all dependencies but the Fifi library which we have to +manually specify a path for: +:: + + python waf configure --bundle=ALL,-fifi --fifi-path=insert-path-to/fifi + +Or we may bundle only Fifi: +:: + + python waf configure --bundle=NONE,fifi --sak-path=insert-path-to/sak/ --boost-path=insert-path-to/external-boost-light/ --waf-tools-path=insert-path-to/external-waf-tools/ --gtest-path=insert-path-to/external-gtest/ --gauge-path=insert-path-to/cxx-gauge/ + +More libraries may be added to the ``--bundle=`` option using commas e.g. +bundle all but Fifi and Sak +:: + + python waf configure --bundle=ALL,-fifi,-sak --fifi-path=insert-path-to/fifi --sak-path=insert-path-to/sak + +The bundle options can be seen by running: +:: + + python waf --help + + +Example application using makefile ------------------------------------- If you would like to see an example of building an application with @@ -97,6 +253,8 @@ in the ``examples/sample_makefile`` folder in the `Kodo repository`_. .. _`Kodo repository`: https://github.com/steinwurf/kodo +In this case it only requires that you have Fifi, Sak and Boost downloaded. + .. diff --git a/doxygen/groups_api.doxygen b/doxygen/groups_api.doxygen index a2a6f22e..8d47befa 100644 --- a/doxygen/groups_api.doxygen +++ b/doxygen/groups_api.doxygen @@ -261,3 +261,22 @@ /// @ingroup block_partitioning /// @brief Implementations of the BlockPartitioning type. +//------------------------------------------------------------------ +// TYPE TRAITS +//------------------------------------------------------------------ + +/// @defgroup type_traits Type traits for generic programming +/// @brief The type traits are useful for compile time enabling or +/// disabling a specific code paths + +//------------------------------------------------------------------ +// GENERIC API +//------------------------------------------------------------------ + +/// @defgroup generic_api Generic API functions +/// @brief The generic API functions are functions that makes it easier to +/// build programs that compile time can enable or disable call to +/// various functions. + + + diff --git a/doxygen/layer_api.doxygen b/doxygen/layer_api.doxygen index 421c59a3..642f1688 100644 --- a/doxygen/layer_api.doxygen +++ b/doxygen/layer_api.doxygen @@ -22,6 +22,10 @@ public: /// also provide this typedef. typedef seed_type seed_type; + /// @typedef rank_type + /// Specifies the data type used to store the rank + typedef rank_type rank_type; + class factory { public: @@ -320,6 +324,13 @@ public: /// the symbol coefficients. uint32_t coefficients_length() const; + /// @ingroup coefficient_storage_api + /// @return The number of coefficients stored in a coefficient vector. The + /// count will say how many finite field elements will be stored + /// within a coefficient vector, whereas the size and length refers + /// to the space (in terms of memory) occupied by those coefficients. + uint32_t coefficients_count() const; + /// @ingroup coefficient_storage_api /// @param index the index in the vector /// @return the specified vector diff --git a/examples/decode_simple/decode_simple.cpp b/examples/decode_simple/decode_simple.cpp index fcc65b96..fb12955f 100644 --- a/examples/decode_simple/decode_simple.cpp +++ b/examples/decode_simple/decode_simple.cpp @@ -41,7 +41,7 @@ namespace kodo debug_cached_symbol_decoder< cached_symbol_decoder< debug_linear_block_decoder< - linear_block_decoder< + forward_linear_block_decoder< // Coefficient Storage API debug_coefficient_storage< coefficient_storage< diff --git a/examples/encode_decode_storage/encode_decode_storage.cpp b/examples/encode_decode_storage/encode_decode_storage.cpp index d9e9e27e..c24bc992 100644 --- a/examples/encode_decode_storage/encode_decode_storage.cpp +++ b/examples/encode_decode_storage/encode_decode_storage.cpp @@ -70,7 +70,7 @@ namespace kodo plain_symbol_id_reader< // Codec API aligned_coefficients_decoder< - linear_block_decoder< + forward_linear_block_decoder< // Coefficient Storage API coefficient_storage< coefficient_info< diff --git a/examples/rank_callback/rank_callback.cpp b/examples/rank_callback/rank_callback.cpp index 19eec273..46288f45 100644 --- a/examples/rank_callback/rank_callback.cpp +++ b/examples/rank_callback/rank_callback.cpp @@ -38,7 +38,7 @@ namespace kodo rank_callback_decoder< aligned_coefficients_decoder< - linear_block_decoder< + forward_linear_block_decoder< // Coefficient Storage API coefficient_storage< coefficient_info< diff --git a/examples/use_debug_layers/use_debug_layers.cpp b/examples/use_debug_layers/use_debug_layers.cpp index 686eedb0..b32e1d2d 100644 --- a/examples/use_debug_layers/use_debug_layers.cpp +++ b/examples/use_debug_layers/use_debug_layers.cpp @@ -16,42 +16,6 @@ /// Simple example showing how to use some of the debug layers defined /// in Kodo. -namespace kodo -{ - template - class debug_full_rlnc_decoder - : public // Payload API - payload_recoder::type, - finite_field_info - > > > > > > > > > > > > > > > > > > - { }; -} - int main() { // Seed rand diff --git a/src/kodo/backward_linear_block_decoder.hpp b/src/kodo/backward_linear_block_decoder.hpp new file mode 100644 index 00000000..1dbc52f1 --- /dev/null +++ b/src/kodo/backward_linear_block_decoder.hpp @@ -0,0 +1,41 @@ +// Copyright Steinwurf ApS 2011-2012. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +namespace kodo +{ + + /// @todo document this + /// @ingroup codec_layers + /// @brief Implements basic linear block decoder. + /// + /// The linear block decoder + /// expects that an encoded symbol is described by a vector of + /// coefficients. Using these coefficients the block decoder subtracts + /// incoming symbols until the original data has been recreated. + template + class backward_linear_block_decoder : + public bidirectional_linear_block_decoder< + backward_linear_block_decoder_policy, SuperCoder> + { }; + +} + diff --git a/src/kodo/backward_linear_block_decoder_policy.hpp b/src/kodo/backward_linear_block_decoder_policy.hpp new file mode 100644 index 00000000..ca1830b6 --- /dev/null +++ b/src/kodo/backward_linear_block_decoder_policy.hpp @@ -0,0 +1,89 @@ +// Copyright Steinwurf ApS 2011-2012. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include +#include + +namespace kodo +{ + + /// Policy object for determining the "direction" of the + /// linear_block_decoder e.g. whether we will look for the + /// first pivot from the beginning of the coefficients vector + /// or in some other way. + /// + /// The backward_linear_block decoder will search for pivots from + /// the right of the encoding vector. + /// E.g. if you see an encoding vector like: + /// + /// <-----------+ direction of search for pivots + /// + /// 0 1 0 1 1 0 0 + /// ^ ^ ^ + /// | | | + /// | | +----+ First coefficient searched + /// | +--------+ First non zero coefficient + /// +----------------+ Last coefficient searched + /// + struct backward_linear_block_decoder_policy + { + /// Construct the policy with the desired start and stop interval + /// but ends included so [start:stop] + /// @param start The first element to look at + /// @param stop The last element to look at + backward_linear_block_decoder_policy(uint32_t start, uint32_t stop) + : m_start(start+1), + m_stop(stop) + { + assert(m_start > m_stop); + } + + /// @return true if the policy is at the end + bool at_end() const + { + return m_start <= m_stop; + } + + /// Advance the policy to the next index + void advance() + { + assert(!at_end()); + --m_start; + } + + /// @return the current index + uint32_t index() const + { + return m_start - 1; + } + + /// @param a The first value + /// @param b The second value + /// @return the maximum value of the two input values + static uint32_t max(uint32_t a, uint32_t b) + { + return std::min(a,b); + } + + /// @param a The first value + /// @param b The second value + /// @return the minimum value of the two input values + static uint32_t min(uint32_t a, uint32_t b) + { + return std::max(a,b); + } + + /// The start value + uint32_t m_start; + + /// The end value + uint32_t m_stop; + + }; + +} + diff --git a/src/kodo/linear_block_decoder.hpp b/src/kodo/bidirectional_linear_block_decoder.hpp similarity index 91% rename from src/kodo/linear_block_decoder.hpp rename to src/kodo/bidirectional_linear_block_decoder.hpp index d34c524c..922683d1 100644 --- a/src/kodo/linear_block_decoder.hpp +++ b/src/kodo/bidirectional_linear_block_decoder.hpp @@ -17,9 +17,11 @@ #include #include +#include +#include + namespace kodo { - /// @ingroup codec_layers /// @brief Implements basic linear block decoder. /// @@ -27,8 +29,8 @@ namespace kodo /// expects that an encoded symbol is described by a vector of /// coefficients. Using these coefficients the block decoder subtracts /// incoming symbols until the original data has been recreated. - template - class linear_block_decoder : public SuperCoder + template + class bidirectional_linear_block_decoder : public SuperCoder { public: @@ -41,10 +43,12 @@ namespace kodo /// @copydoc layer::factory typedef typename SuperCoder::factory factory; + typedef DirectionPolicy direction_policy; + public: /// Constructor - linear_block_decoder() + bidirectional_linear_block_decoder() : m_rank(0), m_maximum_pivot(0) { } @@ -69,7 +73,11 @@ namespace kodo std::fill_n(m_coded.begin(), the_factory.symbols(), false); m_rank = 0; - m_maximum_pivot = 0; + + // Depending on the policy we either go from 0 to symbols or + // from symbols to 0. + m_maximum_pivot = + direction_policy::min(0, the_factory.symbols() - 1); } /// @copydoc layer::decode_symbol(uint8_t*,uint8_t*) @@ -125,10 +133,8 @@ namespace kodo m_uncoded[ symbol_index ] = true; - if(symbol_index > m_maximum_pivot) - { - m_maximum_pivot = symbol_index; - } + m_maximum_pivot = + direction_policy::max(symbol_index, m_maximum_pivot); } } @@ -203,10 +209,8 @@ namespace kodo m_coded[ *pivot_index ] = true; - if(*pivot_index > m_maximum_pivot) - { - m_maximum_pivot = *pivot_index; - } + m_maximum_pivot = + direction_policy::max(*pivot_index, m_maximum_pivot); } /// When adding a raw symbol (i.e. uncoded) with a specific @@ -304,8 +308,14 @@ namespace kodo assert(symbol_id != 0); assert(symbol_data != 0); - for(uint32_t i = 0; i < SuperCoder::symbols(); ++i) + uint32_t start = direction_policy::min(0, SuperCoder::symbols()-1); + uint32_t end = direction_policy::max(0, SuperCoder::symbols()-1); + + // std::cout << start << " " << end << std::endl; + + for(direction_policy p(start, end); !p.at_end(); p.advance()) { + uint32_t i = p.index(); value_type current_coefficient = fifi::get_value(symbol_id, i); @@ -378,10 +388,20 @@ namespace kodo // If this pivot index was smaller than the maximum pivot // index we have, we might also need to backward // substitute the higher pivot values into the new packet - for(uint32_t i = pivot_index + 1; i <= m_maximum_pivot; ++i) + uint32_t end = direction_policy::max(0, SuperCoder::symbols()-1); + + // std::cout << "Pivot " << pivot_index << " to " << end << std::endl; + + direction_policy p(pivot_index, end); + + // Jump past the pivot_index position + p.advance(); + + for(; !p.at_end(); p.advance()) { - // Do we have a non-zero value here? + uint32_t i = p.index(); + // Do we have a non-zero value here? value_type value = fifi::get_value(symbol_id, i); @@ -438,11 +458,18 @@ namespace kodo assert(pivot_index < SuperCoder::symbols()); + uint32_t from = direction_policy::min(0, SuperCoder::symbols()-1); + uint32_t to = m_maximum_pivot; + + // std::cout << from << " " << to << std::endl; + // We found a "1" that nobody else had as pivot, we now // substract this packet from other coded packets // - if they have a "1" on our pivot place - for(uint32_t i = 0; i <= m_maximum_pivot; ++i) + for(direction_policy p(from, to); !p.at_end(); p.advance()) { + uint32_t i = p.index(); + if( m_uncoded[i] ) { // We know that we have no non-zero elements diff --git a/src/kodo/coefficient_info.hpp b/src/kodo/coefficient_info.hpp index 93e89ad7..1275b1ed 100644 --- a/src/kodo/coefficient_info.hpp +++ b/src/kodo/coefficient_info.hpp @@ -51,7 +51,8 @@ namespace kodo /// Constructor coefficient_info() - : m_coefficients_length(0), + : m_coeffcients_elements(0), + m_coefficients_length(0), m_coefficients_size(0) { } @@ -61,6 +62,8 @@ namespace kodo { SuperCoder::initialize(the_factory); + m_coeffcients_elements = the_factory.symbols(); + m_coefficients_length = fifi::elements_to_length(the_factory.symbols()); @@ -71,6 +74,15 @@ namespace kodo assert(m_coefficients_size > 0); } + /// @copydoc layer::coefficients_elements() const + uint32_t coefficients_elements() const + { + // We will typically have a single coefficient per coding symbol + // however in some cases this is not the case. In those cases + // we this value may be updated. + return m_coeffcients_elements; + } + /// @copydoc layer::coefficients_length() const uint32_t coefficients_length() const { @@ -87,6 +99,9 @@ namespace kodo private: + /// The number of useful finite field elements in the coefficients + uint32_t m_coeffcients_elements; + /// The length of coefficients in value_type elements uint32_t m_coefficients_length; diff --git a/src/kodo/coefficient_storage.hpp b/src/kodo/coefficient_storage.hpp index 05267be9..984112df 100644 --- a/src/kodo/coefficient_storage.hpp +++ b/src/kodo/coefficient_storage.hpp @@ -26,12 +26,6 @@ namespace kodo /// @copydoc layer::value_type typedef typename field_type::value_type value_type; - /// Pointer to coder produced by the factories - typedef typename SuperCoder::pointer pointer; - - /// The factory type - typedef typename SuperCoder::factory factory; - public: /// @copydoc layer::construct(Factory&) diff --git a/src/kodo/debug_cached_symbol_decoder.hpp b/src/kodo/debug_cached_symbol_decoder.hpp index 649bc996..00fc2f32 100644 --- a/src/kodo/debug_cached_symbol_decoder.hpp +++ b/src/kodo/debug_cached_symbol_decoder.hpp @@ -22,8 +22,8 @@ namespace kodo /// @brief This layer makes it easy to print the incoming coded symbol /// and coding coefficients /// - /// The debug layer supports printing both the coded symbol data through the - /// print_cached_symbol_data() and the coding coefficients through the + /// The debug layer supports printing both the coded symbol data through + /// the print_cached_symbol_data() and the coding coefficients through the /// print_cached_symbol_coefficients(). /// /// For the print_cached_symbol_data() function the output will be the diff --git a/src/kodo/debug_coefficient_storage.hpp b/src/kodo/debug_coefficient_storage.hpp index 192d86ba..5dfe7619 100644 --- a/src/kodo/debug_coefficient_storage.hpp +++ b/src/kodo/debug_coefficient_storage.hpp @@ -33,35 +33,14 @@ namespace kodo /// Prints the decoding matrix to the output stream /// @param out The output stream to print to - void print_coefficients(std::ostream& out) + void print_coefficients_value(std::ostream& out) { + out << "\t"; for(uint32_t i = 0; i < SuperCoder::symbols(); ++i) { - print_coefficients(out, i); + out << i << "\t"; } - } - - /// Prints a vector of coefficients - /// @param out The output stream to print to - /// @param index The index of the coefficients vector to print - void print_coefficients(std::ostream& out, uint32_t index) - { - out << index << ": "; - - const uint8_t* c = SuperCoder::coefficients_value(index); - - for(uint32_t j = 0; j < SuperCoder::symbols(); ++j) - { - out << (uint32_t) c[j] << " "; - } - - out << std::endl; - } - - /// Prints the decoding matrix to the output stream - /// @param out The output stream to print to - void print_coefficients_value(std::ostream& out) - { + out << "\n"; for(uint32_t i = 0; i < SuperCoder::symbols(); ++i) { print_coefficients_value(out, i); @@ -73,18 +52,18 @@ namespace kodo /// @param index The index of the coefficients vector to print void print_coefficients_value(std::ostream& out, uint32_t index) { - out << index << ": "; + out << index << ":\t"; const value_type* c = SuperCoder::coefficients_value(index); - for(uint32_t j = 0; j < SuperCoder::symbols(); ++j) + for(uint32_t j = 0; j < SuperCoder::coefficients_elements(); ++j) { value_type value = fifi::get_value(c, j); static_assert(sizeof(uint32_t) >= sizeof(value_type), "value_type will overflow in this print"); - out << (uint32_t) value << " "; + out << (uint32_t) value << "\t"; } out << std::endl; diff --git a/src/kodo/debug_linear_block_decoder.hpp b/src/kodo/debug_linear_block_decoder.hpp index 53a134dc..64f1d897 100644 --- a/src/kodo/debug_linear_block_decoder.hpp +++ b/src/kodo/debug_linear_block_decoder.hpp @@ -9,6 +9,8 @@ #include #include #include +#include + #include @@ -69,18 +71,18 @@ namespace kodo { if (!SuperCoder::symbol_pivot(i)) { - out << i << " ?: "; + out << std::setfill(' ') << std::setw(3) << i << " ?: "; } else if (SuperCoder::symbol_coded(i)) { - out << i << " C: "; + out << std::setfill(' ') << std::setw(3) << i << " C: "; } else { - out << i << " U: "; + out << std::setfill(' ') << std::setw(3) << i << " U: "; } - const value_type* c = SuperCoder::coefficients(i); + const value_type* c = SuperCoder::coefficients_value(i); for(uint32_t j = 0; j < SuperCoder::symbols(); ++j) { @@ -88,10 +90,9 @@ namespace kodo out << (uint32_t)value << " "; } - std::cout << std::endl; + out << std::endl; } - std::cout << std::endl; } }; diff --git a/src/kodo/deep_symbol_storage.hpp b/src/kodo/deep_symbol_storage.hpp index 9e8a1ae8..64deb367 100644 --- a/src/kodo/deep_symbol_storage.hpp +++ b/src/kodo/deep_symbol_storage.hpp @@ -123,9 +123,19 @@ namespace kodo void set_symbol(uint32_t index, const sak::const_storage &symbol) { assert(symbol.m_data != 0); - assert(symbol.m_size == SuperCoder::symbol_size()); + + // If the user sets a specific symbol on deep storage we allow that + // it does not have the full size. In this cases it is the users + // who must ensure correct decoding + assert(symbol.m_size <= SuperCoder::symbol_size()); + assert(symbol.m_size > 0); + assert(index < SuperCoder::symbols()); + // Symbols should always be added in order e.g. 0,1,2,3 .. + // so the index specified must equal the current count + assert(index == m_symbols_count); + sak::mutable_storage dest_data = sak::storage(m_data); uint32_t offset = index * SuperCoder::symbol_size(); diff --git a/src/kodo/forward_linear_block_decoder.hpp b/src/kodo/forward_linear_block_decoder.hpp new file mode 100644 index 00000000..932fbcee --- /dev/null +++ b/src/kodo/forward_linear_block_decoder.hpp @@ -0,0 +1,40 @@ +// Copyright Steinwurf ApS 2011-2012. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +namespace kodo +{ + + /// @ingroup codec_layers + /// @brief Implements basic linear block decoder. + /// + /// The linear block decoder + /// expects that an encoded symbol is described by a vector of + /// coefficients. Using these coefficients the block decoder subtracts + /// incoming symbols until the original data has been recreated. + template + class forward_linear_block_decoder : + public bidirectional_linear_block_decoder< + forward_linear_block_decoder_policy, SuperCoder> + { }; + +} + diff --git a/src/kodo/forward_linear_block_decoder_policy.hpp b/src/kodo/forward_linear_block_decoder_policy.hpp new file mode 100644 index 00000000..bd6b6251 --- /dev/null +++ b/src/kodo/forward_linear_block_decoder_policy.hpp @@ -0,0 +1,89 @@ +// Copyright Steinwurf ApS 2011-2012. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include +#include + +namespace kodo +{ + + /// Policy object for determining the "direction" of the + /// linear_block_decoder e.g. whether we will look for the + /// first pivot from the beginning of the coefficients vector + /// or in some other way. + /// + /// The forward_linear_block decoder will search for pivots from + /// the left of the encoding vector. + /// E.g. if you see an encoding vector like: + /// + /// +-----------> direction of search for pivots + /// + /// 0 1 0 1 1 0 0 + /// ^ ^ ^ + /// | | | + /// | | +----+ Last coefficient searched + /// | +--------------+ First non zero coefficient + /// +----------------+ First coefficient searched + /// + struct forward_linear_block_decoder_policy + { + /// Construct the policy with the desired start and stop interval + /// but ends included so [start:stop] + /// @param start The first element to look at + /// @param stop The last element to look at + forward_linear_block_decoder_policy(uint32_t start, uint32_t stop) + : m_start(start), + m_stop(stop) + { + assert(m_start <= m_stop); + } + + /// @return true if the policy is at the end + bool at_end() const + { + return m_start > m_stop; + } + + /// Advance the policy to the next index + void advance() + { + assert(!at_end()); + ++m_start; + } + + /// @return the current index + uint32_t index() const + { + return m_start; + } + + /// @param a The first value + /// @param b The second value + /// @return the maximum value of the two input values + static uint32_t max(uint32_t a, uint32_t b) + { + return std::max(a,b); + } + + /// @param a The first value + /// @param b The second value + /// @return the minimum value of the two input values + static uint32_t min(uint32_t a, uint32_t b) + { + return std::min(a,b); + } + + /// The start value + uint32_t m_start; + + /// The end value + uint32_t m_stop; + + }; + +} + diff --git a/src/kodo/has_debug_cached_symbol_decoder.hpp b/src/kodo/has_debug_cached_symbol_decoder.hpp new file mode 100644 index 00000000..352d9e3e --- /dev/null +++ b/src/kodo/has_debug_cached_symbol_decoder.hpp @@ -0,0 +1,39 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include "debug_cached_symbol_decoder.hpp" + +namespace kodo +{ + + /// @ingroup type_traits + /// Type trait helper allows compile time detection of whether a + /// decoder contains the debug_cached_symbol_decoder + /// + /// Example: + /// + /// typedef kodo::full_rlnc8_decoder decoder_t; + /// + /// if(kodo::has_debug_cached_symbol_decoder::value) + /// { + /// // Do something here + /// } + /// + template + struct has_debug_cached_symbol_decoder + { + template + static uint8_t test(const kodo::debug_cached_symbol_decoder *); + + static uint32_t test(...); + + static const bool value = sizeof(test(static_cast(0))) == 1; + }; + +} + + diff --git a/src/kodo/has_debug_linear_block_decoder.hpp b/src/kodo/has_debug_linear_block_decoder.hpp new file mode 100644 index 00000000..31004523 --- /dev/null +++ b/src/kodo/has_debug_linear_block_decoder.hpp @@ -0,0 +1,39 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include "debug_linear_block_decoder.hpp" + +namespace kodo +{ + + /// @ingroup type_traits + /// Type trait helper allows compile time detection of whether a + /// decoder contains the debug_linear_block_decoder + /// + /// Example: + /// + /// typedef kodo::full_rlnc8_decoder decoder_t; + /// + /// if(kodo::has_debug_linear_block_decoder::value) + /// { + /// // Do something here + /// } + /// + template + struct has_debug_linear_block_decoder + { + template + static uint8_t test(const kodo::debug_linear_block_decoder *); + + static uint32_t test(...); + + static const bool value = sizeof(test(static_cast(0))) == 1; + }; + +} + + diff --git a/src/kodo/has_deep_symbol_storage.hpp b/src/kodo/has_deep_symbol_storage.hpp index 62492228..45673743 100644 --- a/src/kodo/has_deep_symbol_storage.hpp +++ b/src/kodo/has_deep_symbol_storage.hpp @@ -9,6 +9,8 @@ namespace kodo { + + /// @ingroup type_traits /// Type trait helper allows compile time detection of whether an /// encoder / decoder contains the deep_symbol_storage layer /// @@ -32,7 +34,6 @@ namespace kodo static const bool value = sizeof(test(static_cast(0))) == 1; }; - } diff --git a/src/kodo/has_partial_decoding_tracker.hpp b/src/kodo/has_partial_decoding_tracker.hpp new file mode 100644 index 00000000..9d09eef2 --- /dev/null +++ b/src/kodo/has_partial_decoding_tracker.hpp @@ -0,0 +1,39 @@ +// Copyright Steinwurf ApS 2011-2012. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include "partial_decoding_tracker.hpp" + +namespace kodo +{ + + /// @ingroup type_traits + /// Type trait helper allows compile time detection of whether an + /// encoder / decoder contains the partial_decoding_tracker + /// + /// Example: + /// + /// typedef kodo::full_rlnc8_decoder decoder_t; + /// + /// if(kodo::has_partial_decoding_tracker::value) + /// { + /// // Do something here + /// } + /// + template + struct has_partial_decoding_tracker + { + template + static uint8_t test(const kodo::partial_decoding_tracker *); + + static uint32_t test(...); + + static const bool value = sizeof(test(static_cast(0))) == 1; + }; + +} + + diff --git a/src/kodo/has_print_cached_symbol_coefficients.hpp b/src/kodo/has_print_cached_symbol_coefficients.hpp new file mode 100644 index 00000000..a1e3ccdf --- /dev/null +++ b/src/kodo/has_print_cached_symbol_coefficients.hpp @@ -0,0 +1,55 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include +#include "debug_cached_symbol_decoder.hpp" + +namespace kodo +{ + + /// @ingroup type_traits + /// Type trait helper allows compile time detection of whether a + /// decoder has the layer::print_cached_symbol_coefficients(std::ostream&) + /// + /// Example: + /// + /// typedef kodo::full_rlnc8_decoder decoder_t; + /// + /// if(kodo::has_print_cached_symbol_coefficients::value) + /// { + /// // Do something here + /// } + /// + template + class has_print_cached_symbol_coefficients + { + class yes { char m; }; + class no { yes m[2]; }; + + struct base_mixin + { + void print_cached_symbol_coefficients(std::ostream&); + }; + + struct base : public T, public base_mixin {}; + template class helper{}; + + template + static no deduce(U*, helper* = 0); + + static yes deduce(...); + + public: + + static const bool value = + sizeof(yes) == sizeof(deduce(static_cast(0))); + }; + +} + + diff --git a/src/kodo/has_print_cached_symbol_data.hpp b/src/kodo/has_print_cached_symbol_data.hpp new file mode 100644 index 00000000..743a41dd --- /dev/null +++ b/src/kodo/has_print_cached_symbol_data.hpp @@ -0,0 +1,55 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include +#include "debug_cached_symbol_decoder.hpp" + +namespace kodo +{ + + /// @ingroup type_traits + /// Type trait helper allows compile time detection of whether a + /// decoder has the layer::print_cached_symbol_data(std::ostream&) + /// + /// Example: + /// + /// typedef kodo::full_rlnc8_decoder decoder_t; + /// + /// if(kodo::has_print_cached_symbol_data::value) + /// { + /// // Do something here + /// } + /// + template + class has_print_cached_symbol_data + { + class yes { char m; }; + class no { yes m[2]; }; + + struct base_mixin + { + void print_cached_symbol_data(std::ostream&); + }; + + struct base : public T, public base_mixin {}; + template class helper{}; + + template + static no deduce(U*, helper* = 0); + + static yes deduce(...); + + public: + + static const bool value = + sizeof(yes) == sizeof(deduce(static_cast(0))); + }; + +} + + diff --git a/src/kodo/has_shallow_symbol_storage.hpp b/src/kodo/has_shallow_symbol_storage.hpp index a62b3404..40234271 100644 --- a/src/kodo/has_shallow_symbol_storage.hpp +++ b/src/kodo/has_shallow_symbol_storage.hpp @@ -10,6 +10,7 @@ namespace kodo { + /// @ingroup type_traits /// Type trait helper allows compile time detection of whether an /// encoder / decoder contains the shallow_symbol_storage layer /// diff --git a/src/kodo/is_partial_complete.hpp b/src/kodo/is_partial_complete.hpp new file mode 100644 index 00000000..ead7e471 --- /dev/null +++ b/src/kodo/is_partial_complete.hpp @@ -0,0 +1,59 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include "partial_decoding_tracker.hpp" +#include "has_partial_decoding_tracker.hpp" + +namespace kodo +{ + + /// @ingroup generic_api + /// Generic function overload for cases where is_partial_complete is part + /// of a stack. @see partial_decoding_tracker::is_partial_complete() const + /// @param t The stack to query + template + inline bool is_partial_complete(const T& t) + { + return is_partial_complete::value>(t); + } + + /// @ingroup generic_api + /// @copydoc is_partial_complete(const T&) + template + inline bool is_partial_complete(const boost::shared_ptr& t) + { + return is_partial_complete(*t); + } + + /// @ingroup generic_api + /// @copydoc is_partial_complete(const T&) + template + inline bool is_partial_complete(const T& t, char (*)[what] = 0) + { + // Explanation for the char (*)[what] here: + // http://stackoverflow.com/a/6917354/1717320 + return t.is_partial_complete(); + } + + /// @ingroup generic_api + /// @copydoc is_partial_complete(const T&) + template + inline bool is_partial_complete(const T& t, char (*)[!what] = 0) + { + (void)t; + + // We do the assert here - to make sure that this call is not + // silently ignored in cases where the stack does not have the + // is_partial_complete() function. However, this assert can + // be avoided by using the has_partial_decoding_tracker + assert(0); + return false; + } + +} + + diff --git a/src/kodo/largest_nonzero_index_decoder.hpp b/src/kodo/largest_nonzero_index_decoder.hpp new file mode 100644 index 00000000..14d02de8 --- /dev/null +++ b/src/kodo/largest_nonzero_index_decoder.hpp @@ -0,0 +1,198 @@ +// Copyright Steinwurf ApS 2011-2012. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +namespace kodo +{ + + /// @ingroup codec_layers + /// @brief This layer can be added to a decoding stack to monitor the + /// coding coefficients passed to a decoder. This specific layer + /// tracks the largest index where it have seen non-zero coefficients. + /// One use-case of this is to monitor when partial decoding is + /// possible i.e. when symbols in decoder are fully decoded even + /// though the full data block has not been sent. + /// + /// Keeping track when partial decoding is possible can be done + /// in several ways. This layer will "monitor" the incoming + /// coding coefficients, using the information stored in the + /// it is possible to detect cases where all symbols are fully + /// decoded. + /// + /// Imagine we have observed the following two coding vectors: + /// + /// 1: 1 0 0 0 + /// 2: 0 1 1 0 + /// ^ + /// | + /// +----+ Last non-zero position + /// + /// This matrix has a rank of 2 but since the last non-zero coefficient + /// is found at index 2 we cannot yet decode (packet two is still coded). + /// + /// However if we receive on more packet such that the matrix becomes e.g. + /// + /// 1: 1 0 0 0 + /// 2: 0 1 1 0 + /// 3: 1 0 1 0 + /// ^ + /// | + /// +----+ Last non-zero position + /// + /// We see that the matrix has rank 3 and the last non-zero coefficient is + /// still at index 2. We therefore must be able to decode the three packets. + /// + /// One word of caution, this does of course require that the decoder used + /// will always decode all packets to the degree possible. Some decoders + /// such as the linear_block_decoder_delayed will not perform backward + /// substitution until the matrix reaches full rank. That type of decoder + /// are therefore not compatible with this layer. + template + class largest_nonzero_index_decoder : public SuperCoder + { + public: + + /// @copydoc layer::field_type + typedef typename SuperCoder::field_type field_type; + + /// @copydoc layer::value_type + typedef typename SuperCoder::value_type value_type; + + /// @copydoc layer::value_type + typedef typename SuperCoder::rank_type rank_type; + + /// @copydoc layer::factory + typedef typename SuperCoder::factory factory; + + public: + + /// Constructor + largest_nonzero_index_decoder() + : m_largest_nonzero_index(0) + { } + + /// @copydoc layer::initialize(Factory&) + template + void initialize(Factory& the_factory) + { + SuperCoder::initialize(the_factory); + + m_nonzero_seen = false; + m_largest_nonzero_index = 0; + } + + /// @copydoc layer::decode_symbol(uint8_t*,uint8_t*) + void decode_symbol(uint8_t *symbol_data, + uint8_t *symbol_coefficients) + { + assert(symbol_data != 0); + assert(symbol_coefficients != 0); + + // Inspect the coding coefficients to find the maximum + // position where we have a non-zero value + value_type *coefficients + = reinterpret_cast(symbol_coefficients); + + // We inspect the coefficients from the back i.e. if we + // are given the following coding vector: + // + // 23 0 44 213 0 231 0 + // ^ + // | + // +-----+ max position of non-zero + // coefficient + // + // Starting from the back will in many cases e.g. for uniform + // coding vectors be better, since we are looking for the last + // non-zero element. + assert(SuperCoder::symbols() > m_largest_nonzero_index); + + for (uint32_t i = SuperCoder::symbols(); + i --> m_largest_nonzero_index;) + { + value_type current_coefficient + = fifi::get_value(coefficients, i); + + if (current_coefficient) + { + m_nonzero_seen = true; + m_largest_nonzero_index = i; + break; + } + } + + SuperCoder::decode_symbol(symbol_data, symbol_coefficients); + } + + /// @copydoc layer::decode_symbol(uint8_t*, uint32_t) + void decode_symbol(uint8_t *symbol_data, + uint32_t symbol_index) + { + assert(symbol_index < SuperCoder::symbols()); + assert(symbol_data != 0); + + m_largest_nonzero_index = + std::max(symbol_index, m_largest_nonzero_index); + + m_nonzero_seen = true; + + SuperCoder::decode_symbol(symbol_data, symbol_index); + } + + /// @return The rank of the encoder predicted by inspecting the + /// coding coefficients + rank_type encoder_rank() const + { + if(!m_nonzero_seen) + { + assert(m_largest_nonzero_index == 0U); + return m_largest_nonzero_index; + } + else + { + // If the largest index we have seen non-zero for is 0 then + // rank must be 1 and so forth. + return m_largest_nonzero_index + 1; + } + } + + /// @return The position of the last seen non-zero coefficients + rank_type largest_nonzero_index() const + { + assert(m_nonzero_seen); + return m_largest_nonzero_index; + } + + /// @return True is a non-zero coefficient has been seen + bool nonzero_index_seen() const + { + return m_nonzero_seen; + } + + protected: + + /// Tracks whether a non-zero coefficient has been seen + bool m_nonzero_seen; + + /// The largest index seen with a nonzero coefficient + rank_type m_largest_nonzero_index; + + }; + +} + diff --git a/src/kodo/linear_block_decoder_delayed.hpp b/src/kodo/linear_block_decoder_delayed.hpp index da5cf7ed..cf99ec62 100644 --- a/src/kodo/linear_block_decoder_delayed.hpp +++ b/src/kodo/linear_block_decoder_delayed.hpp @@ -33,6 +33,9 @@ namespace kodo /// The value_type used to store the field elements typedef typename field_type::value_type value_type; + /// + typedef typename SuperCoder::direction_policy direction_policy; + public: /// @copydoc layer::decode_symbol(uint8_t*,uint8_t*) @@ -77,10 +80,8 @@ namespace kodo m_uncoded[ symbol_index ] = true; - if(symbol_index > m_maximum_pivot) - { - m_maximum_pivot = symbol_index; - } + m_maximum_pivot = + direction_policy::max(symbol_index, m_maximum_pivot); } @@ -136,10 +137,8 @@ namespace kodo m_coded[ *pivot_index ] = true; - if(*pivot_index > m_maximum_pivot) - { - m_maximum_pivot = *pivot_index; - } + m_maximum_pivot = + direction_policy::max(*pivot_index, m_maximum_pivot); if(SuperCoder::is_complete()) { @@ -156,10 +155,13 @@ namespace kodo { assert(SuperCoder::is_complete()); - uint32_t symbols = SuperCoder::symbols(); + uint32_t start = direction_policy::min(0, SuperCoder::symbols()-1); + uint32_t end = direction_policy::max(0, SuperCoder::symbols()-1); - for(uint32_t i = symbols; i --> 0;) + for(direction_policy p(start, end); !p.at_end(); p.advance()) { + uint32_t i = p.index(); + value_type *symbol_i = SuperCoder::symbol_value(i); diff --git a/src/kodo/nocode/carousel_codes.hpp b/src/kodo/nocode/carousel_codes.hpp index 4ebec00a..e073756c 100644 --- a/src/kodo/nocode/carousel_codes.hpp +++ b/src/kodo/nocode/carousel_codes.hpp @@ -9,7 +9,7 @@ #include "../payload_decoder.hpp" #include "../zero_symbol_encoder.hpp" #include "../linear_block_encoder.hpp" -#include "../linear_block_decoder.hpp" +#include "../forward_linear_block_decoder.hpp" #include "../deep_symbol_storage.hpp" #include "../final_coder_factory_pool.hpp" #include "../finite_field_info.hpp" diff --git a/src/kodo/partial_decoding_tracker.hpp b/src/kodo/partial_decoding_tracker.hpp new file mode 100644 index 00000000..b513351f --- /dev/null +++ b/src/kodo/partial_decoding_tracker.hpp @@ -0,0 +1,67 @@ +// Copyright Steinwurf ApS 2011-2012. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +namespace kodo +{ + + /// @ingroup payload_codec + /// @brief This layer can be added to a decoding stack to track + /// when partial decoding is possible i.e. when symbols in + /// decoder are fully decoded even though the full data + /// block has not been sent. + /// + /// Detecting whether data has been partially decoded can be done by + /// tracking whether encoder and decoder has the same rank. This of course + /// means that we depend on the decoder performs full Gaussian elimination + /// on the incoming symbols. + /// + /// To figure out which symbols have been partially decoded the decoder's + /// layer::symbol_pivot(uint32_t) const function can be used. + template + class partial_decoding_tracker : public SuperCoder + { + public: + + /// @copydoc layer::rank_type + typedef typename SuperCoder::rank_type rank_type; + + public: + + /// @return True if the decoding matrix should be partially decoded. + bool is_partial_complete() const + { + rank_type decoder_rank = SuperCoder::rank(); + rank_type encoder_rank = SuperCoder::encoder_rank(); + + assert(decoder_rank <= encoder_rank); + + if(decoder_rank == 0) + { + return false; + } + else + { + return SuperCoder::rank() == SuperCoder::encoder_rank(); + } + } + + }; + +} + diff --git a/src/kodo/payload_rank_decoder.hpp b/src/kodo/payload_rank_decoder.hpp new file mode 100644 index 00000000..8a5b1fe9 --- /dev/null +++ b/src/kodo/payload_rank_decoder.hpp @@ -0,0 +1,111 @@ +// Copyright Steinwurf ApS 2011-2012. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include +#include + +namespace kodo +{ + + /// @ingroup payload_codec_layers + /// @brief The payload decoder splits the payload buffer into + /// symbol header and symbol. + template + class payload_rank_decoder : public SuperCoder + { + public: + + /// @copydoc layer::rank_type + typedef typename SuperCoder::rank_type rank_type; + + public: + + /// The factory layer associated with this coder. + /// In this case only needed to provide the max_payload_size() + /// function. + class factory : public SuperCoder::factory + { + public: + + /// @copydoc layer::factory::factory(uint32_t,uint32_t) + factory(uint32_t max_symbols, uint32_t max_symbol_size) + : SuperCoder::factory(max_symbols, max_symbol_size) + { } + + /// @copydoc layer::factory::max_payload_size() const + uint32_t max_payload_size() const + { + return SuperCoder::factory::max_payload_size() + + sizeof(rank_type); + } + }; + + public: + + /// @copydoc layer::initialize(Factory&) + template + void initialize(Factory& the_factory) + { + SuperCoder::initialize(the_factory); + + /// Reset the state + m_encoder_rank = 0; + } + + /// Unpacks the symbol data and symbol header from the payload + /// buffer. + /// @copydoc layer::decode(uint8_t*) + void decode(uint8_t* payload) + { + assert(payload != 0); + + uint32_t read = read_rank(payload); + SuperCoder::decode(payload + read); + } + + /// Reads the rank of the encoder from the payload buffer + /// @param payload The payload buffer + /// @return The amount of bytes read + uint32_t read_rank(uint8_t* payload) + { + assert(payload != 0); + + // Write the encoder rank to the payload + rank_type encoder_rank = + sak::big_endian::get(payload); + + // We should never see an encoder which reduces + // its rank + assert(m_encoder_rank <= encoder_rank); + m_encoder_rank = encoder_rank; + + return sizeof(rank_type); + } + + /// @copydoc layer::payload_size() const + uint32_t payload_size() const + { + return SuperCoder::payload_size() + + sizeof(rank_type); + } + + /// @return The rank of the encoder as read from the packet + rank_type encoder_rank() const + { + return m_encoder_rank; + } + + private: + + /// Stores the read encoder rank + rank_type m_encoder_rank; + + }; + +} + + diff --git a/src/kodo/payload_rank_encoder.hpp b/src/kodo/payload_rank_encoder.hpp new file mode 100644 index 00000000..4a8650c5 --- /dev/null +++ b/src/kodo/payload_rank_encoder.hpp @@ -0,0 +1,97 @@ +// Copyright Steinwurf ApS 2011-2012. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include +#include + +namespace kodo +{ + + /// @ingroup payload_codec_layers + /// @brief The payload rank encoder writes the rank of the encoder into + /// the payload buffer. + /// + /// Storing the rank of the encoder in the payload buffer allows a decoder + /// at a receiver to detect early decoding opportunities. This is possible + /// if the receiver detects that it's decoder has the same rank as the + /// encoder from which it is receiving packets. Note, that the rank of an + /// encoder means how many packets that encoder has available for coding. + /// So if the decoder has decoded as many packets as were available then + /// we might be able to access the packets even before reaching full rank + /// at the decoder. The reason we say "might" is that if the decoder is not + /// a Gaussian Elimination decoder (which also does backward substitution) + /// immediately then symbols might not yet be fully decoded. So you have to + /// make sure the decoder used performs both forward and backward + /// substitution on every symbols as it is being received to use this + /// feature. + template + class payload_rank_encoder : public SuperCoder + { + public: + + /// @copydoc layer::rank_type + typedef typename SuperCoder::rank_type rank_type; + + public: + + /// The factory layer associated with this coder. + /// In this case only needed to provide the max_payload_size() + /// function. + class factory : public SuperCoder::factory + { + public: + + /// @copydoc layer::factory::factory(uint32_t,uint32_t) + factory(uint32_t max_symbols, uint32_t max_symbol_size) + : SuperCoder::factory(max_symbols, max_symbol_size) + { } + + /// @copydoc layer::factory::max_payload_size() const + uint32_t max_payload_size() const + { + return SuperCoder::factory::max_payload_size() + + sizeof(rank_type); + } + + }; + + public: + + /// @copydoc layer::encode(uint8_t*) + uint32_t encode(uint8_t* payload) + { + assert(payload != 0); + + // Write the encoder rank to the payload + uint32_t written = write_rank(payload); + return SuperCoder::encode(payload + written) + written; + } + + /// Helper function which writes the rank of the encoder into + /// the payload buffer + /// @param payload The buffer where the rank should be written + /// @return The number of bytes written to the payload + uint32_t write_rank(uint8_t* payload) + { + assert(payload != 0); + + // Write the encoder rank to the payload + sak::big_endian::put(SuperCoder::rank(), payload); + + return sizeof(rank_type); + } + + /// @copydoc layer::payload_size() const + uint32_t payload_size() const + { + return SuperCoder::payload_size() + sizeof(rank_type); + } + + }; + +} + diff --git a/src/kodo/payload_recoder.hpp b/src/kodo/payload_recoder.hpp index 6c685e78..74367ce1 100644 --- a/src/kodo/payload_recoder.hpp +++ b/src/kodo/payload_recoder.hpp @@ -105,10 +105,10 @@ namespace kodo } /// @copydoc layer::recode(uint8_t*) - void recode(uint8_t *payload) + uint32_t recode(uint8_t *payload) { assert(m_recode_stack); - m_recode_stack->encode(payload); + return m_recode_stack->encode(payload); } /// Make sure we have enough space for both the payload diff --git a/src/kodo/print_cached_symbol_coefficients.hpp b/src/kodo/print_cached_symbol_coefficients.hpp new file mode 100644 index 00000000..f69a7c64 --- /dev/null +++ b/src/kodo/print_cached_symbol_coefficients.hpp @@ -0,0 +1,64 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include + +#include "has_print_cached_symbol_coefficients.hpp" + +namespace kodo +{ + + /// @ingroup generic_api + /// Generic function overload for cases where print_decoder_state is part + /// of a stack. @see layer::print_cached_symbol_coefficients(std::ostream&) const + /// @param t The stack to query + /// @param out The output stream where we will write the debug info + template + inline void print_cached_symbol_coefficients(const T& t, std::ostream& out) + { + print_cached_symbol_coefficients::value>(t,out); + } + + /// @ingroup generic_api + /// @copydoc print_cached_symbol_coefficients(const T&, std::ostream&) + template + inline void print_cached_symbol_coefficients(const boost::shared_ptr& t, + std::ostream& out) + { + print_cached_symbol_coefficients(*t, out); + } + + /// @ingroup generic_api + /// @copydoc print_cached_symbol_coefficients(const T&) + template + inline void print_cached_symbol_coefficients(const T& t, std::ostream& out, + char (*)[what] = 0) + { + // Explanation for the char (*)[what] here: + // http://stackoverflow.com/a/6917354/1717320 + t.print_cached_symbol_coefficients(out); + } + + /// @ingroup generic_api + /// @copydoc print_cached_symbol_coefficients(const T&) + template + inline void print_cached_symbol_coefficients(const T& t, std::ostream& out, + char (*)[!what] = 0) + { + (void) t; + (void) out; + + // We do the assert here - to make sure that this call is not + // silently ignored in cases where the stack does not have the + // print_cached_symbol_coefficients() function. However, this assert can + // be avoided by using the has_print_cached_symbol_coefficients + assert(0); + } + +} + + diff --git a/src/kodo/print_cached_symbol_data.hpp b/src/kodo/print_cached_symbol_data.hpp new file mode 100644 index 00000000..79e5d554 --- /dev/null +++ b/src/kodo/print_cached_symbol_data.hpp @@ -0,0 +1,64 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include + +#include "has_print_cached_symbol_data.hpp" + +namespace kodo +{ + + /// @ingroup generic_api + /// Generic function overload for cases where print_decoder_state is part + /// of a stack. @see layer::print_cached_symbol_data(std::ostream&) const + /// @param t The stack to query + /// @param out The output stream where we will write the debug info + template + inline void print_cached_symbol_data(const T& t, std::ostream& out) + { + print_cached_symbol_data::value>(t,out); + } + + /// @ingroup generic_api + /// @copydoc print_cached_symbol_data(const T&, std::ostream&) + template + inline void print_cached_symbol_data(const boost::shared_ptr& t, + std::ostream& out) + { + print_cached_symbol_data(*t, out); + } + + /// @ingroup generic_api + /// @copydoc print_cached_symbol_data(const T&) + template + inline void print_cached_symbol_data(const T& t, std::ostream& out, + char (*)[what] = 0) + { + // Explanation for the char (*)[what] here: + // http://stackoverflow.com/a/6917354/1717320 + t.print_cached_symbol_data(out); + } + + /// @ingroup generic_api + /// @copydoc print_cached_symbol_data(const T&) + template + inline void print_cached_symbol_data(const T& t, std::ostream& out, + char (*)[!what] = 0) + { + (void) t; + (void) out; + + // We do the assert here - to make sure that this call is not + // silently ignored in cases where the stack does not have the + // print_cached_symbol_data() function. However, this assert can + // be avoided by using the has_print_cached_symbol_data + assert(0); + } + +} + + diff --git a/src/kodo/print_decoder_state.hpp b/src/kodo/print_decoder_state.hpp new file mode 100644 index 00000000..0fcf3ee3 --- /dev/null +++ b/src/kodo/print_decoder_state.hpp @@ -0,0 +1,65 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include + +#include "debug_linear_block_decoder.hpp" +#include "has_debug_linear_block_decoder.hpp" + +namespace kodo +{ + + /// @ingroup generic_api + /// Generic function overload for cases where print_decoder_state is part + /// of a stack. @see debug_linear_block_decoder::print_decoder_state() const + /// @param t The stack to query + /// @param out The output stream where we will write the debug info + template + inline void print_decoder_state(const T& t, std::ostream& out) + { + print_decoder_state::value>(t,out); + } + + /// @ingroup generic_api + /// @copydoc print_decoder_state(const T&, std::ostream&) + template + inline void print_decoder_state(const boost::shared_ptr& t, + std::ostream& out) + { + print_decoder_state(*t, out); + } + + /// @ingroup generic_api + /// @copydoc print_decoder_state(const T&) + template + inline void print_decoder_state(const T& t, std::ostream& out, + char (*)[what] = 0) + { + // Explanation for the char (*)[what] here: + // http://stackoverflow.com/a/6917354/1717320 + t.print_decoder_state(out); + } + + /// @ingroup generic_api + /// @copydoc print_decoder_state(const T&) + template + inline void print_decoder_state(const T& t, std::ostream& out, + char (*)[!what] = 0) + { + (void) t; + (void) out; + + // We do the assert here - to make sure that this call is not + // silently ignored in cases where the stack does not have the + // print_decoder_state() function. However, this assert can + // be avoided by using the has_debug_linear_block_decoder + assert(0); + } + +} + + diff --git a/src/kodo/random_annex_encoder.hpp b/src/kodo/random_annex_encoder.hpp index 4fa88dc0..3b618418 100644 --- a/src/kodo/random_annex_encoder.hpp +++ b/src/kodo/random_annex_encoder.hpp @@ -126,7 +126,14 @@ namespace kodo storage.m_data = m_object.m_data + offset; storage.m_size = size; - encoder->set_symbols(storage); + auto symbol_sequence = sak::split_storage( + storage, encoder->symbol_size()); + + uint32_t sequence_size = symbol_sequence.size(); + for(uint32_t i = 0; i < sequence_size; ++i) + { + encoder->set_symbol(i, symbol_sequence[i]); + } // We require that encoders includes the has_bytes_used // layer to support partially filled encoders diff --git a/src/kodo/rank_info.hpp b/src/kodo/rank_info.hpp new file mode 100644 index 00000000..fc6a40f0 --- /dev/null +++ b/src/kodo/rank_info.hpp @@ -0,0 +1,46 @@ +// Copyright Steinwurf ApS 2011-2012. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include +#include + +namespace kodo +{ + + /// @ingroup codec_layers + /// @brief provides the layer::rank_type typedef + /// + /// The data type that is used to store the rank of an encoder of + /// decoder. The rank of an encoder refers to the number of + /// symbols available to the encoder for encoding and the rank of + /// the decoder refers to the number of pivot elements / partially + /// decoded symbols currently seen. + template + class rank_info : public SuperCoder + { + public: + + /// @copydoc layer::rank_type + typedef uint32_t rank_type; + + public: + + /// @copydoc layer::construct(Factory&) + template + void construct(Factory& the_factory) + { + SuperCoder::construct(the_factory); + + assert(std::numeric_limits::max() >= + the_factory.max_symbols()); + } + + }; + +} + + diff --git a/src/kodo/rlnc/full_vector_codes.hpp b/src/kodo/rlnc/full_vector_codes.hpp index 1b6ebfa1..a3134bdc 100644 --- a/src/kodo/rlnc/full_vector_codes.hpp +++ b/src/kodo/rlnc/full_vector_codes.hpp @@ -3,8 +3,7 @@ // See accompanying file LICENSE.rst or // http://www.steinwurf.com/licensing -#ifndef KODO_RLNC_FULL_VECTOR_CODES_HPP -#define KODO_RLNC_FULL_VECTOR_CODES_HPP +#pragma once #include @@ -35,9 +34,12 @@ #include "../proxy_layer.hpp" #include "../storage_aware_encoder.hpp" #include "../encode_symbol_tracker.hpp" +#include "../cached_symbol_decoder.hpp" +#include "../debug_cached_symbol_decoder.hpp" +#include "../debug_linear_block_decoder.hpp" #include "../linear_block_encoder.hpp" -#include "../linear_block_decoder.hpp" +#include "../forward_linear_block_decoder.hpp" #include "../linear_block_decoder_delayed.hpp" namespace kodo @@ -134,7 +136,7 @@ namespace kodo plain_symbol_id_reader< // Codec API aligned_coefficients_decoder< - linear_block_decoder< + forward_linear_block_decoder< // Coefficient Storage API coefficient_storage< coefficient_info< @@ -152,7 +154,44 @@ namespace kodo > > > > > > > > > > > > > > > { }; + /// @ingroup fec_stacks + /// @brief Implementation of a full_rlnc_decoder, but with the debug + /// layers added. + /// + /// @copydoc full_rlnc_decoder + template + class debug_full_rlnc_decoder + : public // Payload API + payload_recoder::type, + finite_field_info + > > > > > > > > > > > > > > > > > > + { }; + } -#endif diff --git a/src/kodo/rlnc/on_the_fly_codes.hpp b/src/kodo/rlnc/on_the_fly_codes.hpp new file mode 100644 index 00000000..d3f557f4 --- /dev/null +++ b/src/kodo/rlnc/on_the_fly_codes.hpp @@ -0,0 +1,185 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include + +#include "full_vector_codes.hpp" +#include "../storage_aware_generator.hpp" +#include "../partial_decoding_tracker.hpp" +#include "../rank_info.hpp" +#include "../payload_rank_encoder.hpp" +#include "../payload_rank_decoder.hpp" + +namespace kodo +{ + + /// @ingroup fec_stacks + /// @brief Complete stack implementing a full RLNC on-the-fly encoder. + /// + /// The on-the-fly encoder has the advantage that symbols can be specified + /// as they arrive at the encoder. This breaks with a traditional block + /// code where all the data has to be available before encoding can start. + /// + /// Implementation of on the fly RLNC encoder uses a storage aware generator + /// and storage aware encoder. The storage aware generator makes sure that + /// we do not generate non-zero coefficients for the missing symbols, the + /// storage aware encoder provides the generator with information about how + /// many symbols have been specified. + template + class on_the_fly_encoder : + public // Payload Codec API + payload_rank_encoder< + payload_encoder< + // Codec Header API + systematic_encoder< + symbol_id_encoder< + // Symbol ID API + plain_symbol_id_writer< + // Coefficient Generator API + storage_aware_generator< + uniform_generator< + // Codec API + encode_symbol_tracker< + zero_symbol_encoder< + linear_block_encoder< + storage_aware_encoder< + rank_info< + // Coefficient Storage API + coefficient_info< + // Symbol Storage API + deep_symbol_storage< + storage_bytes_used< + storage_block_info< + // Finite Field API + finite_field_math::type, + finite_field_info + > > > > > > > > > > > > > > > > > > > + { }; + + /// Intermediate stack implementing the recoding functionality of a + /// RLNC code. As can be seen we are able to reuse a great deal of + /// layers from the encode stack. It is important that the symbols + /// produced by the recoder are compatible with the decoder. This + /// means we have to use compatible Payload, Codec Header Symbol ID + /// layers, between the encoder, recoder and decoder. + /// The only layer specific to recoding is the recoding_symbol_id + /// layer. Finally the recoder uses a proxy_layer which forwards + /// any calls not implemented in the recoding stack to the MainStack. + template + class on_the_fly_recoding_stack + : public // Payload API + payload_rank_encoder< + payload_encoder< + // Codec Header API + non_systematic_encoder< + symbol_id_encoder< + // Symbol ID API + recoding_symbol_id< + // Coefficient Generator API + uniform_generator< + // Codec API + encode_symbol_tracker< + zero_symbol_encoder< + linear_block_encoder< + rank_info< + // Proxy + proxy_layer< + on_the_fly_recoding_stack, + MainStack> > > > > > > > > > > + { }; + + /// @ingroup fec_stacks + /// @brief Implementation of a complete RLNC decoder + /// + /// This configuration adds the following features (including those + /// described for the encoder): + /// - Recoding using the recoding_stack + /// - Linear block decoder using Gauss-Jordan elimination. + template + class on_the_fly_decoder : + public // Payload API + partial_decoding_tracker< + payload_recoder::type, + finite_field_info + > > > > > > > > > > > > > > > > > > + { }; + + /// @ingroup fec_stacks + /// @brief Implementation of a complete RLNC decoder + /// + /// This configuration adds the following features (including those + /// described for the encoder): + /// - Recoding using the recoding_stack + /// - Linear block decoder using Gauss-Jordan elimination. + template + class debug_on_the_fly_decoder : + public // Payload API + partial_decoding_tracker< + payload_recoder::type, + finite_field_info + > > > > > > > > > > > > > > > > > > > > > + { }; + +} + + + diff --git a/src/kodo/rlnc/seed_codes.hpp b/src/kodo/rlnc/seed_codes.hpp index a6608576..39a56ccc 100644 --- a/src/kodo/rlnc/seed_codes.hpp +++ b/src/kodo/rlnc/seed_codes.hpp @@ -3,8 +3,7 @@ // See accompanying file LICENSE.rst or // http://www.steinwurf.com/licensing -#ifndef KODO_RLNC_SEED_CODES_HPP -#define KODO_RLNC_SEED_CODES_HPP +#pragma once #include @@ -38,7 +37,7 @@ #include "../encode_symbol_tracker.hpp" #include "../linear_block_encoder.hpp" -#include "../linear_block_decoder.hpp" +#include "../forward_linear_block_decoder.hpp" namespace kodo { @@ -105,7 +104,7 @@ namespace kodo uniform_generator< // Codec API aligned_coefficients_decoder< - linear_block_decoder< + forward_linear_block_decoder< // Coefficient Storage API coefficient_storage< coefficient_info< @@ -125,5 +124,4 @@ namespace kodo } -#endif diff --git a/src/kodo/rs/reed_solomon_codes.hpp b/src/kodo/rs/reed_solomon_codes.hpp index 99f2796d..927dbb83 100644 --- a/src/kodo/rs/reed_solomon_codes.hpp +++ b/src/kodo/rs/reed_solomon_codes.hpp @@ -27,7 +27,7 @@ #include "../storage_aware_encoder.hpp" #include "../encode_symbol_tracker.hpp" #include "../linear_block_encoder.hpp" -#include "../linear_block_decoder.hpp" +#include "../forward_linear_block_decoder.hpp" #include "reed_solomon_symbol_id_writer.hpp" #include "reed_solomon_symbol_id_reader.hpp" @@ -92,7 +92,7 @@ namespace kodo reed_solomon_symbol_id_reader< systematic_vandermonde_matrix< // Codec API - linear_block_decoder< + forward_linear_block_decoder< // Coefficient Storage API coefficient_storage< coefficient_info< diff --git a/src/kodo/seed_symbol_id_writer.hpp b/src/kodo/seed_symbol_id_writer.hpp index a7d3ef77..811b00be 100644 --- a/src/kodo/seed_symbol_id_writer.hpp +++ b/src/kodo/seed_symbol_id_writer.hpp @@ -3,8 +3,7 @@ // See accompanying file LICENSE.rst or // http://www.steinwurf.com/licensing -#ifndef KODO_SEED_SYMBOL_ID_WRITER_HPP -#define KODO_SEED_SYMBOL_ID_WRITER_HPP +#pragma once #include @@ -63,5 +62,4 @@ namespace kodo } -#endif diff --git a/src/kodo/shallow_symbol_storage.hpp b/src/kodo/shallow_symbol_storage.hpp index da32ad21..96f2cf76 100644 --- a/src/kodo/shallow_symbol_storage.hpp +++ b/src/kodo/shallow_symbol_storage.hpp @@ -123,6 +123,10 @@ namespace kodo assert(symbol.m_size == SuperCoder::symbol_size()); assert(index < SuperCoder::symbols()); + // Symbols should always be added in order e.g. 0,1,2,3 .. + // so the index specified must equal the current count + assert(index == m_symbols_count); + if(m_data[index] == 0) { ++m_symbols_count; diff --git a/src/kodo/sparse_uniform_generator.hpp b/src/kodo/sparse_uniform_generator.hpp index d4aee3e8..224fe169 100644 --- a/src/kodo/sparse_uniform_generator.hpp +++ b/src/kodo/sparse_uniform_generator.hpp @@ -125,10 +125,31 @@ namespace kodo void set_density(double density) { assert(density > 0); + // If binary, the density should be below 1 + assert(!fifi::is_binary::value || density < 1); m_bernoulli = boost::random::bernoulli_distribution<>(density); } + /// Set the number of nonzero symbols + /// @param symbols the number of nonzero symbols + void set_nonzero_symbols(uint32_t symbols) + { + // If binary, check that symbols are less than + // the total number of symbols + assert(!fifi::is_binary::value || + symbols < SuperCoder::symbols()); + + // If not binary, check that symbols are less than or equal the + // total number of symbols + assert(fifi::is_binary::value || + symbols <= SuperCoder::symbols()); + + assert(symbols > 0); + double new_density = (double)symbols/SuperCoder::symbols(); + set_density(new_density); + } + /// Get the density of the coefficients generated /// @return the density of the generator double get_density() const @@ -152,5 +173,4 @@ namespace kodo boost::random::mt19937 m_random_generator; }; -} - +} \ No newline at end of file diff --git a/test/src/basic_api_test_helper.hpp b/test/src/basic_api_test_helper.hpp index beffe3eb..bfef78ff 100644 --- a/test/src/basic_api_test_helper.hpp +++ b/test/src/basic_api_test_helper.hpp @@ -99,319 +99,3 @@ inline void run_test(uint32_t symbols, uint32_t symbol_size) } -template -inline void -invoke_basic_api(uint32_t symbols, uint32_t symbol_size) -{ - - // Common setting - typename Encoder::factory encoder_factory(symbols, symbol_size); - auto encoder = encoder_factory.build(); - - typename Decoder::factory decoder_factory(symbols, symbol_size); - auto decoder = decoder_factory.build(); - - EXPECT_TRUE(symbols == encoder_factory.max_symbols()); - EXPECT_TRUE(symbol_size == encoder_factory.max_symbol_size()); - EXPECT_TRUE(symbols == encoder->symbols()); - EXPECT_TRUE(symbol_size == encoder->symbol_size()); - - EXPECT_TRUE(symbols == decoder_factory.max_symbols()); - EXPECT_TRUE(symbol_size == decoder_factory.max_symbol_size()); - EXPECT_TRUE(symbols == decoder->symbols()); - EXPECT_TRUE(symbol_size == decoder->symbol_size()); - - EXPECT_TRUE(encoder->symbol_length() > 0); - EXPECT_TRUE(decoder->symbol_length() > 0); - - EXPECT_TRUE(encoder->block_size() == symbols * symbol_size); - EXPECT_TRUE(decoder->block_size() == symbols * symbol_size); - - EXPECT_TRUE(encoder_factory.max_payload_size() >= - encoder->payload_size()); - - EXPECT_TRUE(decoder_factory.max_payload_size() >= - decoder->payload_size()); - - EXPECT_EQ(encoder_factory.max_payload_size(), - decoder_factory.max_payload_size()); - - // Encode/decode operations - EXPECT_EQ(encoder->payload_size(), decoder->payload_size()); - - std::vector payload(encoder->payload_size()); - - std::vector data_in = random_vector(encoder->block_size()); - std::vector data_in_copy(data_in); - - sak::mutable_storage storage_in = sak::storage(data_in); - sak::mutable_storage storage_in_copy = sak::storage(data_in_copy); - - EXPECT_TRUE(sak::equal(storage_in, storage_in_copy)); - - // Only used for prime fields, lets reconsider how we implement - // this less intrusive - uint32_t prefix = 0; - - if(fifi::is_prime2325::value) - { - // This field only works for multiple of uint32_t - assert((encoder->block_size() % 4) == 0); - - uint32_t block_length = encoder->block_size() / 4; - - fifi::prime2325_binary_search search(block_length); - prefix = search.find_prefix(storage_in_copy); - - // Apply the negated prefix - fifi::apply_prefix(storage_in_copy, ~prefix); - } - - encoder->set_symbols(storage_in_copy); - - // Set the encoder non-systematic - if(kodo::is_systematic_encoder(encoder)) - kodo::set_systematic_off(encoder); - - while( !decoder->is_complete() ) - { - uint32_t payload_used = encoder->encode( &payload[0] ); - EXPECT_TRUE(payload_used <= encoder->payload_size()); - - decoder->decode( &payload[0] ); - } - - std::vector data_out(decoder->block_size(), '\0'); - decoder->copy_symbols(sak::storage(data_out)); - - if(fifi::is_prime2325::value) - { - // Now we have to apply the negated prefix to the decoded data - fifi::apply_prefix(sak::storage(data_out), ~prefix); - } - - EXPECT_TRUE(std::equal(data_out.begin(), - data_out.end(), - data_in.begin())); -} - -template -inline void -invoke_out_of_order_raw(uint32_t symbols, uint32_t symbol_size) -{ - // Common setting - typename Encoder::factory encoder_factory(symbols, symbol_size); - auto encoder = encoder_factory.build(); - - typename Decoder::factory decoder_factory(symbols, symbol_size); - auto decoder = decoder_factory.build(); - - // Encode/decode operations - EXPECT_TRUE(encoder->payload_size() == decoder->payload_size()); - - std::vector payload(encoder->payload_size()); - std::vector data_in = random_vector(encoder->block_size()); - - encoder->set_symbols(sak::storage(data_in)); - - if(kodo::is_systematic_encoder(encoder)) - kodo::set_systematic_off(encoder); - - while( !decoder->is_complete() ) - { - - if((rand() % 100) > 50) - { - encoder->encode( &payload[0] ); - decoder->decode( &payload[0] ); - } - else - { - uint32_t symbol_id = rand() % encoder->symbols(); - - if(decoder->symbol_pivot(symbol_id)) - { - continue; - } - - ASSERT_TRUE(encoder->symbol_size() <= payload.size()); - - encoder->copy_symbol( - symbol_id, sak::storage(payload)); - - const uint8_t *symbol_src = encoder->symbol(symbol_id); - const uint8_t *symbol_dest = &payload[0]; - - EXPECT_TRUE(std::equal(symbol_src, - symbol_src + encoder->symbol_size(), - symbol_dest)); - - - decoder->decode_symbol(&payload[0], symbol_id); - - } - } - - EXPECT_EQ(encoder->block_size(), decoder->block_size()); - - std::vector data_out(decoder->block_size(), '\0'); - decoder->copy_symbols(sak::storage(data_out)); - - EXPECT_TRUE(std::equal(data_out.begin(), - data_out.end(), - data_in.begin())); - -} - -/// Checks that the encoders and decoders are in a clean state after using -/// the initialize function. -template -inline void -invoke_initialize(uint32_t symbols, uint32_t symbol_size) -{ - - // Common setting - typename Encoder::factory encoder_factory(symbols, symbol_size); - auto encoder = encoder_factory.build(); - - typename Decoder::factory decoder_factory(symbols, symbol_size); - auto decoder = decoder_factory.build(); - - - for(uint32_t i = 0; i < 10; ++i) - { - encoder->initialize(encoder_factory); - decoder->initialize(decoder_factory); - - std::vector payload(encoder->payload_size()); - - // Ensure that the we may re-use the encoder also with partial - // data. - uint32_t block_size = rand_nonzero(encoder->block_size()); - - std::vector data_in = random_vector(block_size); - - encoder->set_symbols(sak::storage(data_in)); - - // Set the encoder non-systematic - if(kodo::is_systematic_encoder(encoder)) - kodo::set_systematic_off(encoder); - - while( !decoder->is_complete() ) - { - uint32_t payload_used = encoder->encode( &payload[0] ); - EXPECT_TRUE(payload_used <= encoder->payload_size()); - - decoder->decode( &payload[0] ); - } - - std::vector data_out(block_size, '\0'); - decoder->copy_symbols(sak::storage(data_out)); - - bool data_equal = sak::equal(sak::storage(data_out), - sak::storage(data_in)); - - ASSERT_TRUE(data_equal); - - } - -} - - - -template -inline void -invoke_systematic(uint32_t symbols, uint32_t symbol_size) -{ - - // Common setting - typename Encoder::factory encoder_factory(symbols, symbol_size); - auto encoder = encoder_factory.build(); - - typename Decoder::factory decoder_factory(symbols, symbol_size); - auto decoder = decoder_factory.build(); - - // Encode/decode operations - EXPECT_TRUE(encoder->payload_size() == decoder->payload_size()); - - std::vector payload(encoder->payload_size()); - std::vector data_in = random_vector(encoder->block_size()); - - encoder->set_symbols(sak::storage(data_in)); - - // Ensure encoder systematic - EXPECT_TRUE(kodo::is_systematic_encoder(encoder)); - kodo::set_systematic_on(encoder); - - uint32_t pkg_count = 0; - - while( !decoder->is_complete() ) - { - encoder->encode( &payload[0] ); - decoder->decode( &payload[0] ); - - ++pkg_count; - } - - EXPECT_TRUE(pkg_count == encoder->symbols()); - - std::vector data_out(decoder->block_size(), '\0'); - decoder->copy_symbols(sak::storage(data_out)); - - EXPECT_TRUE(std::equal(data_out.begin(), - data_out.end(), - data_in.begin())); - -} - -/// Tests that an encoder support progressively specifying the symbols -template -inline void -invoke_set_symbol(uint32_t symbols, uint32_t symbol_size) -{ - - // Common setting - typename Encoder::factory encoder_factory(symbols, symbol_size); - auto encoder = encoder_factory.build(); - - typename Decoder::factory decoder_factory(symbols, symbol_size); - auto decoder = decoder_factory.build(); - - std::vector payload(encoder->payload_size()); - std::vector data_in = random_vector(encoder->block_size()); - - auto symbol_sequence = sak::split_storage( - sak::storage(data_in), symbol_size); - - // Set the encoder non-systematic - if(kodo::is_systematic_encoder(encoder)) - kodo::set_systematic_off(encoder); - - EXPECT_EQ(encoder->rank(), 0U); - EXPECT_EQ(decoder->rank(), 0U); - - while( !decoder->is_complete() ) - { - encoder->encode( &payload[0] ); - decoder->decode( &payload[0] ); - - if(encoder->rank() < symbols) - { - uint32_t i = rand() % symbols; - encoder->set_symbol(i, symbol_sequence[i]); - } - } - - std::vector data_out(decoder->block_size(), '\0'); - decoder->copy_symbols(sak::storage(data_out)); - - EXPECT_TRUE(std::equal(data_out.begin(), - data_out.end(), - data_in.begin())); -} - - - - - - diff --git a/test/src/helper_test_basic_api.hpp b/test/src/helper_test_basic_api.hpp new file mode 100644 index 00000000..748ae8cb --- /dev/null +++ b/test/src/helper_test_basic_api.hpp @@ -0,0 +1,153 @@ +// Copyright Steinwurf ApS 2011-2012. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include "basic_api_test_helper.hpp" + +template +inline void test_basic_api(uint32_t symbols, uint32_t symbol_size) +{ + + // Common setting + typename Encoder::factory encoder_factory(symbols, symbol_size); + auto encoder = encoder_factory.build(); + + typename Decoder::factory decoder_factory(symbols, symbol_size); + auto decoder = decoder_factory.build(); + + EXPECT_TRUE(symbols == encoder_factory.max_symbols()); + EXPECT_TRUE(symbol_size == encoder_factory.max_symbol_size()); + EXPECT_TRUE(symbols == encoder->symbols()); + EXPECT_TRUE(symbol_size == encoder->symbol_size()); + + EXPECT_TRUE(symbols == decoder_factory.max_symbols()); + EXPECT_TRUE(symbol_size == decoder_factory.max_symbol_size()); + EXPECT_TRUE(symbols == decoder->symbols()); + EXPECT_TRUE(symbol_size == decoder->symbol_size()); + + EXPECT_TRUE(encoder->symbol_length() > 0); + EXPECT_TRUE(decoder->symbol_length() > 0); + + EXPECT_TRUE(encoder->block_size() == symbols * symbol_size); + EXPECT_TRUE(decoder->block_size() == symbols * symbol_size); + + EXPECT_TRUE(encoder_factory.max_payload_size() >= + encoder->payload_size()); + + EXPECT_TRUE(decoder_factory.max_payload_size() >= + decoder->payload_size()); + + EXPECT_EQ(encoder_factory.max_payload_size(), + decoder_factory.max_payload_size()); + + // Encode/decode operations + EXPECT_EQ(encoder->payload_size(), decoder->payload_size()); + + std::vector payload(encoder->payload_size()); + + std::vector data_in = random_vector(encoder->block_size()); + std::vector data_in_copy(data_in); + + sak::mutable_storage storage_in = sak::storage(data_in); + sak::mutable_storage storage_in_copy = sak::storage(data_in_copy); + + EXPECT_TRUE(sak::equal(storage_in, storage_in_copy)); + + // Only used for prime fields, lets reconsider how we implement + // this less intrusive + uint32_t prefix = 0; + + if(fifi::is_prime2325::value) + { + // This field only works for multiple of uint32_t + assert((encoder->block_size() % 4) == 0); + + uint32_t block_length = encoder->block_size() / 4; + + fifi::prime2325_binary_search search(block_length); + prefix = search.find_prefix(storage_in_copy); + + // Apply the negated prefix + fifi::apply_prefix(storage_in_copy, ~prefix); + } + + encoder->set_symbols(storage_in_copy); + + // Set the encoder non-systematic + if(kodo::is_systematic_encoder(encoder)) + kodo::set_systematic_off(encoder); + + while( !decoder->is_complete() ) + { + uint32_t payload_used = encoder->encode( &payload[0] ); + EXPECT_TRUE(payload_used <= encoder->payload_size()); + + decoder->decode( &payload[0] ); + } + + std::vector data_out(decoder->block_size(), '\0'); + decoder->copy_symbols(sak::storage(data_out)); + + if(fifi::is_prime2325::value) + { + // Now we have to apply the negated prefix to the decoded data + fifi::apply_prefix(sak::storage(data_out), ~prefix); + } + + EXPECT_TRUE(std::equal(data_out.begin(), + data_out.end(), + data_in.begin())); +} + +template +< + template class Encoder, + template class Decoder +> +inline void test_basic_api(uint32_t symbols, uint32_t symbol_size) +{ + test_basic_api + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_basic_api + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_basic_api + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_basic_api + < + Encoder, + Decoder + >(symbols, symbol_size); +} + +template +< + template class Encoder, + template class Decoder +> +inline void test_basic_api() +{ + + test_basic_api(32, 1600); + test_basic_api(1, 1600); + + uint32_t symbols = rand_symbols(); + uint32_t symbol_size = rand_symbol_size(); + + test_basic_api(symbols, symbol_size); +} + diff --git a/test/src/helper_test_initialize_api.hpp b/test/src/helper_test_initialize_api.hpp new file mode 100644 index 00000000..452a6463 --- /dev/null +++ b/test/src/helper_test_initialize_api.hpp @@ -0,0 +1,113 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + + +/// @file test_rlnc_on_the_fly_codes.cpp Unit tests for the full +/// vector codes (i.e. Network Coding encoders and decoders). + +#include + +#include + +/// Checks that the encoders and decoders are in a clean state after using +/// the initialize function. +template +inline void test_initialize(uint32_t symbols, uint32_t symbol_size) +{ + + // Common setting + typename Encoder::factory encoder_factory(symbols, symbol_size); + auto encoder = encoder_factory.build(); + + typename Decoder::factory decoder_factory(symbols, symbol_size); + auto decoder = decoder_factory.build(); + + + for(uint32_t i = 0; i < 3; ++i) + { + encoder->initialize(encoder_factory); + decoder->initialize(decoder_factory); + + std::vector payload(encoder->payload_size()); + + // Ensure that the we may re-use the encoder also with partial + // data. + uint32_t block_size = rand_nonzero(encoder->block_size()); + + std::vector data_in = random_vector(block_size); + + encoder->set_symbols(sak::storage(data_in)); + + // Set the encoder non-systematic + if(kodo::is_systematic_encoder(encoder)) + kodo::set_systematic_off(encoder); + + while( !decoder->is_complete() ) + { + uint32_t payload_used = encoder->encode( &payload[0] ); + EXPECT_TRUE(payload_used <= encoder->payload_size()); + + decoder->decode( &payload[0] ); + } + + std::vector data_out(block_size, '\0'); + decoder->copy_symbols(sak::storage(data_out)); + + bool data_equal = sak::equal(sak::storage(data_out), + sak::storage(data_in)); + + ASSERT_TRUE(data_equal); + + } + +} + + + +template +< + template class Encoder, + template class Decoder +> +inline void test_initialize(uint32_t symbols, uint32_t symbol_size) +{ + + test_initialize + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_initialize + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_initialize + < + Encoder, + Decoder + >(symbols, symbol_size); +} + +template +< + template class Encoder, + template class Decoder +> +inline void test_initialize() +{ + test_initialize(32, 1600); + test_initialize(1, 1600); + + uint32_t symbols = rand_symbols(); + uint32_t symbol_size = rand_symbol_size(); + + test_initialize(symbols, symbol_size); +} + + + diff --git a/test/src/helper_test_mix_uncoded_api.hpp b/test/src/helper_test_mix_uncoded_api.hpp new file mode 100644 index 00000000..fc244da6 --- /dev/null +++ b/test/src/helper_test_mix_uncoded_api.hpp @@ -0,0 +1,127 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + + +/// @file test_rlnc_on_the_fly_codes.cpp Unit tests for the full +/// vector codes (i.e. Network Coding encoders and decoders). + +#include + +#include + +template +inline void test_mix_uncoded(uint32_t symbols, uint32_t symbol_size) +{ + // Common setting + typename Encoder::factory encoder_factory(symbols, symbol_size); + auto encoder = encoder_factory.build(); + + typename Decoder::factory decoder_factory(symbols, symbol_size); + auto decoder = decoder_factory.build(); + + // Encode/decode operations + EXPECT_TRUE(encoder->payload_size() == decoder->payload_size()); + + std::vector payload(encoder->payload_size()); + std::vector data_in = random_vector(encoder->block_size()); + + encoder->set_symbols(sak::storage(data_in)); + + if(kodo::is_systematic_encoder(encoder)) + kodo::set_systematic_off(encoder); + + while( !decoder->is_complete() ) + { + + if((rand() % 100) > 50) + { + encoder->encode( &payload[0] ); + decoder->decode( &payload[0] ); + } + else + { + uint32_t symbol_id = rand() % encoder->symbols(); + + if(decoder->symbol_pivot(symbol_id)) + { + continue; + } + + ASSERT_TRUE(encoder->symbol_size() <= payload.size()); + + encoder->copy_symbol( + symbol_id, sak::storage(payload)); + + const uint8_t *symbol_src = encoder->symbol(symbol_id); + const uint8_t *symbol_dest = &payload[0]; + + EXPECT_TRUE(std::equal(symbol_src, + symbol_src + encoder->symbol_size(), + symbol_dest)); + + + decoder->decode_symbol(&payload[0], symbol_id); + + } + } + + EXPECT_EQ(encoder->block_size(), decoder->block_size()); + + std::vector data_out(decoder->block_size(), '\0'); + decoder->copy_symbols(sak::storage(data_out)); + + EXPECT_TRUE(std::equal(data_out.begin(), + data_out.end(), + data_in.begin())); + +} + + +template +< + template class Encoder, + template class Decoder +> +inline void test_mix_uncoded(uint32_t symbols, uint32_t symbol_size) +{ + test_mix_uncoded + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_mix_uncoded + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_mix_uncoded + < + Encoder, + Decoder + >(symbols, symbol_size); +} + +template +< + template class Encoder, + template class Decoder +> +inline void test_mix_uncoded() +{ + test_mix_uncoded(32, 1600); + test_mix_uncoded(1, 1600); + + uint32_t symbols = rand_symbols(); + uint32_t symbol_size = rand_symbol_size(); + + test_mix_uncoded(symbols, symbol_size); +} + + + + + diff --git a/test/src/helper_test_on_the_fly_api.hpp b/test/src/helper_test_on_the_fly_api.hpp new file mode 100644 index 00000000..d439e956 --- /dev/null +++ b/test/src/helper_test_on_the_fly_api.hpp @@ -0,0 +1,380 @@ +// Copyright Steinwurf ApS 2011-2012. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + + +/// @file helper_on_the_fly_api.hpp Unit test helper for the on the fly API. +/// on-the-fly referrers to the fact the we progressively will set symbols +/// on the stack + +#include +#include + +#include + +/// Tests that an encoder support progressively specifying the symbols +template +< + class Encoder, + class Decoder +> +inline void test_on_the_fly(uint32_t symbols, uint32_t symbol_size) +{ + + // Common setting + typename Encoder::factory encoder_factory(symbols, symbol_size); + auto encoder = encoder_factory.build(); + + typename Decoder::factory decoder_factory(symbols, symbol_size); + auto decoder = decoder_factory.build(); + + std::vector payload(encoder->payload_size()); + std::vector data_in = random_vector(encoder->block_size()); + + auto symbol_sequence = sak::split_storage( + sak::storage(data_in), symbol_size); + + // Set the encoder non-systematic + if(kodo::is_systematic_encoder(encoder)) + kodo::set_systematic_off(encoder); + + EXPECT_EQ(encoder->rank(), 0U); + EXPECT_EQ(decoder->rank(), 0U); + + while( !decoder->is_complete() ) + { + EXPECT_TRUE(encoder->rank() >= decoder->rank()); + + encoder->encode( &payload[0] ); + + // Simulate some loss + if((rand() % 2) == 0) + continue; + + decoder->decode( &payload[0] ); + + if(kodo::is_partial_complete(decoder)) + { + // Check that we as many pivot elements as expected and that these + // are decoded + uint32_t pivot_count = 0; + for(uint32_t i = 0; i < decoder->symbols(); ++i) + { + if(!decoder->symbol_pivot(i)) + continue; + + ++pivot_count; + + auto symbol_storage = + sak::storage(decoder->symbol(i), decoder->symbol_size()); + + EXPECT_TRUE(sak::equal(symbol_storage, symbol_sequence[i])); + } + + EXPECT_EQ(pivot_count, decoder->rank()); + } + + if(((rand() % 2) == 0) && encoder->rank() < symbols) + { + uint32_t i = encoder->rank(); + encoder->set_symbol(i, symbol_sequence[i]); + } + } + + std::vector data_out(decoder->block_size(), '\0'); + decoder->copy_symbols(sak::storage(data_out)); + + EXPECT_TRUE(std::equal(data_out.begin(), + data_out.end(), + data_in.begin())); +} + + +template +< + template class Encoder, + template class Decoder +> +inline void test_on_the_fly(uint32_t symbols, uint32_t symbol_size) +{ + + test_on_the_fly< + Encoder, + Decoder >( + symbols, symbol_size); + + test_on_the_fly< + Encoder, + Decoder >( + symbols, symbol_size); + + test_on_the_fly< + Encoder, + Decoder >( + symbols, symbol_size); + +} + +template +< + template class Encoder, + template class Decoder +> +inline void test_on_the_fly() +{ + + test_on_the_fly(32, 1600); + test_on_the_fly(1, 1600); + + uint32_t symbols = rand_symbols(); + uint32_t symbol_size = rand_symbol_size(); + + test_on_the_fly(symbols, symbol_size); + +} + + +/// This class tests on-the-fly adding symbols to the encoding while +/// using the systematic feature of the encoder +template +inline void test_on_the_fly_systematic(uint32_t symbols, uint32_t symbol_size) +{ + // Common setting + typename Encoder::factory encoder_factory(symbols, symbol_size); + auto encoder = encoder_factory.build(); + + typename Decoder::factory decoder_factory(symbols, symbol_size); + auto decoder = decoder_factory.build(); + + // Encode/decode operations + EXPECT_TRUE(encoder->payload_size() == decoder->payload_size()); + + std::vector payload(encoder->payload_size()); + std::vector data_in = random_vector(encoder->block_size()); + + auto symbol_sequence = sak::split_storage( + sak::storage(data_in), symbol_size); + + // Make sure the encoder is systematic + if(kodo::is_systematic_encoder(encoder)) + kodo::set_systematic_on(encoder); + + + while( !decoder->is_complete() ) + { + encoder->encode( &payload[0] ); + + // Simulate some loss + if((rand() % 2) == 0) + continue; + + decoder->decode( &payload[0] ); + + if(kodo::is_partial_complete(decoder)) + { + // Check that we as many pivot elements as expected and that these + // are decoded + uint32_t pivot_count = 0; + for(uint32_t i = 0; i < decoder->symbols(); ++i) + { + if(!decoder->symbol_pivot(i)) + continue; + + ++pivot_count; + + auto symbol_storage = + sak::storage(decoder->symbol(i), decoder->symbol_size()); + + EXPECT_TRUE(sak::equal(symbol_storage, symbol_sequence[i])); + } + + EXPECT_EQ(pivot_count, decoder->rank()); + } + + + // set symbol 50% of the time ONLY if rank is not full + if(((rand() % 2) == 0) && (encoder->rank() < symbols)) + { + uint32_t i = encoder->rank(); + encoder->set_symbol(i, symbol_sequence[i]); + } + + EXPECT_TRUE(encoder->rank() >= decoder->rank()); + } + + std::vector data_out(decoder->block_size(), '\0'); + decoder->copy_symbols(sak::storage(data_out)); + + EXPECT_TRUE(std::equal(data_out.begin(), + data_out.end(), + data_in.begin())); + +} + +template +< + template class Encoder, + template class Decoder +> +inline void test_on_the_fly_systematic(uint32_t symbols, uint32_t symbol_size) +{ + test_on_the_fly_systematic + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_on_the_fly_systematic + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_on_the_fly_systematic + < + Encoder, + Decoder + >(symbols, symbol_size); +} + +template +< + template class Encoder, + template class Decoder +> +inline void test_on_the_fly_systematic() +{ + test_on_the_fly_systematic(32, 1600); + test_on_the_fly_systematic(1, 1600); + + uint32_t symbols = rand_symbols(); + uint32_t symbol_size = rand_symbol_size(); + + test_on_the_fly_systematic(symbols, symbol_size); +} + + +/// This class tests on-the-fly adding symbols to the encoding while +/// using the systematic feature of the encoder +template +inline void test_on_the_fly_systematic_no_errors(uint32_t symbols, + uint32_t symbol_size) +{ + // Common setting + typename Encoder::factory encoder_factory(symbols, symbol_size); + auto encoder = encoder_factory.build(); + + typename Decoder::factory decoder_factory(symbols, symbol_size); + auto decoder = decoder_factory.build(); + + // Encode/decode operations + EXPECT_TRUE(encoder->payload_size() == decoder->payload_size()); + + std::vector payload(encoder->payload_size()); + std::vector data_in = random_vector(encoder->block_size()); + + auto symbol_sequence = sak::split_storage( + sak::storage(data_in), symbol_size); + + // Make sure the encoder is systematic + if(kodo::is_systematic_encoder(encoder)) + kodo::set_systematic_on(encoder); + + while( !decoder->is_complete() ) + { + encoder->encode( &payload[0] ); + decoder->decode( &payload[0] ); + + if(decoder->rank() > 0) + { + EXPECT_TRUE(kodo::is_partial_complete(decoder)); + } + + if(kodo::is_partial_complete(decoder)) + { + // Check that we as many pivot elements as expected and that these + // are decoded + uint32_t pivot_count = 0; + for(uint32_t i = 0; i < decoder->symbols(); ++i) + { + if(!decoder->symbol_pivot(i)) + continue; + + ++pivot_count; + + auto symbol_storage = + sak::storage(decoder->symbol(i), decoder->symbol_size()); + + EXPECT_TRUE(sak::equal(symbol_storage, symbol_sequence[i])); + } + + EXPECT_EQ(pivot_count, decoder->rank()); + } + + + // set symbol 50% of the time ONLY if rank is not full + if((/*(rand() % 2) == 0) && (*/encoder->rank() < symbols)) + { + uint32_t i = encoder->rank(); + encoder->set_symbol(i, symbol_sequence[i]); + } + + EXPECT_TRUE(encoder->rank() >= decoder->rank()); + } + + std::vector data_out(decoder->block_size(), '\0'); + decoder->copy_symbols(sak::storage(data_out)); + + EXPECT_TRUE(std::equal(data_out.begin(), + data_out.end(), + data_in.begin())); + +} + +template +< + template class Encoder, + template class Decoder +> +inline void test_on_the_fly_systematic_no_errors(uint32_t symbols, + uint32_t symbol_size) +{ + test_on_the_fly_systematic_no_errors + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_on_the_fly_systematic_no_errors + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_on_the_fly_systematic_no_errors + < + Encoder, + Decoder + >(symbols, symbol_size); +} + +template +< + template class Encoder, + template class Decoder +> +inline void test_on_the_fly_systematic_no_errors() +{ + test_on_the_fly_systematic_no_errors(32, 1600); + test_on_the_fly_systematic_no_errors(1, 1600); + + uint32_t symbols = rand_symbols(); + uint32_t symbol_size = rand_symbol_size(); + + test_on_the_fly_systematic_no_errors( + symbols, symbol_size); +} + + + diff --git a/test/src/helper_test_recoding_api.hpp b/test/src/helper_test_recoding_api.hpp new file mode 100644 index 00000000..99dd51ef --- /dev/null +++ b/test/src/helper_test_recoding_api.hpp @@ -0,0 +1,175 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +/// @file helper_recoding_api.hpp Unit test helper for testing that recoding +/// works + +#include +#include + +#include + +/// Small helper structure holding the parameters needed for the +/// recoding tests. +struct recoding_parameters +{ + uint32_t m_max_symbols; + uint32_t m_max_symbol_size; + uint32_t m_symbols; + uint32_t m_symbol_size; +}; + +/// Tests that the recoding function works, this is done by using one +/// encoder +/// and two decoders: +/// +/// +------------+ +------------+ +------------+ +/// | encoder |+---->| decoder #1 |+---->| decoder #2 | +/// +------------+ +------------+ +------------+ +/// +/// Where the encoder passes data to the first decoder which then +/// recodes and passes data to the second decoder +/// +/// @param param The recoding parameters to use +template +inline void invoke_recoding(recoding_parameters param) +{ + // Common setting + typename Encoder::factory encoder_factory( + param.m_max_symbols, param.m_max_symbol_size); + + encoder_factory.set_symbols(param.m_symbols); + encoder_factory.set_symbol_size(param.m_symbol_size); + + auto encoder = encoder_factory.build(); + + typename Decoder::factory decoder_factory( + param.m_max_symbols, param.m_max_symbol_size); + + decoder_factory.set_symbols(param.m_symbols); + decoder_factory.set_symbol_size(param.m_symbol_size); + + auto decoder_one = decoder_factory.build(); + auto decoder_two = decoder_factory.build(); + + // If tested with a shallow decoder we have to remember to set the + // buffers to use for the decoding + std::vector buffer_decoder_one(decoder_one->block_size(), '\0'); + std::vector buffer_decoder_two(decoder_two->block_size(), '\0'); + + if(kodo::has_shallow_symbol_storage::value) + { + decoder_one->set_symbols(sak::storage(buffer_decoder_one)); + decoder_two->set_symbols(sak::storage(buffer_decoder_two)); + } + + EXPECT_EQ(encoder->payload_size(), decoder_one->payload_size()); + EXPECT_EQ(encoder->payload_size(), decoder_two->payload_size()); + + std::vector payload(encoder->payload_size()); + std::vector data_in = random_vector(encoder->block_size()); + + encoder->set_symbols(sak::storage(data_in)); + + // Set the encoder non-systematic + if(kodo::is_systematic_encoder(encoder)) + kodo::set_systematic_off(encoder); + + while( !decoder_two->is_complete() ) + { + uint32_t encode_size = encoder->encode( &payload[0] ); + EXPECT_TRUE(encode_size <= payload.size()); + EXPECT_TRUE(encode_size > 0); + + decoder_one->decode( &payload[0] ); + + uint32_t recode_size = decoder_one->recode( &payload[0] ); + EXPECT_TRUE(recode_size <= payload.size()); + EXPECT_TRUE(recode_size > 0); + + decoder_two->decode( &payload[0] ); + } + + std::vector data_out_one(decoder_one->block_size(), '\0'); + std::vector data_out_two(decoder_two->block_size(), '\0'); + + decoder_one->copy_symbols(sak::storage(data_out_one)); + decoder_two->copy_symbols(sak::storage(data_out_two)); + + EXPECT_TRUE(std::equal(data_out_one.begin(), + data_out_one.end(), + data_in.begin())); + + EXPECT_TRUE(std::equal(data_out_two.begin(), + data_out_two.end(), + data_in.begin())); +} + +/// Invokes the recoding API for the Encoder and Decoder with +/// typical field sizes +/// @param param The recoding parameters +template +< + template class Encoder, + template class Decoder +> +inline void test_recoders(recoding_parameters param) +{ + invoke_recoding< + Encoder, + Decoder >(param); + + invoke_recoding< + Encoder, + Decoder >(param); + + invoke_recoding< + Encoder, + Decoder >(param); +} + + +/// Invokes the recoding API for the Encoder and Decoder with +/// typical field sizes +/// @param param The recoding parameters +template +< + template class Encoder, + template class Decoder +> +inline void test_recoders() +{ + recoding_parameters param; + param.m_max_symbols = 32; + param.m_max_symbol_size = 1600; + param.m_symbols = param.m_max_symbols; + param.m_symbol_size = param.m_max_symbol_size; + + test_recoders(param); + + param.m_max_symbols = 1; + param.m_max_symbol_size = 1600; + param.m_symbols = param.m_max_symbols; + param.m_symbol_size = param.m_max_symbol_size; + + test_recoders(param); + + param.m_max_symbols = 8; + param.m_max_symbol_size = 8; + param.m_symbols = param.m_max_symbols; + param.m_symbol_size = param.m_max_symbol_size; + + test_recoders(param); + + param.m_max_symbols = rand_symbols(); + param.m_max_symbol_size = rand_symbol_size(); + param.m_symbols = rand_symbols(param.m_max_symbols); + param.m_symbol_size = rand_symbol_size(param.m_max_symbol_size); + + test_recoders(param); +} + diff --git a/test/src/helper_test_reuse_api.hpp b/test/src/helper_test_reuse_api.hpp new file mode 100644 index 00000000..b974cb45 --- /dev/null +++ b/test/src/helper_test_reuse_api.hpp @@ -0,0 +1,266 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +#pragma once + +#include + +#include "basic_api_test_helper.hpp" + +/// Helper for the reuse test, ensures that all encoders and decoders +/// produce valid data +template +inline void test_reuse_helper(Encoder encoder, Decoder decoder) +{ + std::vector payload(encoder->payload_size()); + + std::vector data_in = random_vector(encoder->block_size()); + sak::mutable_storage storage_in = sak::storage(data_in); + + encoder->set_symbols(storage_in); + + // Set the encoder non-systematic + if(kodo::is_systematic_encoder(encoder)) + kodo::set_systematic_off(encoder); + + while( !decoder->is_complete() ) + { + uint32_t payload_used = encoder->encode( &payload[0] ); + EXPECT_TRUE(payload_used <= encoder->payload_size()); + + decoder->decode( &payload[0] ); + } + + std::vector data_out(decoder->block_size(), '\0'); + decoder->copy_symbols(sak::storage(data_out)); + + EXPECT_TRUE(std::equal(data_out.begin(), + data_out.end(), + data_in.begin())); + +} + +/// Test that instantiates a number of encoders and decoders from +/// the same factories +template +inline void test_reuse(uint32_t symbols, uint32_t symbol_size) +{ + // Common setting + typename Encoder::factory encoder_factory(symbols, symbol_size); + typename Decoder::factory decoder_factory(symbols, symbol_size); + + for(uint32_t i = 0; i < 3; ++i) + { + uint32_t coders = rand_nonzero(5); + + std::vector encoders; + std::vector decoders; + + for(uint32_t j = 0; j < coders; ++j) + { + encoders.push_back(encoder_factory.build()); + decoders.push_back(decoder_factory.build()); + + } + + for(uint32_t j = 0; j < coders; ++j) + { + test_reuse_helper(encoders[j], decoders[j]); + } + + } + + // Test with differing symbols and symbol sizes + for(uint32_t i = 0; i < 2; ++i) + { + uint32_t coders = rand_nonzero(3); + + std::vector encoders; + std::vector decoders; + + for(uint32_t j = 0; j < coders; ++j) + { + uint32_t s = rand_symbols(encoder_factory.max_symbols()); + uint32_t l = rand_symbol_size(encoder_factory.max_symbol_size()); + + encoder_factory.set_symbols(s); + encoder_factory.set_symbol_size(l); + decoder_factory.set_symbols(s); + decoder_factory.set_symbol_size(l); + + encoders.push_back(encoder_factory.build()); + decoders.push_back(decoder_factory.build()); + } + + for(uint32_t j = 0; j < coders; ++j) + { + test_reuse_helper(encoders[j], decoders[j]); + } + + } + +} + +/// Test reuse function which will invoke the reuse test for the common +/// field sizes +template +< + template class Encoder, + template class Decoder +> +inline void test_reuse(uint32_t symbols, uint32_t symbol_size) +{ + test_reuse + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_reuse + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_reuse + < + Encoder, + Decoder + >(symbols, symbol_size); +} + +template +< + template class Encoder, + template class Decoder +> +inline void test_reuse() +{ + test_reuse(1, 1600); + test_reuse(32, 1600); + + uint32_t symbols = rand_symbols(); + uint32_t symbol_size = rand_symbol_size(); + + test_reuse(symbols, symbol_size); +} + + + +/// Tests that encoders and decoders can be safely reused after incomplete +/// encoding / decoding. +template +inline void test_reuse_incomplete(uint32_t symbols, uint32_t symbol_size) +{ + + bool do_complete; + + typename Encoder::factory encoder_factory(symbols, symbol_size); + typename Decoder::factory decoder_factory(symbols, symbol_size); + + // Use factory a lot of times + for (uint32_t i = 0; i < 20; ++i) + { + // Build coders + auto encoder = encoder_factory.build(); + auto decoder = decoder_factory.build(); + + // Prepare buffers + std::vector payload(encoder->payload_size()); + std::vector data_in(encoder->block_size()); + + // Fill with random data + for (auto &e: data_in) + e = rand() % 256; + + // Put data in encoder + encoder->set_symbols(sak::storage(data_in)); + + if (rand() % 100 > 90) + { + do_complete = false; + } + else + { + do_complete = true; + } + + // Start encoding/decoding + while (!decoder->is_complete()) + { + encoder->encode(&payload[0]); + + // Loose a packet with probability + if (rand() % 100 > 90) + continue; + + decoder->decode(&payload[0]); + + // Stop decoding after a while with probability + if (!do_complete && decoder->rank() == symbols - 2) + break; + } + + // Check if completed decoders are correct + if (decoder->is_complete()) + { + std::vector data_out(decoder->block_size()); + decoder->copy_symbols(sak::storage(data_out)); + + ASSERT_TRUE(sak::equal(sak::storage(data_out), + sak::storage(data_in))); + } + } + +} + + +/// Test reuse function which will invoke the reuse test for the common +/// field sizes +template + < + template class Encoder, + template class Decoder + > +inline void test_reuse_incomplete(uint32_t symbols, uint32_t symbol_size) +{ + test_reuse_incomplete + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_reuse_incomplete + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_reuse_incomplete + < + Encoder, + Decoder + >(symbols, symbol_size); +} + + +template +< + template class Encoder, + template class Decoder +> +inline void test_reuse_incomplete() +{ + test_reuse_incomplete(1, 1600); + test_reuse_incomplete(32, 1600); + + uint32_t symbols = rand_symbols(); + uint32_t symbol_size = rand_symbol_size(); + + test_reuse_incomplete(symbols, symbol_size); +} + + + diff --git a/test/src/helper_test_systematic_api.hpp b/test/src/helper_test_systematic_api.hpp new file mode 100644 index 00000000..bda84306 --- /dev/null +++ b/test/src/helper_test_systematic_api.hpp @@ -0,0 +1,104 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + + +/// @file test_rlnc_on_the_fly_codes.cpp Unit tests for the full +/// vector codes (i.e. Network Coding encoders and decoders). + +#include + +#include + + +template +inline void test_systematic(uint32_t symbols, uint32_t symbol_size) +{ + // Common setting + typename Encoder::factory encoder_factory(symbols, symbol_size); + auto encoder = encoder_factory.build(); + + typename Decoder::factory decoder_factory(symbols, symbol_size); + auto decoder = decoder_factory.build(); + + // Encode/decode operations + EXPECT_TRUE(encoder->payload_size() == decoder->payload_size()); + + std::vector payload(encoder->payload_size()); + std::vector data_in = random_vector(encoder->block_size()); + + encoder->set_symbols(sak::storage(data_in)); + + // Ensure encoder systematic + EXPECT_TRUE(kodo::is_systematic_encoder(encoder)); + kodo::set_systematic_on(encoder); + + uint32_t pkg_count = 0; + + while( !decoder->is_complete() ) + { + encoder->encode( &payload[0] ); + decoder->decode( &payload[0] ); + + ++pkg_count; + } + + EXPECT_TRUE(pkg_count == encoder->symbols()); + + std::vector data_out(decoder->block_size(), '\0'); + decoder->copy_symbols(sak::storage(data_out)); + + EXPECT_TRUE(std::equal(data_out.begin(), + data_out.end(), + data_in.begin())); + +} + +template +< + template class Encoder, + template class Decoder +> +inline void test_systematic(uint32_t symbols, uint32_t symbol_size) +{ + test_systematic + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_systematic + < + Encoder, + Decoder + >(symbols, symbol_size); + + test_systematic + < + Encoder, + Decoder + >(symbols, symbol_size); +} + +template +< + template class Encoder, + template class Decoder +> +inline void test_systematic() +{ + test_systematic(32, 1600); + test_systematic(1, 1600); + + uint32_t symbols = rand_symbols(); + uint32_t symbol_size = rand_symbol_size(); + + test_systematic(symbols, symbol_size); +} + + + + + + diff --git a/test/src/test_backward_linear_block_decoder.cpp b/test/src/test_backward_linear_block_decoder.cpp new file mode 100644 index 00000000..859f9f6e --- /dev/null +++ b/test/src/test_backward_linear_block_decoder.cpp @@ -0,0 +1,245 @@ +// Copyright Steinwurf ApS 2011-2013. +// Distributed under the "STEINWURF RESEARCH LICENSE 1.0". +// See accompanying file LICENSE.rst or +// http://www.steinwurf.com/licensing + +/// @file test_backward_linear_block_decoder.cpp Unit tests for the +/// kodo::backward_linear_block_decoder + +#include +#include + +#include +#include + +#include + +#include "basic_api_test_helper.hpp" + +namespace kodo +{ + + template + class test_backward_stack + : public // Payload API + // Codec Header API + // Symbol ID API + // Codec API + debug_linear_block_decoder< + backward_linear_block_decoder< + // Coefficient Storage API + coefficient_storage< + coefficient_info< + // Storage API + deep_symbol_storage< + storage_bytes_used< + storage_block_info< + // Finite Field API + finite_field_math::type, + finite_field_info + > > > > > > > > > > + { }; + + template + class test_backward_delayed_stack + : public // Payload API + // Codec Header API + // Symbol ID API + // Codec API + debug_linear_block_decoder< + linear_block_decoder_delayed< + backward_linear_block_decoder< + // Coefficient Storage API + coefficient_storage< + coefficient_info< + // Storage API + deep_symbol_storage< + storage_bytes_used< + storage_block_info< + // Finite Field API + finite_field_math::type, + finite_field_info + > > > > > > > > > > > + { }; + +} + +template