ZLib streams

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

ZLib streams

Robin Redeker-2
Hi!

I'm sorry to disturb you further, but I found something weird
while trying what we discussed:

   Eval [
      PackageLoader fileInPackage: #TCP.
      PackageLoader fileInPackage: #ZLib.
   ]
   Eval [
      |zr zw sock|
      sock := TCP.Socket remote: 'localhost' port: 12324.

      zw := RawDeflateStream on: sock.
      zr := RawInflateStream on: sock.

      zw nextPutAll: 'TESTABC'; syncFlush.

      [sock atEnd not] whileTrue: [
         | hunk |
         hunk := zr nextHunk.
         'hunk!' displayNl.
         hunk inspect.
      ].
   ]

I tested this with socat like this:

   socat TCP4-LISTEN:12324,reuseaddr PIPE

The problem is that this line blocks/hangs:

   zr := RawInflateStream on: sock.

It blocks because RawInflateStream>>#on: calls ZlibReadStream>>#fillBuffer
a few method calls down the stack. And fillBuffer actually does
call nextHunk to wait for data to arrive on the line.

If I rearrange the lines btw.:

      zw nextPutAll: 'TESTABC'; syncFlush.
      zr := RawInflateStream on: sock.

Then everything seems to be fine (because there is data for nextHunk).

Would it be possible to prevent the call to fillBuffer to not have
a blocking RawInflateStream creation and only have it block when _I_
call RawInflateStream>>#nextHunk?


Thanks!

Robin


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: ZLib streams

Paolo Bonzini

> Would it be possible to prevent the call to fillBuffer to not have
> a blocking RawInflateStream creation and only have it block when _I_
> call RawInflateStream>>#nextHunk?

Seems easy...


--- orig/packages/zlib/ZLibReadStream.st
+++ mod/packages/zlib/ZLibReadStream.st
@@ -145,9 +145,9 @@ position
  !ZlibReadStream methodsFor: 'private'!

  resetBuffer
+    ptr := 0.
      delta := 0.
-    endPtr := 0.
-    self fillBuffer!
+    endPtr := 0!

  initialize: aStream
      super initialize: aStream.


Paolo


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: ZLib streams

Robin Redeker-2
On Thu, Aug 23, 2007 at 09:55:30AM +0200, Paolo Bonzini wrote:

>
> >Would it be possible to prevent the call to fillBuffer to not have
> >a blocking RawInflateStream creation and only have it block when _I_
> >call RawInflateStream>>#nextHunk?
>
> Seems easy...
>
>
> --- orig/packages/zlib/ZLibReadStream.st
> +++ mod/packages/zlib/ZLibReadStream.st
> @@ -145,9 +145,9 @@ position
>  !ZlibReadStream methodsFor: 'private'!
>
>  resetBuffer
> +    ptr := 0.
>      delta := 0.
> -    endPtr := 0.
> -    self fillBuffer!
> +    endPtr := 0!
>
>  initialize: aStream
>      super initialize: aStream.

Ok, thats easy. But maybe a bit too easy :-)

That worked for my small example. But I've tried to implement it in my program
now and ran into some other problem:

  [socket atEnd not] whileTrue: [self handleData: inputStream nextHunk]

