| 1 |
module compression; |
|---|
| 2 |
|
|---|
| 3 |
/** |
|---|
| 4 |
* tango.compression.model.ICompressionCodec |
|---|
| 5 |
*/ |
|---|
| 6 |
|
|---|
| 7 |
import tango.io.model.IConduit; |
|---|
| 8 |
import tango.io.protocol.model.IReader; |
|---|
| 9 |
import tango.io.protocol.model.IWriter; |
|---|
| 10 |
|
|---|
| 11 |
/** |
|---|
| 12 |
* CompressionProfile provides an abstract way of setting up compression |
|---|
| 13 |
* settings without having to worry about the concrete details. |
|---|
| 14 |
*/ |
|---|
| 15 |
enum CompressionProfile |
|---|
| 16 |
{ |
|---|
| 17 |
Normal = 1, /// Average level compression. |
|---|
| 18 |
Fast = 0, /// As fast as possible: may omit compression altogether. |
|---|
| 19 |
Small = 2 /// As small as possible. |
|---|
| 20 |
} |
|---|
| 21 |
|
|---|
| 22 |
interface ICompressionCodec : IConduitFilter |
|---|
| 23 |
{ |
|---|
| 24 |
/** |
|---|
| 25 |
* These two functions provide a way to serialise the codec state |
|---|
| 26 |
* necessary to exactly reproduce its behaviour. For instance, this could |
|---|
| 27 |
* be used to write out the precise profile used to compress a stream of |
|---|
| 28 |
* data, thus allowing it to be decompressed again. |
|---|
| 29 |
*/ |
|---|
| 30 |
abstract void readProfile(IReader input); |
|---|
| 31 |
abstract void writeProfile(IWriter output); /// ditto |
|---|
| 32 |
|
|---|
| 33 |
/** |
|---|
| 34 |
* These two functions access the abstract profile setting, which means |
|---|
| 35 |
* people don't have to stuff around with the above two if they don't have |
|---|
| 36 |
* the need. |
|---|
| 37 |
*/ |
|---|
| 38 |
abstract CompressionProfile getProfile(); |
|---|
| 39 |
abstract void setProfile(CompressionProfile profile); /// ditto |
|---|
| 40 |
} |
|---|
| 41 |
|
|---|
| 42 |
/** |
|---|
| 43 |
* tango.compression.ZLib |
|---|
| 44 |
*/ |
|---|
| 45 |
|
|---|
| 46 |
//import tango.io.model.IConduit : IConduitFilter; |
|---|
| 47 |
//import tango.compression.model.ICompressionCodec; |
|---|
| 48 |
|
|---|
| 49 |
class ZLib : ICompressionCodec |
|---|
| 50 |
{ |
|---|
| 51 |
protected IConduitFilter next; |
|---|
| 52 |
|
|---|
| 53 |
private |
|---|
| 54 |
{ |
|---|
| 55 |
CompressionProfile profile_; |
|---|
| 56 |
int level_; // = ??? There's a default constant somewhere... |
|---|
| 57 |
z_stream zs; |
|---|
| 58 |
bool inited = false; |
|---|
| 59 |
} |
|---|
| 60 |
|
|---|
| 61 |
this() |
|---|
| 62 |
{ |
|---|
| 63 |
this(CompressionProfile.init); |
|---|
| 64 |
} |
|---|
| 65 |
|
|---|
| 66 |
this(CompressionProfile profile) |
|---|
| 67 |
{ |
|---|
| 68 |
this.profile = profile; |
|---|
| 69 |
} |
|---|
| 70 |
|
|---|
| 71 |
this(int level) |
|---|
| 72 |
{ |
|---|
| 73 |
this.level = level; |
|---|
| 74 |
} |
|---|
| 75 |
|
|---|
| 76 |
uint reader(void[] dst) |
|---|
| 77 |
{ |
|---|
| 78 |
} |
|---|
| 79 |
|
|---|
| 80 |
uint writer(void[] src) |
|---|
| 81 |
{ |
|---|
| 82 |
if( src.length == 0 ) |
|---|
| 83 |
// You never know what weird stuff people will do :p |
|---|
| 84 |
return next.writer(src); |
|---|
| 85 |
|
|---|
| 86 |
if( !inited ) |
|---|
| 87 |
initZlib(); |
|---|
| 88 |
|
|---|
| 89 |
// Create a new buffer for the compressed data to live in, and tell |
|---|
| 90 |
// zlib where it is. Also important is to make sure that the buffer |
|---|
| 91 |
// is deleted once we leave this function. |
|---|
| 92 |
ubyte[] destbuf = new ubyte[zs.avail_in + src.length]; |
|---|
| 93 |
scope(exit) delete destbuf; |
|---|
| 94 |
zs.next_out = destbuf.ptr; |
|---|
| 95 |
zs.avail_out = destbuf.length; |
|---|
| 96 |
|
|---|
| 97 |
if( zs.avail_in ) |
|---|
| 98 |
// If there is already some data ready to be compressed, then we |
|---|
| 99 |
// need to prepend that to our source data. |
|---|
| 100 |
src = zs.next_in[0 .. zs.avail_in] ~ src; |
|---|
| 101 |
|
|---|
| 102 |
// Tell zlib where the input data lives. |
|---|
| 103 |
zs.next_in = src.ptr; |
|---|
| 104 |
zs.avail_in = src.length; |
|---|
| 105 |
|
|---|
| 106 |
// Ok, compress that data; compress it good. |
|---|
| 107 |
auto err = deflate(&zs, Z_NO_FLUSH); |
|---|
| 108 |
if( err != Z_STREAM_END && err != Z_OK ) |
|---|
| 109 |
// Uh-oh; something went horribly, horribly wrong. |
|---|
| 110 |
zerror(err); |
|---|
| 111 |
|
|---|
| 112 |
// We truncate the output buffer since zlib may not have used all the |
|---|
| 113 |
// space we allocated; zs.avail_out is the number of bytes it didn't |
|---|
| 114 |
// use. |
|---|
| 115 |
destbuf = destbuf[0 .. $-zs.avail_out] |
|---|
| 116 |
destbuf.length = destbuf.length - zs.avail_out; |
|---|
| 117 |
|
|---|
| 118 |
// Ok; our original data is now compressed. We just need to feed that |
|---|
| 119 |
// to the next filter in the chain. |
|---|
| 120 |
return next.writer(destbuf); |
|---|
| 121 |
} |
|---|
| 122 |
|
|---|
| 123 |
int level() |
|---|
| 124 |
{ |
|---|
| 125 |
return level_; |
|---|
| 126 |
} |
|---|
| 127 |
|
|---|
| 128 |
int level(int level_) |
|---|
| 129 |
{ |
|---|
| 130 |
if( inited ) |
|---|
| 131 |
throw new Exception("Cannot change compression settings" |
|---|
| 132 |
" after (de)compression has begun!"); |
|---|
| 133 |
this.level_ = level_; |
|---|
| 134 |
return level_; |
|---|
| 135 |
} |
|---|
| 136 |
|
|---|
| 137 |
CompressionProfile profile() |
|---|
| 138 |
{ |
|---|
| 139 |
return profile_; |
|---|
| 140 |
} |
|---|
| 141 |
|
|---|
| 142 |
CompressionProfile profile(CompressionProfile profile_) |
|---|
| 143 |
{ |
|---|
| 144 |
switch( profile_ ) |
|---|
| 145 |
{ |
|---|
| 146 |
/+case CompressionProfile.None: |
|---|
| 147 |
level = Z_NO_COMPRESSION; |
|---|
| 148 |
break;+/ |
|---|
| 149 |
|
|---|
| 150 |
case CompressionProfile.Fast: |
|---|
| 151 |
level = Z_BEST_SPEED; |
|---|
| 152 |
break; |
|---|
| 153 |
|
|---|
| 154 |
case CompressionProfile.Normal: |
|---|
| 155 |
level = Z_DEFAULT_COMPRESSION; |
|---|
| 156 |
break; |
|---|
| 157 |
|
|---|
| 158 |
case CompressionProfile.Small: |
|---|
| 159 |
level = Z_BEST_COMPRESSION; |
|---|
| 160 |
break; |
|---|
| 161 |
|
|---|
| 162 |
default: |
|---|
| 163 |
throw new Exception("Invalid compression profile."); |
|---|
| 164 |
} |
|---|
| 165 |
|
|---|
| 166 |
return profile_; |
|---|
| 167 |
} |
|---|
| 168 |
|
|---|
| 169 |
void readProfile(IReader input) |
|---|
| 170 |
{ |
|---|
| 171 |
if( inited ) |
|---|
| 172 |
throw new Exception("Cannot change compression settings" |
|---|
| 173 |
" after (de)compression has begun!"); |
|---|
| 174 |
|
|---|
| 175 |
int level_; |
|---|
| 176 |
|
|---|
| 177 |
input.get (level_); |
|---|
| 178 |
level = level_; |
|---|
| 179 |
} |
|---|
| 180 |
|
|---|
| 181 |
void writeProfile(IWriter output) |
|---|
| 182 |
{ |
|---|
| 183 |
output.put (level); |
|---|
| 184 |
} |
|---|
| 185 |
|
|---|
| 186 |
void bind(IConduit conduit, IConduitFilter next) |
|---|
| 187 |
{ |
|---|
| 188 |
this.next = next; |
|---|
| 189 |
} |
|---|
| 190 |
|
|---|
| 191 |
void unbind() |
|---|
| 192 |
{ |
|---|
| 193 |
} |
|---|
| 194 |
|
|---|
| 195 |
private void initZlib() |
|---|
| 196 |
{ |
|---|
| 197 |
// |
|---|
| 198 |
} |
|---|
| 199 |
} |
|---|