Index: ext/zlib/zlib.c =================================================================== --- ext/zlib/zlib.c (revision 34377) +++ ext/zlib/zlib.c (working copy) @@ -56,6 +56,8 @@ max_uint(long n) #define sizeof(x) ((int)sizeof(x)) +static ID id_dictionaries; + /*--------- Prototypes --------*/ static NORETURN(void raise_zlib_error(int, const char*)); @@ -875,6 +877,15 @@ zstream_run(struct zstream *z, Bytef *sr if (z->stream.avail_in > 0) { zstream_append_input(z, z->stream.next_in, z->stream.avail_in); } + if (err == Z_NEED_DICT) { + VALUE self = (VALUE)z->stream.opaque; + VALUE dicts = rb_ivar_get(self, id_dictionaries); + VALUE dict = rb_hash_aref(dicts, rb_uint2inum(z->stream.adler)); + if (!NIL_P(dict)) { + rb_inflate_set_dictionary(self, dict); + continue; + } + } raise_zlib_error(err, z->stream.msg); } if (z->stream.avail_out > 0) { @@ -965,6 +976,7 @@ zstream_new(VALUE klass, const struct zs obj = Data_Make_Struct(klass, struct zstream, zstream_mark, zstream_free, z); zstream_init(z, funcs); + z->stream.opaque = (voidpf)obj; return obj; } @@ -1602,12 +1614,12 @@ rb_deflate_set_dictionary(VALUE obj, VAL * dup) itself. */ - - static VALUE rb_inflate_s_allocate(VALUE klass) { - return zstream_inflate_new(klass); + VALUE inflate = zstream_inflate_new(klass); + rb_ivar_set(inflate, id_dictionaries, rb_hash_new()); + return inflate; } /* @@ -1737,6 +1749,25 @@ do_inflate(struct zstream *z, VALUE src) } } +/* Document-method: Zlib::Inflate#add_dictionary + * + * call-seq: add_dictionary(string) + * + * Provide the inflate stream with a dictionary that may be required in the + * future. Multiple dictionaries may be provided. The inflate stream will + * automatically choose the correct user-provided dictionary based on the + * stream's required dictionary. + */ +static VALUE +rb_inflate_add_dictionary(VALUE obj, VALUE dictionary) { + VALUE dictionaries = rb_ivar_get(obj, id_dictionaries); + VALUE checksum = do_checksum(1, &dictionary, adler32); + + rb_hash_aset(dictionaries, checksum, dictionary); + + return obj; +} + /* * Document-method: Zlib::Inflate#inflate * @@ -4002,6 +4033,8 @@ Init_zlib() mZlib = rb_define_module("Zlib"); + id_dictionaries = rb_intern("@dictionaries"); + cZError = rb_define_class_under(mZlib, "Error", rb_eStandardError); cStreamEnd = rb_define_class_under(mZlib, "StreamEnd", cZError); cNeedDict = rb_define_class_under(mZlib, "NeedDict", cZError); @@ -4070,6 +4103,7 @@ Init_zlib() rb_define_singleton_method(mZlib, "inflate", rb_inflate_s_inflate, 1); rb_define_alloc_func(cInflate, rb_inflate_s_allocate); rb_define_method(cInflate, "initialize", rb_inflate_initialize, -1); + rb_define_method(cInflate, "add_dictionary", rb_inflate_add_dictionary, 1); rb_define_method(cInflate, "inflate", rb_inflate_inflate, 1); rb_define_method(cInflate, "<<", rb_inflate_addstr, 1); rb_define_method(cInflate, "sync", rb_inflate_sync, 1); Index: test/zlib/test_zlib.rb =================================================================== --- test/zlib/test_zlib.rb (revision 34377) +++ test/zlib/test_zlib.rb (working copy) @@ -185,6 +185,23 @@ if defined? Zlib assert_equal("foo", z.finish) end + def test_add_dictionary + dictionary = "foo" + + deflate = Zlib::Deflate.new + deflate.set_dictionary dictionary + compressed = deflate.deflate "foofoofoo", Zlib::FINISH + deflate.close + + out = nil + inflate = Zlib::Inflate.new + inflate.add_dictionary "foo" + + out = inflate.inflate compressed + + assert_equal "foofoofoo", out + end + def test_inflate s = Zlib::Deflate.deflate("foo") z = Zlib::Inflate.new