THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Browse Source

Add backtracking forms that use even less memory

master
Joel Yliluoma 2 years ago
parent
commit
400563e30b
3 changed files with 2224 additions and 683 deletions
  1. +68
    -117
      README.md
  2. +1876
    -566
      gunzip.hh
  3. +280
    -0
      proto-generator.php

+ 68
- 117
README.md View File

@@ -6,44 +6,61 @@ that requires minimal amount of memory to work.
Terms of use: Zlib
Copyright © 2017 Joel Yliluoma

## Memory usage at aggressive settings
## Memory usage at aggressive settings (backtrackable input)

* 408 bytes of automatic storage for three huffman trees (384 elements, 5…9 bits each, 88 % space efficiency)
* 24 bytes of temporary automatic storage while a huffman tree is being generated (15 elements, 9 bits each, 66 % space efficiency)
* An assortment of automatic variables for various purposes (may be register variables, depending on the architecture and of the compiler wits)
* ABI mandated alignment losses

Total: 408 bytes minimum, 432+N bytes maximum

Theoretical minimum at 100 % efficiency: 357.1 + 15.32 ≃ 373 bytes (not yet attained by this library).

## Memory usage at aggressive settings (non-backtrackable input)

* 144 bytes of automatic storage for length tables (288 elements, 4 bits each, 100 % space efficiency)
* 384 bytes of automatic storage for huffman tree (352 elements, 5…9 bits each, 88 % space efficiency)
* 384 bytes of automatic storage for two huffman trees (350 elements, 5…9 bits each, 88 % space efficiency)
* 24 bytes of temporary automatic storage while a huffman tree is being generated (15 elements, 9 bits each, 66 % space efficiency)
* An assortment of automatic variables for various purposes (may be register variables, depending on the architecture and of the compiler wits)
* ABI mandated alignment losses

Total: 528 bytes minimum, 552+N bytes maximum

In addition, if you neither decompress into a raw memory area nor supply your own window function,
32768 bytes of automatic storage is allocated for the look-behind window.

Theoretical minimum at 100 % efficiency: 144 + 338.9 + 15.32 ≃ 499 bytes (not yet attained by this library).

## Memory usage at default settings
## Memory usage at default settings (backtrackable input)

* 687 bytes of automatic storage for three huffman trees (52 % space efficiency)
* 30 bytes of temporary automatic storage while a huffman tree is being generated (53 % space efficiency)
* An assortment of automatic variables for various purposes (may be register variables, depending on the architecture and of the compiler wits)
* ABI mandated alignment losses

Total: 687 bytes minimum, 717+N bytes maximum

## Memory usage at default settings (non-backtrackable input)

* 288 bytes of automatic storage for length tables (50 % space efficiency)
* 653 bytes of automatic storage for huffman tree (52 % space efficiency)
* 653 bytes of automatic storage for two huffman trees (52 % space efficiency)
* 30 bytes of temporary automatic storage while a huffman tree is being generated (53 % space efficiency)
* An assortment of automatic variables for various purposes (may be register variables, depending on the architecture and of the compiler wits)
* ABI mandated alignment losses

Total: 941 bytes minimum, 971+N bytes maximum

In addition, if you neither decompress into a raw memory area nor supply your own window function,
32768 bytes of automatic storage is allocated for the look-behind window.

## Tuning

To adjust the memory usage, there are three settings in gunzip.hh you can change:

| Setting name | 'false' memory use bytes | 'true' memory use bytes | 'true' performance impact
| ------------------------------------------- | ---:| ----:|--------------
| `USE_BITARRAY_TEMPORARY_IN_HUFFMAN_CREATION` | 30 | 24 | Negligible
| `USE_BITARRAY_FOR_LENGTHS` | 288 | 144 | Noticeable
| `USE_BITARRAY_FOR_HUFFNODES` | 653 | 384 | Significant
| **Total** | 971 | 552 | _Plus alignment losses, callframes and spills_
| `USE_BITARRAY_TEMPORARY_IN_HUFFMAN_CREATION` | 30 | 24 | Negligible
| `USE_BITARRAY_FOR_LENGTHS` | 288 or 0 | 144 or 0 | Noticeable
| `USE_BITARRAY_FOR_HUFFNODES` | 653 or 687 | 384 or 408 | Significant
| **Total** | 971 or 717 | 552 or 432 | _Plus alignment losses, callframes and spills_

