]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/IMAPdeflate.pm
*deflate: drop invalid comment about rbuf
[public-inbox.git] / lib / PublicInbox / IMAPdeflate.pm
1 # Copyright (C) 2020 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3 # TODO: reduce duplication from PublicInbox::NNTPdeflate
4
5 # RFC 4978
6 package PublicInbox::IMAPdeflate;
7 use strict;
8 use warnings;
9 use 5.010_001;
10 use base qw(PublicInbox::IMAP);
11 use Compress::Raw::Zlib;
12 use Hash::Util qw(unlock_hash); # dependency of fields for perl 5.10+, anyways
13
14 my %IN_OPT = (
15         -Bufsize => 1024,
16         -WindowBits => -15, # RFC 1951
17         -AppendOutput => 1,
18 );
19
20 # global deflate context and buffer
21 my $zbuf = \(my $buf = '');
22 my $zout;
23 {
24         my $err;
25         ($zout, $err) = Compress::Raw::Zlib::Deflate->new(
26                 # nnrpd (INN) and Compress::Raw::Zlib favor MemLevel=9,
27                 # the zlib C library and git use MemLevel=8 as the default
28                 # -MemLevel => 9,
29                 -Bufsize => 65536, # same as nnrpd
30                 -WindowBits => -15, # RFC 1951
31                 -AppendOutput => 1,
32         );
33         $err == Z_OK or die "Failed to initialize zlib deflate stream: $err";
34 }
35
36 sub enable {
37         my ($class, $self, $tag) = @_;
38         my ($in, $err) = Compress::Raw::Zlib::Inflate->new(%IN_OPT);
39         if ($err != Z_OK) {
40                 $self->err("Inflate->new failed: $err");
41                 $self->write(\"$tag BAD failed to activate compression\r\n");
42                 return;
43         }
44         unlock_hash(%$self);
45         $self->write(\"$tag OK DEFLATE active\r\n");
46         bless $self, $class;
47         $self->{zin} = $in;
48 }
49
50 # overrides PublicInbox::NNTP::compressed
51 sub compressed { 1 }
52
53 sub do_read ($$$$) {
54         my ($self, $rbuf, $len, $off) = @_;
55
56         my $zin = $self->{zin} or return; # closed
57         my $doff;
58         my $dbuf = delete($self->{dbuf}) // '';
59         $doff = length($dbuf);
60         my $r = PublicInbox::DS::do_read($self, \$dbuf, $len, $doff) or return;
61
62         # assert(length($$rbuf) == $off) as far as NNTP.pm is concerned
63         # -ConsumeInput is true, so $dbuf is automatically emptied
64         my $err = $zin->inflate($dbuf, $rbuf);
65         if ($err == Z_OK) {
66                 $self->{dbuf} = $dbuf if $dbuf ne '';
67                 $r = length($$rbuf) and return $r;
68                 # nothing ready, yet, get more, later
69                 $self->requeue;
70         } else {
71                 delete $self->{zin};
72                 $self->close;
73         }
74         0;
75 }
76
77 # override PublicInbox::DS::msg_more
78 sub msg_more ($$) {
79         my $self = $_[0];
80
81         # $_[1] may be a reference or not for ->deflate
82         my $err = $zout->deflate($_[1], $zbuf);
83         $err == Z_OK or die "->deflate failed $err";
84         1;
85 }
86
87 sub zflush ($) {
88         my ($self) = @_;
89
90         my $deflated = $zbuf;
91         $zbuf = \(my $next = '');
92
93         my $err = $zout->flush($deflated, Z_FULL_FLUSH);
94         $err == Z_OK or die "->flush failed $err";
95
96         # We can still let the lower socket layer do buffering:
97         PublicInbox::DS::msg_more($self, $$deflated);
98 }
99
100 # compatible with PublicInbox::DS::write, so $_[1] may be a reference or not
101 sub write ($$) {
102         my $self = $_[0];
103         return PublicInbox::DS::write($self, $_[1]) if ref($_[1]) eq 'CODE';
104
105         my $deflated = $zbuf;
106         $zbuf = \(my $next = '');
107
108         # $_[1] may be a reference or not for ->deflate
109         my $err = $zout->deflate($_[1], $deflated);
110         $err == Z_OK or die "->deflate failed $err";
111         $err = $zout->flush($deflated, Z_FULL_FLUSH);
112         $err == Z_OK or die "->flush failed $err";
113
114         # We can still let the socket layer do buffering:
115         PublicInbox::DS::write($self, $deflated);
116 }
117
118 1;