(Please note that I tried to use '[inputStream atEnd not]' first, but that
seemed to run into the same problem I'm going to describe below as a
call to ZlibReadStream>>#atEnd results in a call to ZlibReadStream>>#fillBuffer)

nextHunk does not seem to return properly. I guess the problem is in
 ZlibReadStream>>#fillBuffer:
       "Fill the output buffer, supplying data to zlib until it can actually
        produce something."
       | flush |
       delta := delta + endPtr.
       ptr := 0.
       [
           inBytes isNil ifTrue: [
               inBytes := self stream atEnd
                   ifTrue: [ #[] ]
                   ifFalse: [ self stream nextHunk ] ].

           flush := self stream atEnd ifTrue: [ 4 ] ifFalse: [ 0 ].
           endPtr := self processInput: flush size: inBytes size.
           endPtr = 0 ] whileTrue.

       "End of data, or zlib error encountered."
       endPtr = -1 ifTrue: [ self checkError ]! !

The following line completes when it received the syncFlushed data,
and an inspect revealed that there really was data.

           ifFalse: [ self stream nextHunk ] ].

However, I put in some displayNls and observed that the call:

           flush := self stream atEnd ifTrue: [ 4 ] ifFalse: [ 0 ].

Seemed to block. That wouldn't be a problem if there would be further
data waiting now, but it isn't (due to the fact that the first packet was a
'HELO' in my protocol and my software waits now for further input which
wont come).

You may also note that the call to ZlibReadStream>>#processInput:size: which
will result in a call to RawInflateStream>>#processInput:size: will receive the
wrong flag (0 Z_NO_FLUSH or 4 Z_FINISH, and not 2 Z_SYNC_FLUSH) when atEnd
finally returns.

I'm not familiar enough and currently don't have enough time to try to fix it
or propose a solution to this.


Robin


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: ZLib streams

Paolo Bonzini

> That worked for my small example. But I've tried to implement it in my program
> now and ran into some other problem:
>
>   [socket atEnd not] whileTrue: [self handleData: inputStream nextHunk]
>
> Seemed to block.

You cannot know if the stream is atEnd unless a) it closes, b) you have
one more byte.  So yes, #atEnd blocks (by design).

I would do something like

    [
      socket atEnd ifTrue: [ ^self ]
      self handleData: inputStream nextHunk
    ] whileFalse.

with #handleData: returning true when it finishes reading a packet.

> You may also note that the call to ZlibReadStream>>#processInput:size: which
> will result in a call to RawInflateStream>>#processInput:size: will receive the
> wrong flag (0 Z_NO_FLUSH or 4 Z_FINISH, and not 2 Z_SYNC_FLUSH) when atEnd
> finally returns.

As far as I could see from the zlib source code, Z_SYNC_FLUSH only
matters for deflating.  On inflation, it only looks for Z_NO_FLUSH,
Z_FINISH, Z_BLOCK.

Paolo


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: ZLib streams

Robin Redeker-2
On Thu, Aug 23, 2007 at 03:14:20PM +0200, Paolo Bonzini wrote:

>
> >That worked for my small example. But I've tried to implement it in my
> >program
> >now and ran into some other problem:
> >
> >  [socket atEnd not] whileTrue: [self handleData: inputStream nextHunk]
> >
> >Seemed to block.
>
> You cannot know if the stream is atEnd unless a) it closes, b) you have
> one more byte.  So yes, #atEnd blocks (by design).

Thanks okay. But what I mean was that nextHunk blocked even after it
received a SYNC_FLUSHed packet from the other end. See the rest of the
mail I wrote there.