In addition, if you neither decompress into a raw memory area nor supply your own window function,
32768 bytes of automatic storage is allocated for the look-behind window.

You can also change the memory allocation scheme:

@@ -83,120 +100,50 @@ distance used by your compressed data.
## Definitions

```C++
// Most generic: Input and output functors
template<typename InputFunctor, typename OutputFunctor>
int Deflate(InputFunctor&& input, OutputFunctor&& output); // (1) (5) (9)

// An optional window functor can be supplied, so the lib will not allocate 32768-byte look-behind buffer
template<typename InputFunctor, typename OutputFunctor, typename WindowFunctor>
int Deflate(InputFunctor&& input, OutputFunctor&& output, WindowFunctor&& outputcopy); // (2) (5)

// Output iterator (write only)
template<typename InputFunctor, typename OutputIterator>
int Deflate(InputFunctor&& input, OutputIterator&& target); // (5) (9)

// Random-access iterator. It is also used for reading (look-behinds).
template<typename InputFunctor, typename RandomAccessIterator>
int Deflate(InputFunctor&& input, RandomAccessIterator&& target); // (5) (10)

template<typename InputFunctor, typename RandomAccessIterator>
int Deflate(InputFunctor&& input, RandomAccessIterator&& target, std::size_t target_limit); // (3) (5) (10)

template<typename InputFunctor, typename RandomAccessIterator>
int Deflate(InputFunctor&& input, RandomAccessIterator&& target_begin, RandomAccessIterator&& target_end); // (4) (5) (10)


// These are the same as the first six,
// but instead of an input functor, they use an input iterator.
// An input iterator is dereferenced and read at most once (per byte), and then incremented.

template<typename InputIterator, typename OutputFunctor>
int Deflate(InputIterator&& input, OutputFunctor&& output); // (1) (7) (9)

template<typename InputIterator, typename OutputFunctor, typename WindowFunctor>
int Deflate(InputIterator&& input, OutputFunctor&& output, WindowFunctor&& outputcopy); // (2) (7)

template<typename InputIterator, typename OutputIterator>
int Deflate(InputIterator&& input, OutputIterator&& target); // (7) (9)

template<typename InputIterator, typename RandomAccessIterator>
int Deflate(InputIterator&& input, RandomAccessIterator&& target); // (7) (10)

template<typename InputIterator, typename RandomAccessIterator>
int Deflate(InputIterator&& input, RandomAccessIterator&& target, std::size_t target_limit); // (3) (7) (10)

template<typename InputIterator, typename RandomAccessIterator>
int Deflate(InputIterator&& input, RandomAccessIterator&& target_begin, RandomAccessIterator&& target_end); // (4) (7) (10)


// These are the same as the first six, but instead of an input functor,
// they use a pair of forward iterators to indicate the range of input data.
// Forward iterators are used like input iterators, but two iterators can be compared for equality.

template<typename ForwardIterator, typename OutputFunctor>
int Deflate(ForwardIterator&& begin, ForwardIterator&& end, OutputFunctor&& output); // (1) (6) (9)

template<typename ForwardIterator, typename OutputFunctor, typename WindowFunctor>
int Deflate(ForwardIterator&& begin, ForwardIterator&& end, OutputFunctor&& output, WindowFunctor&& outputcopy); // (2) (6)

template<typename ForwardIterator, typename OutputIterator>
int Deflate(ForwardIterator&& begin, ForwardIterator&& end, OutputIterator&& target); // (6) (9)

template<typename ForwardIterator, typename RandomAccessIterator>
int Deflate(ForwardIterator&& begin, ForwardIterator&& end, RandomAccessIterator&& target); // (6) (10)

template<typename ForwardIterator, typename RandomAccessIterator>
int Deflate(ForwardIterator&& begin, ForwardIterator&& end, RandomAccessIterator&& target, std::size_t target_limit); // (3) (6) (10)

template<typename ForwardIterator, typename RandomAccessIterator>
int Deflate(ForwardIterator&& begin, ForwardIterator&& end, RandomAccessIterator&& target_begin, RandomAccessIterator&& target_end); // (4) (6) (10)


// These are the same as the first six, but instead of an input functor,
// they use an input iterator and a length.

template<typename InputIterator, typename OutputFunctor>
int Deflate(InputIterator&& begin, std::size_t length, OutputFunctor&& output); // (1) (7) (8) (9)

template<typename InputIterator, typename OutputFunctor, typename WindowFunctor>
int Deflate(InputIterator&& begin, std::size_t length, OutputFunctor&& output, WindowFunctor&& outputcopy); // (2) (7) (8)

template<typename InputIterator, typename OutputIterator>
int Deflate(InputIterator&& begin, std::size_t length, OutputIterator&& target); // (7) (8) (9)

template<typename InputIterator, typename RandomAccessIterator>
int Deflate(InputIterator&& begin, std::size_t length, RandomAccessIterator&& target); // (7) (8) (10)

template<typename InputIterator, typename RandomAccessIterator>
int Deflate(InputIterator&& begin, std::size_t length, RandomAccessIterator&& target, std::size_t target_limit); // (3) (7) (8) (10)

template<typename InputIterator, typename RandomAccessIterator>
int Deflate(InputIterator&& begin, std::size_t length, RandomAccessIterator&& target_begin, RandomAccessIterator&& target_end); // (4) (7) (8) (10)


// Finally, the following three patterns are available, if you need to know
// after decompression how many bytes were read and/or written:

struct DeflateTrackNoSize{};
struct DeflateTrackInSize{};
struct DeflateTrackOutSize{};
struct DeflateTrackBothSize{};

template<typename... Args>
int Deflate(Args&&... args, DeflateTrackNoSize); // (Same as no tag)
int/*exit status*/ Deflate(InputParams..., OutputParams..., DeflateTrackNoSize = {});

template<typename... Args>
std::pair<int, std::uint_fast64_t> Deflate(Args&&... args, DeflateTrackInSize); // (11)
std::pair<int/*exit status*/, std::uint_fast64_t/*number of input bytes consumed*/>
Deflate(InputParams..., OutputParams..., DeflateTrackInSize); // (11)

template<typename... Args>
std::pair<int, std::uint_fast64_t> Deflate(Args&&... args, DeflateTrackOutSize); // (12)
std::pair<int/*exit status*/, std::uint_fast64_t/*number of output bytes generated*/>
Deflate(InputParams..., OutputParams..., DeflateTrackOutSize); // (12)

template<typename... Args>
std::pair<int, std::pair<std::uint_fast64_t,std::uint_fast64_t>> Deflate(Args&&... args, DeflateTrackBothSize); // (13)
std::pair<int/*exit status*/, std::pair<std::uint_fast64_t/*in size*/, std::uint_fast64_t/*out size*/>>
Deflate(InputParams..., OutputParams..., DeflateTrackBothSize); // (13)

// A counter for sizes is only allocated if explicitly requested by using one of these three overloads.
// A counter for sizes is only allocated if explicitly requested
// by using one of the former three tracking overloads.
```

