I've been tracking IOBuffer extensions back to String.Buffer, I'll present what I have shortly.
I suspect (but benchmarks will have to tell) that the String.Buffer implementation is not significantly slower than the current IOBuffer one (whilst supporting the full range of character widths).
Well. You have some minor optimizations to do:
| int perf(object buffer) | { | buffer->add("11"); | for(int i=0;i<10000; i++ ) | { | int l; | if( buffer->cut ) | { | l = (buffer[0]<<8) | buffer[1]; | buffer->cut(0,2,1); | } | else | l = buffer->read_int(2); | buffer->add(random_string(l)); | return sizeof(buffer); | } | }
perf( String.Buffer() );
Result 2: 325250971 Compilation: 624ns, Execution: 144.86s
perf( Stdio.IOBuffer() );
Result 3: 328787331 Compilation: 639ns, Execution: 194.06ms
(note that the length differs due to random_string)
However, reviewing the IOBuffer interface, I wonder about the following issues:
- Isn't it prudent to drop set_error_mode() and simply implement this functionality (the throw()) using a custom range_error() override?
Well. That would work, yes, I just simply did not remove the old version of throwing errors since it would most often be used using the simply buff->set_error_mode(1) when doing sub-parsing as I showed in the documentation for set_error_mode.
The need to do rather complex sub-classing for that common usecase seemed somewhat pointless.
- Why insist on lock()ing the buffer when subbuffers are active? Couldn't the code figure out by itself when a subbuffer exists and then decide on-demand and automatically when a copy needs to be made to transparently support the desired operation?
Not really, since the subbuffer only contains a pointer directly into the memory area of the main buffer, if the main buffer changes that using realloc or malloc it would be invalid, this could of course be fixed by adding a list of subbuffers to the main buffer, but then you run into issues with refcounting and such. Since the usecase where you have a subbuffer active and want to modify the main buffer is rather uncommon I thought it was OK that you have to call trim() on the subbuffer to do that.
Why not return IOBuffers practically everywhere, and then let the caller decide when and if to cast them to a string? It gets rid of excessive method diversification due to there needing to be a string and a buffer returning one. Returning a buffer is cheap, it doesn't copy the content.
Well, there is about a factor of 3 performance difference:
string perf2(object b) { while( b->read(1) ); } string perf3(object b) { while( b->read_buffer(1) ); }
perf2(Stdio.IOBuffer(mb100));
Result 5: 0 Compilation: 664ns, Execution: 92.19ms
perf3(Stdio.IOBuffer(mb100));
Result 6: 0 Compilation: 680ns, Execution: 173.68ms
Since that does not include the cast, which should be about as fast as the first read, it becomes about 3x slower.
And most of the time you actually want the string version, not the buffer version.