It's also fine in that loop when atEnd blocks, if it comes back after data
arrived or the stream closed or whatever, but inputStream (being (a
RawInflateStream (which I maybe missed to say, sorry)) needs to give me the
packet it got (and it actually really got a _complete_ packet, which I found
out while debugging). The problem was that atEnd (on the socket) which is
called in fillBuffer blocks _after_ the compressed data was read from the socket.
Which resulted in nextHunk not to give me the finished packet it got.

In fact nextHunk returned the packet when I shut down one end of the TCP connection
(which resulted in the atEnd in fillBuffer to return).

> I would do something like
>
>    [
>      socket atEnd ifTrue: [ ^self ]
>      self handleData: inputStream nextHunk
>    ] whileFalse.
>
> with #handleData: returning true when it finishes reading a packet.

handleData accumulates the data it got and maybe finished parsing none,
one, two, ..., N protocol messages. And I don't see why I would like to
end my read-loop when I got packets from the socket.

Maybe my approach of accumulating the bytes from the network in a buffer
and reparse it when more bytes arrive to detect completed packets is not
practical in smalltalk.

My logic was: "read all packets from the socket until we get a disconnect":

    [inputStream "(or socket)" atEnd not]
        whileTrue: [self handleData: inputStream nextHunk]

> >You may also note that the call to ZlibReadStream>>#processInput:size:
> >which
> >will result in a call to RawInflateStream>>#processInput:size: will
> >receive the
> >wrong flag (0 Z_NO_FLUSH or 4 Z_FINISH, and not 2 Z_SYNC_FLUSH) when atEnd
> >finally returns.
>
> As far as I could see from the zlib source code, Z_SYNC_FLUSH only
> matters for deflating.  On inflation, it only looks for Z_NO_FLUSH,
> Z_FINISH, Z_BLOCK.

I don't know about the zlib source code, but I read the manual:
   http://www.zlib.net/manual.html#inflate

   If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much output
   as possible to the output buffer.
And:
   However if all decompression is to be performed in a single step (a single
   call of inflate), the parameter flush should be set to Z_FINISH. In this case
   all pending input is processed and all pending output is flushed ;

The manual might be wrong, I don't know.


Robin


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: ZLib streams

Paolo Bonzini-2

> Thanks okay. But what I mean was that nextHunk blocked even after it
> received a SYNC_FLUSHed packet from the other end. See the rest of the
> mail I wrote there.

Ah, I see now.  Could you try this further patch?

Paolo

--- orig/packages/zlib/ZLibReadStream.st
+++ mod/packages/zlib/ZLibReadStream.st
@@ -161,12 +161,10 @@ fillBuffer
     delta := delta + endPtr.
     ptr := 0.
     [
-        inBytes isNil ifTrue: [
-    inBytes := self stream atEnd
-        ifTrue: [ #[] ]
-        ifFalse: [ self stream nextHunk ] ].
+        (inBytes isNil and: [ self stream atEnd not ])
+    ifTrue: [ inBytes := self stream nextHunk ].
 
- flush := self stream atEnd ifTrue: [ 4 ] ifFalse: [ 0 ].
+ flush := inBytes isNil ifTrue: [ 4 ] ifFalse: [ 0 ].
         endPtr := self processInput: flush size: inBytes size.
  endPtr = 0 ] whileTrue.
 

_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: Re: ZLib streams

Robin Redeker-2
On Thu, Aug 23, 2007 at 04:45:23PM +0200, Paolo Bonzini wrote:
>
> >Thanks okay. But what I mean was that nextHunk blocked even after it
> >received a SYNC_FLUSHed packet from the other end. See the rest of the
> >mail I wrote there.
>
> Ah, I see now.  Could you try this further patch?

With pleasure!

Yes, it seems to work just fine now (together with the patch for
resetBuffer). I'm however still a bit concerned about the value
for 'flush', but if you think 4 and 0 are fine there I'll trust
you :-)

Thanks!


Robin


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk
Reply | Threaded
Open this post in threaded view
|

Re: ZLib streams

Paolo Bonzini
In reply to this post by Robin Redeker-2

> Is the XMLParser (in SAX mode) able to parse directly from a TCP.Socket
> in such kind of way that completed input leads to SAX events? I've seen
> a XMLParser>>#on: method... would this work:
>
>    socket := ...
>    XMLParser on: socket.
>    ...
> ?
>
> (That kind of feature would be required for parsing XMPP (the protocol
> that was formerly called 'Jabber')), but I'm not sure I'm ever going to
> implement a XMPP lib in smalltalk (I've already done that in Perl, and
> it was _not_ a pleasure :-))

I think so, yes.  You might have problems with lookahead there, too, but
apart from that it would be okay.

Paolo


_______________________________________________
help-smalltalk mailing list
[hidden email]
http://lists.gnu.org/mailman/listinfo/help-smalltalk