`InputParams` may be one of the following sets of parameters:

* InputFunctor input `(5)` `(14)`
* InputIterator begin `(7)` `(14)`
* InputIterator begin, InputIterator end `(6)` `(14)`
* InputIterator begin, SizeType length `(8)` `(14)`
* BidirectionalIterator begin, SizeType length `(8)` `(15)`
* ForwardIterator begin `(7)` `(14)`
* BidirectionalIterator begin `(7)` `(15)`
* RandomAccessIterator begin `(7)` `(15)`
* ForwardIterator begin, ForwardIterator end `(6)` `(15)`
* BidirectionalIterator begin, BidirectionalIterator end `(6)` `(15)`
* RandomAccessIterator begin, RandomAccessIterator end `(6)` `(15)`

`OutputParams` may be one of the following sets of parameters:

* OutputFunctor output `(1)` `(9)`
* OutputFunctor output, WindowFunctor window `(2)`
* OutputIterator target `(9)`
* RandomAccessIterator target `(10)`
* RandomAccessIterator target, SizeType target_limit `(3)` `(10)`
* RandomAccessIterator target, RandomAccessIterator target_end `(4)` `(10)`


1) If the output functor (`output`) returns a `bool`, and the returned value is `true`, the decompression aborts with return value -3
without writing any more data.

@@ -238,6 +185,10 @@ The `second` field in the return value contains the number of bytes that were wr
The `second.first` field in the return value contains the number of bytes that were consumed from the input.
The `second.second` field in the return value contains the number of bytes that were written to the output.

14) This method is non-backtrackable, and uses a bit more memory than the backtrackable ones.

15) This method is backtrackable, meaning that some bytes in the input may be read twice. It uses less memory than the non-backtrackable calls.

### Tips

Some of these definitions may be ambiguous.

+ 1876
- 566
gunzip.hh
File diff suppressed because it is too large
View File


+ 280
- 0
proto-generator.php View File

@@ -0,0 +1,280 @@
<?php

$inputs = Array();
$outputs = Array();
$bodies = Array();

/*********************************/

/* Input functor */
$inputs['Inf'] = Array(
'templateparams' => Array('InputFunctor'),
'enables' => Array('DeflIsInputFunctor'),
'params' => Array('InputFunctor&& inputfun'),
'abortable' => ('DeflInputAbortable_Type(std::decay_t<std::result_of_t<InputFunctor()>>)'),
'before' => 'gunzip_ns::dummy btfun{};'
);
/* Input iterator */
$inputs['InI'] = Array(
'templateparams' => Array('InputIterator'),
'enables' => Array('DeflIsInputIterator'),
'params' => Array('InputIterator&& input'),
'abortable' => ('DeflInputAbortable_Type(std::decay_t<decltype(*input)>)'),
'before' => 'auto inputfun = [&]() { auto r = *input; ++input; return r; };'."\n".
'gunzip_ns::dummy btfun{};'
);
/* Input iterator begin and end */
$inputs['InI2'] = Array(
'templateparams' => Array('InputIterator'),
'enables' => Array('DeflIsInputIterator'),
'params' => Array('InputIterator&& begin', 'InputIterator&& end'),
'abortable' => ('1'),
'before' => 'auto inputfun = [&]() { if(begin==end) { return -1; } int r = *begin; ++begin; return r; };'."\n".
'gunzip_ns::dummy btfun{};'
);
/* Input iterator and number of bytes */
$inputs['InIl'] = Array(
'templateparams' => Array('InputIterator', 'SizeType'),
'enables' => Array('DeflIsInputIterator', 'DeflIsSizeType'),
'params' => Array('InputIterator&& begin', 'SizeType&& length'),
'abortable' => ('1'),
'before' => 'typename std::iterator_traits<std::decay_t<InputIterator>>::difference_type remain(length);'."\n".
'auto inputfun = [&]() -> std::common_type_t<int, decltype(*begin)> '."\n".
'{ if(!remain) { return -1; } --remain; int r = *begin; ++begin; return r; };'."\n".
'gunzip_ns::dummy btfun{};'
);

/* Bidir iterator and number of bytes */
$inputs['InBl'] = Array(
'templateparams' => Array('BidirIterator', 'SizeType'),
'enables' => Array('DeflIsBidirIterator', 'DeflIsSizeType'),
'params' => Array('BidirIterator&& begin', 'SizeType&& length'),
'abortable' => ('1'),
'before' => 'typename std::iterator_traits<std::decay_t<BidirIterator>>::difference_type remain(length), savestate{};'."\n".
'auto inputfun = [&]() -> std::common_type_t<int, decltype(*begin)> '."\n".
'{ if(!remain) { return -1; } --remain; int r = *begin; ++begin; return r; };'."\n".
'auto btfun = [&](bool act) { if(act) { begin -= (savestate-remain); remain = savestate; } else savestate = remain; };'
);

/* Forward/bidir/const random iterator */
$inputs['InF'] = Array(
'templateparams' => Array('ForwardIterator'),
'enables' => Array('DeflIsForwardIterator'),
'params' => Array('ForwardIterator&& begin'),
'abortable' => ('0'),
'before' => 'ForwardIterator saved;'.
'auto inputfun = [&]() { auto r = *begin; ++begin; return r; };'."\n".
'auto btfun = [&](bool act) { if(act) begin = saved; else saved = std::move(begin); };'
);

/* Forward/bidir/const random iterator begin and end */
$inputs['InF2'] = Array(
'templateparams' => Array('ForwardIterator'),
'enables' => Array('DeflIsForwardIterator'),
'params' => Array('ForwardIterator&& begin', 'ForwardIterator&& end'),
'abortable' => ('1'),
'before' => 'ForwardIterator saved;'."\n".
'auto inputfun = [&]() { if(begin==end) { return -1; } int r = *begin; ++begin; return r; };'."\n".
'auto btfun = [&](bool act) { if(act) begin = saved; else saved = std::move(begin); };'
);

/*********************************/

/* Output functor and window functor */
$outputs['Outfw'] = Array(
'templateparams' => Array('OutputFunctor', 'WindowFunctor'),
'enables' => Array('DeflIsOutputFunctor', 'DeflIsWindowFunctor'),
'params' => Array('OutputFunctor&& output', 'WindowFunctor&& outputcopy'),
'abortable' => '(DeflOutputAbortable_OutputFunctor & DeflOutputAbortable_WindowFunctor)',
'before' => ''
);

/* Output functor */
$outputs['Outf'] = Array(
'templateparams' => Array('OutputFunctor'),
'enables' => Array('DeflIsOutputFunctor'),
'params' => Array('OutputFunctor&& output_orig'),
'abortable' => 'DeflOutputAbortable_OutputFunctor',
'before' =>
'DeflDeclWindow'."\n".
'auto output = [&](unsigned char l)'."\n".
'{'."\n".
' window.Data[window.Head++ % gunzip_ns::MAX_WINDOW_SIZE] = l;'."\n".
' return output_orig(l);'."\n".
'};'."\n".
'auto outputcopy = [&](std::uint_least16_t length, std::uint_fast32_t offs)'."\n".
'{'."\n".
' /* length=0 means that offs is the size of the window. */'."\n".
' for(; length>0; --length)'."\n".
' {'."\n".
' unsigned char byte = window.Data[(window.Head - offs) % gunzip_ns::MAX_WINDOW_SIZE];'."\n".
' if(gunzip_ns::OutputHelper<bool(Abortable&2)>::output(output, byte))'."\n".
' break;'."\n".
' }'."\n".
' return length;'."\n".
'};'
);

/* Output iterator */
$outputs['OutI'] = Array(
'templateparams' => Array('OutputIterator'),
'enables' => Array('DeflIsOutputIterator'),
'params' => Array('OutputIterator&& target'),
'abortable' => '0',
'before' =>
'DeflDeclWindow'."\n".
'auto output = [&](unsigned char l)'."\n".
'{'."\n".
' window.Data[window.Head++ % gunzip_ns::MAX_WINDOW_SIZE] = l;'."\n".
' *target = l; ++target;'."\n".
'};'."\n".
'auto outputcopy = [&](std::uint_least16_t length, std::uint_fast32_t offs)'."\n".
'{'."\n".
' /* length=0 means that offs is the size of the window. */'."\n".
' for(; length>0; --length)'."\n".
' {'."\n".
' unsigned char byte = window.Data[(window.Head - offs) % gunzip_ns::MAX_WINDOW_SIZE];'."\n".
' output(byte);'."\n".
' }'."\n".
' return false;'."\n".
'};'
);

/* Random access iterator */
$outputs['Outr'] = Array(
'templateparams' => Array('RandomAccessIterator'),
'enables' => Array('DeflIsRandomAccessIterator'),
'params' => Array('RandomAccessIterator&& target'),
'abortable' => '0',
'before' =>
'auto output = [&](unsigned char l) { *target = l; ++target; };'."\n".
'auto outputcopy = [&](std::uint_least16_t length, std::uint_fast32_t offs)'."\n".
'{'."\n".
' /* length=0 means that offs is the size of the window. */'."\n".
' for(; length--; ++target) { *target = *(target-offs); }'."\n".
'};'
);

/* Random access iterator and length limit */
$outputs['Outrl'] = Array(
'templateparams' => Array('RandomAccessIterator', 'SizeType2'),
'enables' => Array('DeflIsRandomAccessIterator', 'DeflIsSizeType2'),
'params' => Array('RandomAccessIterator&& target', 'SizeType2&& target_limit'),
'abortable' => '2',
'before' =>
'typename std::iterator_traits<std::decay_t<RandomAccessIterator>>::difference_type used{}, cap=target_limit;'."\n".
'auto output = [&](unsigned char l)'."\n".
'{'."\n".
' if(used >= cap) return true;'."\n".
' target[used] = l; ++used;'."\n".
' return false;'."\n".
'};'."\n".
'auto outputcopy = [&](std::uint_least16_t length, std::uint_fast32_t offs)'."\n".
'{'."\n".
' /* length=0 means that offs is the size of the window. */'."\n".
' for(; length > 0 && used < cap; ++used, --length)'."\n".
' {'."\n".
' target[used] = target[used - offs];'."\n".
' }'."\n".
' return length;'."\n".
'};'
);

/* Random access iterator begin and end */
$outputs['Outr2'] = Array(
'templateparams' => Array('RandomAccessIterator'),
'enables' => Array('DeflIsRandomAccessIterator'),
'params' => Array('RandomAccessIterator&& target_begin', 'RandomAccessIterator&& target_end'),
'abortable' => '2',
'before' =>
'auto output = [&](unsigned char l)'."\n".
'{'."\n".
' if(target_begin == target_end) return true;'."\n".
' *target_begin = l; ++target_begin;'."\n".
' return false;'."\n".
'};'."\n".
'auto outputcopy = [&](std::uint_least16_t length, std::uint_fast32_t offs)'."\n".
'{'."\n".
' /* length=0 means that offs is the size of the window. */'."\n".
' for(; length > 0 && !(target_begin == target_end); --length, ++target_begin)'."\n".
' {'."\n".
' *target_begin = *(target_begin - offs);'."\n".
' }'."\n".
' return length;'."\n".
'};'
);

/*********************************/

$bodies = Array();

$bodies['Tk0'] = Array(
'params' => Array('DeflateTrackNoSize = {}'),
'ret' => 'int',
'body' => 'return gunzip_ns::Gunzip<Abortable>(inputfun, output, outputcopy, btfun);'
);

$bodies['Tki'] = Array(
'params' => Array('DeflateTrackInSize'),
'ret' => 'std::pair<int, std::uint_fast64_t>',
'body' => 'gunzip_ns::SizeTracker<DeflateTrackInSize> tracker;'."\n".
'return tracker(gunzip_ns::Gunzip<Abortable>(tracker.template ForwardInput(inputfun), output, outputcopy, btfun));'
);

$bodies['Tko'] = Array(
'params' => Array('DeflateTrackOutSize'),
'ret' => 'std::pair<int, std::uint_fast64_t>',
'body' => 'gunzip_ns::SizeTracker<DeflateTrackOutSize> tracker;'."\n".
'return tracker(gunzip_ns::Gunzip<Abortable>(inputfun, tracker.template ForwardOutput(output), tracker.template ForwardWindow(outputcopy), btfun));'
);

$bodies['Tkb'] = Array(
'params' => Array('DeflateTrackBothSize'),
'ret' => 'std::pair<int, std::pair<std::uint_fast64_t,std::uint_fast64_t>>',
'body' => 'gunzip_ns::SizeTracker<DeflateTrackBothSize> tracker;'."\n".
'return tracker(gunzip_ns::Gunzip<Abortable>(tracker.template ForwardInput(inputfun), tracker.template ForwardOutput(output), tracker.template ForwardWindow(outputcopy), btfun));'
);

$replacements = Array();
function Replace(&$string, $hint)
{
global $replacements;
if($string == '') return;
if(!isset($replacements[$string]))
{
$v = "DEFL_MACRO_$hint";
$replacements[$string] = $v;
$s = "\n$string";
$s = str_replace("\n", " \\\n ", $s);
print "#define $v $s\n";
}
$string = $replacements[$string];
}



foreach($inputs as $k=>&$input) Replace($input['before'], $k);
foreach($outputs as $k=>&$output) Replace($output['before'], $k);
foreach($bodies as $k=>&$body) Replace($body['body'], $k);
unset($input); unset($output); unset($body);


foreach($inputs as $input)
foreach($outputs as $output)
foreach($bodies as $body)
{
$s = 'template<';
foreach($input['templateparams'] as $p) { print "{$s}typename {$p}"; $s=','; }
foreach($output['templateparams'] as $p) { print "{$s}typename {$p}"; $s=','; }
$s = ">\nstd::enable_if_t<";
foreach($input['enables'] as $p) { print "{$s}{$p}"; $s=' && '; }
foreach($output['enables'] as $p) { print "{$s}{$p}"; $s=' && '; }
$s = ", {$body['ret']}>\n Deflate(";
foreach($input['params'] as $p) { print "{$s}{$p}"; $s=','; }
foreach($output['params'] as $p) { print "{$s}{$p}"; $s=','; }
foreach($body['params'] as $p) { print "{$s}{$p}"; $s=','; }
print ")\n{\nconstexpr unsigned char Abortable = {$input['abortable']} | {$output['abortable']};\n{$input['before']} {$output['before']} {$body['body']}\n}\n\n";
}

foreach($replacements as $s=>$v) print "#undef $v\n";

Loading…
Cancel
Save