diff --git a/lib/modules/String.pmod/testsuite.in b/lib/modules/String.pmod/testsuite.in
index e07447bc7c899efcd706eba4edfadfc971f0430f..5082b60cfadb723c59f3d7cfaa020d81f5bd204a 100644
--- a/lib/modules/String.pmod/testsuite.in
+++ b/lib/modules/String.pmod/testsuite.in
@@ -3,25 +3,25 @@ START_MARKER
 test_eq([[ String.Buffer()->add("xxx") ]], 3)
 test_any([[
   String.Buffer s=String.Buffer();
-  s->add("xxx");
-  s->add("a", "b");
+  s->append("xxx");
+  s->append("a", "b");
   s->putchar(65);
-  s->add("B");
+  s->append("B");
   s->putchar(67);
   return (string)s;
 ]], "xxxabABC")
 test_any([[
   String.Buffer s=String.Buffer(2);
-  s->add("a");
-  s->add("b");
-  s->add("c");
+  s->append("a");
+  s->append("b");
+  s->append("c");
   return (string)s;
 ]], "abc")
 test_any([[
   String.Buffer s=String.Buffer(2);
-  s->add("abcdefg");
-  s->add("");
-  s->add("hij");
+  s->append("abcdefg");
+  s->append("");
+  s->append("hij");
   return s->get();
 ]], "abcdefghij")
 test_any([[
@@ -30,23 +30,23 @@ test_any([[
 ]], "")
 test_any([[
   String.Buffer s=String.Buffer();
-  s->add("xxx");
+  s->append("xxx");
   string t=s->get();
-  s->add("yyy");
+  s->append("yyy");
   string u=(string)s;
-  s->add("zzz");
+  s->append("zzz");
   return t+u+(string)s;
 ]], "xxxyyyyyyzzz")
 test_any([[
   String.Buffer s=String.Buffer();
-  s->add("xxx");
+  s->append("xxx");
   string t=s->get_copy();
-  s->add("yyy");
+  s->append("yyy");
   return t+(string)s;
 ]], "xxxxxxyyy")
 test_any([[
   String.Buffer s=String.Buffer();
-  s->add("abcde");
+  s->append("abcde");
   return sizeof(s);
 ]], 5)
 test_any([[
@@ -61,17 +61,17 @@ test_any([[
 ]], 1)
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("abc");
-  b->add("\400\500");
+  b->append("abc");
+  b->append("\400\500");
   b->putchar(256);
-  b->add("x");
+  b->append("x");
   return (string)b;
 ]], "abc\400\500\400x")
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("x");
+  b->append("x");
   b->sprintf("%x",1178);
-  b->add("y");
+  b->append("y");
   return (string)b;
 ]], "x49ay")
 test_any([[
@@ -88,7 +88,7 @@ test_any([[
 
 test_compile_error([[ String.Buffer()->sprintf("%d"); ]])
 test_eval_error([[ function f=String.Buffer()->sprintf; f("%d"); ]])
-test_eval_error([[ String.Buffer()->add("x",0); ]])
+test_eval_error([[ String.Buffer()->append("x",([])); ]])
 
 test_do([[
   String.Buffer b = String.Buffer();
@@ -111,194 +111,593 @@ test_do([[
 
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("abc");
+  b->append("abc");
   return b[1];
 ]], 98)
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("abc");
+  b->append("abc");
   return b[-1];
 ]], 99)
 test_runtime_error([[
   String.Buffer b = String.Buffer();
-  b->add("abc");
+  b->append("abc");
   return b[3];
 ]])
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("a\u0100a");
+  b->append("a\u0100a");
   return b[1];
 ]], 256)
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("a\U00010000a");
+  b->append("a\U00010000a");
   return b[1];
 ]], 65536)
 
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("aaa");
+  b->append("aaa");
   return b[1]='b';
 ]], 98)
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("aaa");
+  b->append("aaa");
   b[1]='b';
   return (string)b;
 ]], "aba")
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("aaa");
+  b->append("aaa");
   b[1]=256;
   return (string)b;
 ]], "a\u0100a")
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("aaa");
+  b->append("aaa");
   b[1]=65536;
   return (string)b;
 ]], "a\U00010000a")
 
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("1234");
+  b->append("1234");
   b = b[..1];
-  b->add("x");
+  b->append("x");
   return (string)b;
 ]], "12x")
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("1234");
+  b->append("1234");
   return (string)b[1..];
 ]], "234")
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("1234");
+  b->append("1234");
   return (string)b[1..2];
 ]], "23")
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("1234");
+  b->append("1234");
   return (string)b[..<1];
 ]], "123")
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("ab\u0100\u0101\U00010000\U00010001");
+  b->append("ab\u0100\u0101\U00010000\U00010001");
   return (string)b[0..1];
 ]], "ab")
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("ab\u0100\u0101\U00010000\U00010001");
+  b->append("ab\u0100\u0101\U00010000\U00010001");
   return (string)b[2..3];
 ]], "\u0100\u0101")
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("ab\u0100\u0101\U00010000\U00010001");
+  b->append("ab\u0100\u0101\U00010000\U00010001");
   return (string)b[4..5];
 ]], "\U00010000\U00010001")
 
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("ab\u0100\u0101\U00010000\U00010001cd");
+  b->append("ab\u0100\u0101\U00010000\U00010001cd");
   b->cut(4,5,1);
   return (string)b;
 ]], "ab\u0100\u0101cd")
 test_any([[
   String.Buffer b = String.Buffer();
-  b->add("ab\u0100\u0101\U00010000\U00010001cd");
+  b->append("ab\u0100\u0101\U00010000\U00010001cd");
   b->cut(2,5,1);
   return (string)b;
 ]], "abcd")
 test_any([[
-  String.Buffer b = String.Buffer();
-  b->add("ab\u0100\u0101\U00010000\U00010001cd");
-  return (string)b->cut(4,5) + "_" + (string)b;
+  String.Buffer b = String.Buffer(),c;
+  b->append("ab\u0100\u0101\U00010000\U00010001cd");
+  c=b->cut(4,5);
+  return (string)(c + "_" + b);
 ]], "\U00010000\U00010001_ab\u0100\u0101cd")
 test_any([[
-  String.Buffer b = String.Buffer();
-  b->add("ab\u0100\u0101\U00010000\U00010001cd");
-  return (string)b->cut(2,5) + "_" + (string)b;
+  String.Buffer b = String.Buffer(),c;
+  b->append("ab\u0100\u0101\U00010000\U00010001cd");
+  c=b->cut(2,5);
+  return (string)c + "_" + (string)b;
 ]], "\u0100\u0101\U00010000\U00010001_abcd")
 
 test_any([[
   String.Buffer b = String.Buffer(), a = String.Buffer();
-  b->add("abcd");a->add("abcd");
+  b->append("abcd");a->append("abcd");
   return b=="abcd" && b==a;
 ]], 1)
 test_any([[
   String.Buffer b = String.Buffer(), a = String.Buffer();
-  b->add("abcd");a->add("abcd");
+  b->append("abcd");a->append("abcd");
   return b>="abcd" && b>=a;
 ]], 1)
 test_any([[
   String.Buffer b = String.Buffer(), a = String.Buffer();
-  b->add("abcd");a->add("abcd");
+  b->append("abcd");a->append("abcd");
   return b<="abcd" && b<=a;
 ]], 1)
 test_any([[
   String.Buffer b = String.Buffer(), a = String.Buffer();
-  b->add("abce");a->add("abcd");
+  b->append("abce");a->append("abcd");
   return b>="abcd" && b>=a;
 ]], 1)
 test_any([[
   String.Buffer b = String.Buffer(), a = String.Buffer();
-  b->add("abce");a->add("abcd");
+  b->append("abce");a->append("abcd");
   return b<="abcd" || b<=a;
 ]], 0)
 test_any([[
   String.Buffer b = String.Buffer(), a = String.Buffer();
-  b->add("abce");a->add("abcd");
+  b->append("abce");a->append("abcd");
   return b>"abcd" && b>a;
 ]], 1)
 test_any([[
   String.Buffer b = String.Buffer(), a = String.Buffer();
-  b->add("abce");a->add("abcd");
+  b->append("abce");a->append("abcd");
   return b<"abcd" || b<a;
 ]], 0)
 test_any([[
   String.Buffer b = String.Buffer(), a = String.Buffer();
-  b->add("abcb");a->add("abcd");
+  b->append("abcb");a->append("abcd");
   return b=="abcd" || b==a;
 ]], 0)
 test_any([[
   String.Buffer b = String.Buffer(), a = String.Buffer();
-  b->add("abcb");a->add("abcd");
+  b->append("abcb");a->append("abcd");
   return b>="abcd" || b>=a;
 ]], 0)
 test_any([[
   String.Buffer b = String.Buffer(), a = String.Buffer();
-  b->add("abcb");a->add("abcd");
+  b->append("abcb");a->append("abcd");
   return b<="abcd" && b<=a;
 ]], 1)
 test_any([[
   String.Buffer b = String.Buffer(), a = String.Buffer();
-  b->add("abcb");a->add("abcd");
+  b->append("abcb");a->append("abcd");
   return b>"abcd" || b>a;
 ]], 0)
 test_any([[
   String.Buffer b = String.Buffer(), a = String.Buffer();
-  b->add("abcb");a->add("abcd");
+  b->append("abcb");a->append("abcd");
   return b<"abcd" || b<a;
 ]], 1)
 
 test_any([[
   String.Buffer a=String.Buffer(),b=String.Buffer();
-  a->add("Testing");
-  a->addat(0, "Is that sentence");
+  a->append("Testing");
+  a->appendat(0, "Is that sentence");
   a->putchar(',');
-  a->add(" the concluding three ", "words");
-  a->add(" `were left out'");
-  a->addat(3,"th","is");
+  a->append(" the concluding three ", "words");
+  a->append(" `were left out'");
+  a->appendat(3,"th","is");
   a[1]=a[10];
-  b->add("Test me harder");
-  b->add(a);
+  b->append("Test me harder");
+  b->append(a);
   if(b->cut(5,6) != "me")
     return 0;
   b->cut(0,11,1);
-  return a[3..6] == "this" && a->get_copy() ==(string)b && a->get()
-    == "In this sentence, the concluding three words `were left out'";
+  if(a[3..6] != "this" || a->get_copy() !=b)
+    return "X"+(string)b;
+  if(a->get()!="In this sentence, the concluding three words `were left out'")
+    return "X"+a->get();
+  return 1;
 ]],1)
 
+test_equal([[String.Buffer("hej")->read(1)]], "h")
+
+dnl sscanf
+
+test_equal([[String.Buffer("hej")->sscanf("%3c")]], ({6841706}))
+test_equal([[String.Buffer("hej")->match("%3c")]], 6841706)
+
+test_any([[
+  int i;
+  String.Buffer b=String.Buffer("hej hej hej");
+
+  while( array a = b->sscanf( "%*[ ]%[^ ]") )
+     i+= sizeof(a);
+  return i;
+]], 3)
+
+dnl sizeof()
+test_equal( sizeof(String.Buffer("hej")), sizeof("hej"))
+test_equal( sizeof(String.Buffer("ej")->append("alpha")), 7)
+test_equal( sizeof(String.Buffer()->sprintf("%4H","hej")), 7)
+
+dnl create(int)
+test_any([[
+    String.Buffer b = String.Buffer(1024*1024);
+    if( b->_size_object() != 1024*1024 )
+      return -1;
+   for( int i = 0; i<1024/4*1024; i++ )
+     b->append("test");
+   if( b->_size_object() != 1024*1024 )
+      return -2;
+
+   return 1;
+]], 1 )
+
+dnl create/add( system.memory )
+test_any([[
+  System.Memory a = System.Memory(__FILE__);
+  System.Memory b = System.Memory(100);
+
+  String.Buffer buf = String.Buffer(a);
+
+  if( buf->_size_object() != 0 )
+    return -1;
+
+  buf->append( b );
+
+  if( buf->_size_object() < sizeof(a)+sizeof(b) )
+    return -2;
+ if( !has_suffix((string)buf,"\0"*100) )
+    return -3;
+ return 1;
+]], 1)
+
+dnl create/add( iobuffer )
+test_any([[
+  String.Buffer a = String.Buffer("buffer 1");
+  String.Buffer b = String.Buffer("buffer 2");
+
+  String.Buffer buf = String.Buffer(a);
+
+  buf->append( b );
+
+  if( buf->_size_object() < sizeof(a)+sizeof(b) )
+    return -2;
+ if( (string)buf != "buffer 1buffer 2")
+    return -3;
+ return 1;
+]], 1)
+
+dnl add( char )
+test_any([[
+  string a = ("buffer 1");
+
+  String.Buffer buf = String.Buffer(a);
+
+  if( buf->_size_object() >= sizeof(a) )
+    return -1;
+
+  buf->append( 'b','u','f','f','e','r',' ','2' );
+
+  if( buf->_size_object() < sizeof(a)+8 )
+    return -2;
+ if( (string)buf != "buffer 1buffer 2")
+    return -3;
+ return 1;
+]], 1)
+
+dnl add(array( mix ))
+test_any([[
+  System.Memory tst  = System.Memory(2);
+  tst[0] = ' ';
+  tst[1] = '2';
+  String.Buffer er = String.Buffer();
+  mixed z = ({
+             'b','u','f',String.Buffer("f"),
+             er,
+             tst
+   });
+  er->append("er");
+
+  String.Buffer buf = String.Buffer("buffer 1");
+
+  buf->append( z, 'b','u','f','f',({'e','r',({' '}),'3'}) );
+
+  if( buf->_size_object() < 24 )
+    return -2;
+  if( (string)buf != "buffer 1buffer 2buffer 3")
+     return -3;
+
+ return 1;
+]], 1)
+
+dnl multi add( combo, also sort of a speed test )
+dnl will use on average 3Mb RAM
+test_any([[
+   String.Buffer two = String.Buffer();
+   array tst = ({
+    System.Memory(1),
+    two,
+    '3',
+    "4",
+    String.Buffer("5"),
+    ({ "6", ({'7',"","",""}) }),
+  });
+
+  two->append("2");
+
+  int l = 0;
+  String.Buffer res = String.Buffer();
+  for( int i = 0; i<102*1024; i++ )
+  {
+     array args = allocate( random(50) );
+     for( int j=0; j<sizeof( args ); j++ )
+     {
+       mixed e = random( tst );
+       if( intp( e ) )
+         l +=1;
+       else
+         l += sizeof( e );
+       args[j] = e;
+     }
+     res->append(@args);
+     if( sizeof( res ) != l )
+       return ([i:args, "l":l,"sizeof":sizeof(res)]);
+  }
+
+ return 1;
+]], 1)
+
+dnl basic zero-copy check
+
+test_any( [[
+  String.Buffer i = String.Buffer("zero copy");
+
+  i->append(" data goes here");
+  if( i->_size_object() == 0 )  return -4;
+
+  i->read();
+  if( i->_size_object() != 0 )  return -5;
+
+  i->append("zero copy");
+  if( i->_size_object() == 0 )  return -6;
+
+  i->append(" indeed"); /* note: shorter than first string. */
+  if( i->_size_object() == 0 )  return -7;
+
+  /*
+  crete - no move
+  add - realloc + move
+  read - no move (buffer now empty)
+  add - point to str
+  add -  move to malloc arena
+  add - no move
+  */
+  return 1;
+]], 1)
+
+dnl add_byte() add_int8()
+
+test_any( [[
+ String.Buffer b = String.Buffer();
+
+ for( int i=0; i<255; i++ )
+   b->add_byte( i )->add_int8(~i);
+
+  for( int i=0; i<255; i++ )
+     if( b[i*2] != i || b[i*2+1] != (~i&255))
+       return -i;
+  return 1;
+]], 1 )
+
+dnl add_short() add_int16()
+
+ test_any( [[
+ string ref = "";
+ String.Buffer b = String.Buffer();
+
+ for( int i=0; i<255; i++ )
+ {
+   ref += sprintf("%2c%2c", i, (~i)&0xffff );
+   b->add_short( i )->add_int16(~i);
+   if( (string)b != ref ) return ([
+     "i":i,
+     "ref":ref,
+     "b":(string)b
+  ]);
+  }
+  return 1;
+ ]], 1 )
+
+dnl add_int32()
+ test_any( [[
+ string ref = "";
+ String.Buffer b = String.Buffer();
+
+ for( int i=0; i<255; i++ )
+ {
+   ref += sprintf("%4c%4c", i, (~i)&0xffffffff );
+   b->add_int32( i )->add_int32(~i);
+   if( (string)b != ref ) return ([
+     "i":i,
+     "ref":ref,
+     "b":(string)b
+    ]);
+  }
+  return 1;
+ ]], 1 )
+
+
+dnl add_int( x, bits);
+
+ test_any( [[
+ string ref = "";
+ String.Buffer b = String.Buffer();
+
+ for( int j=1; j<255; j++ )
+ {
+   string fmt = "%"+j+"c";
+   for( int i=0; i<255; i+=random(10) )
+   {
+     ref += sprintf(fmt, i );
+     b->add_int( i, j );
+    }
+    if( (string)b != ref ) return ([
+      "width":j,
+      "ref":ref,
+      "b":(string)b
+     ]);
+    b->clear();
+    ref = "";
+  }
+  return 1;
+ ]], 1 )
+
+
+dnl add_int( bignum, bits);
+
+test_any( [[
+ string ref = "";
+ String.Buffer b = String.Buffer();
+
+ for( int j=1; j<255; j++ )
+ {
+   int i = random((1<<(j*8))-1);
+   ref += sprintf("%"+j+"c", i );
+   b->add_int( i, j );
+   if( (string)b != ref ) return ([
+     "i":i,  "j":j,
+     "ref":ref,
+     "b":(string)b
+    ]);
+  }
+  return 1;
+ ]], 1 )
+
+
+dnl add_hstring( str, bytes )
+
+test_error( [[
+ String.Buffer b = String.Buffer();
+  b->add_hstring("testing", 0 );
+ ]], 1 )
+
+test_error( [[
+ String.Buffer b = String.Buffer();
+ b->add_hstring(" "*256, 1 );
+]], 1 )
+
+test_any( [[
+ array(String.Buffer) b = allocate(10,String.Buffer)();
+ for( int i = 0; i<1000; i++ )
+ {
+   string chunk = " "*random(255);
+
+   for( int w = 0; w<9; w++ )
+     b[w]->add_hstring( chunk, w+1 );
+  }
+
+ for( int i = 0; i<1000; i++ )
+ {
+   string chunk = b[0]->read_hstring(1);
+   for( int w = 1; w<9; w++ )
+   {
+     if( b[w]->read_hstring( w+1 ) != chunk )
+        return ({w+1,strlen(chunk)});
+   }
+ }
+ return 1;
+]], 1);
+
+test_any( [[
+   String.Buffer b = String.Buffer();
+   b->append( "\0\1" );
+   if( b->read_hstring( 2 ) )
+     return -1;
+   if( sizeof(b) != 2 )
+     return -2;
+   b->append("x");
+   if( b->read_hstring( 2 ) != "x" )
+     return -3;
+   return 1;
+]], 1);
+
+dnl add_hstring( obj*, bits )
+
+test_any( [[
+ array(String.Buffer) b = allocate(10,String.Buffer)();
+ System.Memory chunk = System.Memory(255);
+ for( int i = 0; i<1000; i++ )
+ {
+   for( int w = 0; w<9; w++ )
+     b[w]->add_hstring( chunk, w+1 );
+  }
+
+ for( int i = 0; i<1000; i++ )
+ {
+   string chunk = b[0]->read_hstring(1);
+   for( int w = 1; w<9; w++ )
+   {
+     if( b[w]->read_hstring( w+1 ) != chunk )
+        return ({w+1,strlen(chunk)});
+   }
+ }
+ return 1;
+]], 1);
+
+dnl add_ints
+
+test_any( [[
+ array q = allocate(100000,random)(8438439834983948938439849834983498349834983498);
+ String.Buffer i = String.Buffer();
+ i->add_ints( q, 20 );
+ return 1;
+]], 1);
+
+test_error( [[
+ array q = ({8438439834983948938439849834983498349834983498});
+ String.Buffer i = String.Buffer();
+ i->add_ints( q, 7 );
+ return 1;
+]], 1);
+
+
+test_any( [[
+ array q = ({10,20,30});
+ String.Buffer i = String.Buffer();
+ i->add_ints( q, 7 );
+ return 1;
+]], 1);
+test_error( [[
+ array q = ({10,"20",30});
+ String.Buffer i = String.Buffer();
+ i->add_ints( q, 7 );
+ return 1;
+]], 1);
+
+test_any( [[
+ array q = ({10,"20",30});
+ String.Buffer i = String.Buffer();
+ catch{i->add_ints( q, 7 );};
+ if( sizeof(i) )
+   return -1;
+ return 1;
+]], 1);
+
+dnl lock
+
+dnl cast
+
+dnl indexing
+
+dnl set_error_mode
+
+dnl _sprintf
 
 test_eq([[ String.count( "", "a" ) ]], 0)
 test_eq([[ String.count( "aaa", "a" ) ]], 3)
diff --git a/src/builtin.cmod b/src/builtin.cmod
index f257f6b57ac7cb7299f41413d21a6e8737cd6526..fb05bc33993dc47e4364a9741df4c5676a17bc1f 100644
--- a/src/builtin.cmod
+++ b/src/builtin.cmod
@@ -31,6 +31,10 @@
 #include "pikecode.h"
 #include "opcodes.h"
 #include "whitespace.h"
+#include "fdlib.h"
+
+#include "modules/_Stdio/file_machine.h"
+#include "modules/_Stdio/file.h"
 
 #include <ctype.h>
 #include <errno.h>
@@ -2971,19 +2975,497 @@ PIKEFUN array(mixed) backtrace()
  */
 PIKECLASS Buffer
 {
+  CVAR char* buffer;
+  CVAR size_t offset;
+  CVAR size_t len;
+  CVAR unsigned shift;
   CVAR struct string_builder str;
-  CVAR int initial;
+  CVAR unsigned readonly;
+  CVAR size_t initial;
+
+  CVAR struct object *source;
+  /* CVAR program *error_mode; */
+  CVAR unsigned error_mode;
+
+  static INLINE unsigned min_magnitude(const unsigned c)
+  { 
+    return c<256 ? 0 : c<65536 ? 1 : 2;
+  }
+
+  static int buffer_range_error(int howmuch)
+  {
+    if( THIS->error_mode )
+      Pike_error("Trying to read %d outside allowed range\n", howmuch);
+    return 0;
+  }
+
+  static struct pike_string*buffer_mkspace(ptrdiff_t diff, unsigned shift)
+  {
+    struct Buffer_struct *str = THIS;
+    struct pike_string *s = str->str.s;
+
+    if (str->readonly)
+      Pike_error("Attempt to modify readonly buffer.\n");
+
+    if (!s || s->refs!=1) {
+      shift |= str->shift;
+      shift = shift & ~(shift >> 1);
+      init_string_builder_alloc(&str->str, str->len+diff, shift);
+      generic_memcpy(MKPCHARP(str->str.s->str, shift),
+       MKPCHARP(str->buffer, str->shift), str->len);
+      if (s)
+        sub_ref(s);
+      str->offset = 0;
+      goto fixptr;
+    } else {
+      if (!(s->flags & STRING_NOT_SHARED))
+        unlink_pike_string (s);
+      string_build_mkspace(&str->str, diff, shift);
+fixptr:
+      s = str->str.s;
+      str->buffer = s->str + (str->offset << (str->shift = s->size_shift));
+    }
+
+    if( str->source ) {
+      free_object( str->source );
+      str->source = 0;
+    }
+
+    return s;
+  }
+
+  static void buffer_lock()
+  {
+    THIS->readonly++;
+  }
+
+  static void buffer_release()
+  {
+    THIS->readonly--;
+  }
 
   PIKEFUN int _size_object()
   {
-    RETURN THIS->str.malloced;
+    struct Buffer_struct *str = THIS;
+    unsigned retval = 0;
+
+    if (str->str.s) {
+      retval = str->str.s->refs;
+      retval = (str->str.malloced+retval-1)/retval;
+    }
+    push_int( retval );
+  }
+
+  /*! @decl void set_error_mode(int m)
+   *!
+   *! Set the error mode of this buffer to @[m].
+   *!
+   *! If true operations that would normally return 0
+   *! (like trying to read too much) will instead thrown an error.
+   *!
+   *! This is useful when parsing received data, you do not have to
+   *! verify that each and every read operation suceeds.
+   *!
+   *! However, the non-error mode is more useful when checking to see
+   *! if a packet/segment/whatever has arrived.
+   *!
+   *! The thrown error object will have the constant buffer_error set
+   *! to a non-false value.
+   *!
+   *! @example
+   *! @code
+   *! void read_callback(int i, string new_data)
+   *! {
+   *!   inbuffer->add( new_data );
+   *!
+   *!   while( String.Buffer packet = inbuffer->read_hbuffer(2) )
+   *!   {
+   *!     packet->set_error_mode(Buffer.THROW_ERROR);
+   *!     if( mixed e = catch( handle_packet( packet ) ) )
+   *!       if( e->buffer_error )
+   *!         protocol_error(); // illegal data in packet
+   *!       else
+   *!         throw(e); // the other code did something bad
+   *!    }
+   *! }
+   *!
+   *!
+   *! void handle_packet( String.Buffer pack )
+   *! {
+   *!   switch( pack->read_int8() )
+   *!   {
+   *!     ...
+   *!   case HEADER_FRAME:
+   *!     int num_headers = pack->read_int32();
+   *!     for( int i = 0; i<num_headers; i++ )
+   *!      headers[pack->read_hstring(2)] = pack->read_hstring(2);
+   *!     ...
+   *!   }
+   *! }
+   *! @endcode
+   */
+  PIKEFUN void set_error_mode(int m)
+  {
+    /* FIXME: buffer_error and Buffer.THROW_ERROR still need to
+       be implemented */
+    THIS->error_mode = m;
+    pop_stack();
+  }
+
+  /*! @decl protected bool range_error( int howmuch )
+   *!
+   *! This function is called when an attempt is made to read out of bounds.
+   *! The default implementation simply returnns 0.
+   *!
+   *! Override this function to change the behaviour
+   *!
+   *! The argument @[howmuch] indicates how much data is needed:
+   *!
+   *! @dl
+   *! @item int(1..) howmuch
+   *! Need @[howmuch] bytes more
+   *! @item int(0..0) howmuch
+   *!  The amount of data needed is not certain.
+   *!  This most often happens when  @[sscanf] or @[read_json] is used
+   *! @item int(..-1) howmuch=...
+   *!  Tried to @[unread] X bytes. There is usually no way to satisfy
+   *!  the requested range.
+   *!
+   *! Fill the @[unread()] buffer by passing data to it, then
+   *! @[consume()] it again.
+   *! @enddl
+   *!
+   *! @returns
+   *!
+   *! @[true] if the operation should be retried, @[false] otherwise.
+   *!
+   *! Do not return true unless you have added data to the buffer,
+   *! doing so could result in an infinite loop (since no data is
+   *! added, the range_error will be called again immediately).
+   */
+  PIKEFUN int(0..1) range_error( int howmuch )
+      flags ID_PROTECTED;
+  {
+    /* Default: throw error if so desired, otherwise return 0. */
+    pop_n_elems(args);
+    push_int(0);
+  }
+
+  struct sysmem {
+    unsigned char *p;
+    size_t size;
+  };
+
+  static struct program *shm_program;
+  static struct sysmem *system_memory(struct object *o)
+  {
+    if( !shm_program ) {
+      push_text("System.Memory");
+      SAFE_APPLY_MASTER("resolv", 1);
+      shm_program = program_from_svalue(Pike_sp - 1);
+      Pike_sp--;
+      if (!shm_program)
+        return 0;
+    }
+    return o->prog == shm_program ? get_storage( o, shm_program ) : 0;
+  };
+
+  static size_t buffer_findsize(unsigned args, struct svalue*pargs,
+    unsigned *pshift)
+  {
+    unsigned j,shift = 0;
+    size_t sum = 0;
+
+    for( j=args ; j-- ; ++pargs)
+      switch(TYPEOF(*pargs)) {
+        case PIKE_T_STRING:
+        {
+          struct pike_string *a = pargs->u.string;
+          sum += a->len;
+          shift |= a->size_shift;
+          break;
+        }
+        case PIKE_T_INT:
+          sum++;
+          shift |= min_magnitude(pargs->u.integer);
+          break;
+        case PIKE_T_ARRAY:
+        {
+          struct array *arp = pargs->u.array;
+	  DECLARE_CYCLIC();
+
+	  if (BEGIN_CYCLIC(Pike_fp->current_object, arp))
+	    Pike_error("Attempt to append a cyclic array to a buffer.\n");
+
+          sum += buffer_findsize(arp->size, arp->item, &shift);
+
+	  END_CYCLIC();
+          break;
+        }
+        case PIKE_T_OBJECT:
+        {
+          struct sysmem *sm;
+          if(pargs->u.object->prog == Buffer_program) {
+            struct Buffer_struct *bstr = OBJ2_BUFFER(pargs->u.object);
+            sum += bstr->len;
+            shift |= bstr->shift;
+            break;
+          } else if (sm = system_memory(pargs->u.object)) {
+            sum += sm->size;
+            break;
+          }
+        }
+        default:
+          SIMPLE_BAD_ARG_ERROR("append", args-j,
+           "string|int|String.Buffer|System.Memory"
+           "|array(string|int|String.Buffer|System.Memory)");
+      }
+    *pshift |= shift;
+    return sum;
+  }
+
+  static char* buffer_memcpy(char*dest, unsigned shift,
+    unsigned args, struct svalue*pargs)
+  {
+    unsigned j;
+
+    for( j=args ; j-- ; ++pargs) {
+      PCHARP b;
+      size_t blen;
+
+      switch(TYPEOF(*pargs)) {
+        case PIKE_T_STRING:
+        {
+          struct pike_string *bs;
+          b = MKPCHARP_STR(bs = pargs->u.string);
+          blen = bs->len;
+          break;
+        }
+        case PIKE_T_INT:
+        {
+          unsigned ch = pargs->u.integer;
+          switch( shift ) {
+            case 0:  *(p_wchar0*)dest = ch; break;
+            case 1:  *(p_wchar1*)dest = ch; break;
+            default: *(p_wchar2*)dest = ch; break;
+          }
+          blen = 1;
+          goto inc;
+        }
+        case PIKE_T_ARRAY:
+        {
+          struct array *arp = pargs->u.array;
+          dest = buffer_memcpy(dest, shift, arp->size, arp->item);
+          continue;
+        }
+        default:
+        {
+          struct sysmem *sm;
+          if(pargs->u.object->prog == Buffer_program) {
+            struct Buffer_struct *bstr = OBJ2_BUFFER(pargs->u.object);
+            b = MKPCHARP(bstr->buffer, bstr->shift);
+            blen = bstr->len;
+            break;
+          } else if (sm = system_memory(pargs->u.object)) {
+            b = MKPCHARP(sm->p, 0);
+            blen = sm->size;
+            break;
+          }
+        }
+      }
+      generic_memcpy(MKPCHARP(dest, shift), b, blen);
+inc:  dest += blen<<shift;
+    }
+    return dest;
+  }
+
+  /*! @decl Buffer appendat(int(0..) pos,
+   *!    string|int|Buffer|object|array(string|int|Buffer|object) ... data)
+   *!
+   *!   Adds @[data] to the buffer, starting at position @[pos].
+   *!   It overwrites existing content at that offset, never truncates the
+   *!   buffer, possibly extends the buffer if the new content does not fit.
+   *!
+   *! @returns
+   *!   Returns the buffer itself.
+   *!
+   *! @note
+   *!   If the starting position @[pos] extends beyond the end of the
+   *!   current buffer content, the gap will be filled with NUL-characters.
+   *!
+   *! @seealso
+   *!   @[append()], @[add()]
+   */
+  PIKEFUN Buffer appendat(int(0..) pos,
+   string|int|Buffer|object|array(string|int|Buffer|object) ... data )
+  {
+    struct Buffer_struct *str = THIS;
+    struct svalue*pargs;
+    struct pike_string *s;
+    ptrdiff_t sum;
+    int j;
+    unsigned shift = 0;
+
+    if (pos < 0)
+      SIMPLE_BAD_ARG_ERROR("appendat", 1, "int(0..)");
+
+    args--;
+    sum = pos + buffer_findsize(args, Pike_sp-args, &shift);
+
+    shift |= str->shift;
+    shift = shift & ~(shift >> 1);
+    j = sum - str->len;
+    if (j>0) {
+      if (str->initial > j && str->initial > str->str.malloced)
+        j = str->initial;
+      s = buffer_mkspace(j, shift);
+    }
+    else
+      s = buffer_mkspace(0, shift);
+    /* We know it will be a string that really is this wide. */
+    str->str.known_shift = shift;
+
+    if (str->len < pos)				 /* Clear the padding */
+      MEMSET(str->buffer + (str->len << shift), 0, (pos - str->len) << shift);
+
+    {
+      char*dest = buffer_memcpy(s->str+((pos+str->offset)<<shift), shift,
+       args, Pike_sp-args);
+
+      if (s->len < (pos = (dest - s->str)>>shift))
+        s->len = pos;
+      if (str->len < (pos -= str->offset))
+        str->len = pos;
+    }
+
+    pop_n_elems( args+1 );
+    ref_push_object(Pike_fp->current_object);
+  }
+
+  /*! @decl Buffer append(
+   *!   string|int|Buffer|object|array(string|int|Buffer|object) ... data )
+   *!
+   *!   Adds @[data] to the buffer.
+   *!
+   *! @returns
+   *!   Returns the buffer itself.
+   *!
+   *! @seealso
+   *!   @[appendat()], @[add()]
+   */
+  PIKEFUN Buffer append(
+   string|int|Buffer|object|array(string|int|Buffer|object) ... data )
+  {
+    push_int(THIS->len);
+    stack_revroll(++args);
+    f_Buffer_appendat(args);
+  }
+
+  /*! @decl int add(
+   *!   string|int|Buffer|object|array(string|int|Buffer|object) ... data )
+   *!
+   *!   Adds @[data] to the buffer.
+   *!
+   *! @returns
+   *!   Returns the size of the buffer.
+   *!
+   *! @seealso
+   *!   @[appendat()]
+   */
+  PIKEFUN int add(
+   string|int|Buffer|object|array(string|int|Buffer|object) ... data )
+  {
+    f_Buffer_append(args);
+    pop_stack();
+    push_int(THIS->len);
+  }
+
+  /*! @decl string get_copy()
+   *!
+   *!   Get the data from the buffer. Significantly slower than @[get],
+   *!   but does not clear the buffer.
+   *!
+   *! @seealso
+   *!   @[get()]
+   */
+  PIKEFUN string get_copy()
+  {
+    struct Buffer_struct *str = THIS;
+
+    if(str->len) {
+      struct pike_string *s;
+      s = make_shared_binary_pcharp(MKPCHARP(str->buffer, str->shift),
+           str->len);
+      if( Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT )
+          s->flags |= STRING_CLEAR_ON_EXIT;
+      push_string(s);
+    } else 
+      push_empty_string();
+  }
+
+  /*! @decl string|void read(void|int n)
+   *!
+   *!   Read and remove @[n] characters of data from the beginning
+   *!   of the buffer.
+   *!   If there are less than @[n] characters available, nothing
+   *!   will be removed and the function will return 0.
+   *!
+   *!   When called without arguments, this is equivalent to @[get()].
+   *!
+   *! @seealso
+   *!   @[get()], @[clear()]
+   */
+  PIKEFUN string|void read( void|int(0..) n )
+  {
+    struct Buffer_struct *str = THIS;
+    struct pike_string *s;
+    if (args) {
+      int want = n->u.integer;
+      pop_stack();
+      while (want<0 || want>str->len)
+        if (!buffer_range_error( want )) {
+          push_undefined();
+          return;
+        }
+      if (want != str->len) {
+        push_string(make_shared_binary_pcharp(
+         MKPCHARP(str->buffer, str->shift), want));
+        str->buffer += want<<=str->shift;
+        str->offset += want;
+        str->len -= want;
+        return;
+      }
+    }
+    buffer_mkspace(0, 0);
+    if (str->offset) {
+      memmove( str->str.s->str, str->buffer, str->len<<str->shift);
+      str->str.s->len = str->len;
+    }
+    s = finish_string_builder( &str->str );
+    if( Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT )
+      s->flags |= STRING_CLEAR_ON_EXIT;
+    push_string(s);
+    str->str.s = 0;
+    str->len = 0;
   }
 
-  void f_Buffer_get_copy( INT32 args );
-  void f_Buffer_get( INT32 args );
-  void f_Buffer_add( INT32 args );
+  /*! @decl string get()
+   *!
+   *!   Get the data from the buffer.
+   *!
+   *! @note
+   *!   This will clear the data in the buffer
+   *!
+   *! @seealso
+   *!   @[get_copy()], @[clear()]
+   */
+  PIKEFUN string get( )
+  {
+    f_Buffer_read(0);
+  }
 
   /*! @decl void create(void|int(1..) initial_size)
+   *! @decl void create(string|Buffer|System.Memory value)
    *!
    *!   Initializes a new buffer.
    *!
@@ -2992,11 +3474,58 @@ PIKECLASS Buffer
    *!   the operation of @[add()] (slightly) by passing the size to this
    *!   function.
    */
-  PIKEFUN void create( int|void size )
+  PIKEFUN void create( string|object|int|void value )
   {
     struct Buffer_struct *str = THIS;
-    init_string_builder_alloc(&str->str,
-     str->initial = size ? MINIMUM(size->u.integer, 1 ) : 256 , 0);
+    struct pike_string *s;
+    struct sysmem *sm;
+
+    if( str->buffer )
+      Pike_error("Can not initialise twice.\n");
+    str->initial = 256;
+    if (!args)
+      goto definit;
+    switch(TYPEOF(*value)) {
+      default:
+        Pike_error("Unsupported object type\n");
+      case PIKE_T_INT:
+        str->initial = MAX(value->u.integer, 1 );
+definit:
+        init_string_builder_alloc(&str->str, str->initial, 0);
+        str->buffer = str->str.s->str;
+        break;
+      case PIKE_T_STRING:
+        s = value->u.string;
+        if (s->refs == 1)
+          /* Unlink the string and use it as buffer directly. */
+          unlink_pike_string (s);
+        else
+          add_ref(s);
+        str->str.s = s;
+        str->len = str->str.malloced = s->len;
+        str->shift = str->str.known_shift = s->size_shift;
+        str->buffer = s->str;
+        break;
+      case PIKE_T_OBJECT:
+        if (value->u.object->prog == Buffer_program) {
+          struct Buffer_struct *str2 = OBJ2_BUFFER(value->u.object);
+          str->buffer = str2->buffer;
+          str->offset = str2->offset;
+          str->len = str2->len;
+          str->shift = str2->shift;
+          if (str->source = str2->source)
+            add_ref(str->source);
+          str->str = str2->str;
+          if (str->str.s)
+            add_ref(str->str.s);
+        }
+        else if (sm = system_memory(value->u.object)) {
+          add_ref(str->source = value->u.object);
+          str->buffer = sm->p;
+          str->len = sm->size;
+        }
+        break;
+    }
   }
 
   /*! @decl string _sprintf( int flag, mapping flags )
@@ -3005,37 +3534,52 @@ PIKECLASS Buffer
    */
   PIKEFUN string _sprintf( int flag, mapping flags )
   {
+    pop_n_elems( args );
     switch( flag )
     {
       case 'O':
-	{
-	  struct pike_string *res;
+        {
 	  struct Buffer_struct *str = THIS;
-	  push_text( "Buffer(%d /* %d */)" );
-	  push_int(str->str.s->len);
+	  push_text( "Buffer(%d-%d /* %d */)" );
+	  push_int(str->len);
+	  push_int(str->offset);
 	  push_int(str->str.malloced);
-	  f_sprintf( 3 );
-	  dmalloc_touch_svalue(Pike_sp-1);
-	  res = Pike_sp[-1].u.string;
-	  Pike_sp--;
-	  RETURN res;
-	}
-
+	  f_sprintf( 4 );
+	  break;
+        }
       case 's':
-	{
-	  pop_n_elems( args );
-	  if( Pike_fp->current_object->refs != 1 )
-	    f_Buffer_get_copy( 0 );
-	  else
-	    f_Buffer_get( 0 );
-	}
-	return;
-	
+	if( Pike_fp->current_object->refs != 1
+         || THIS->str.s->refs != 1)
+	  f_Buffer_get_copy( 0 );
+	else
+	  f_Buffer_read( 0 );
+	break;
+
       case 't':
-	RETURN make_shared_binary_string("Buffer",6);
+	push_string(make_shared_binary_string("Buffer",6));
+	break;
+
+      default:
+        push_undefined();
     }
-    pop_n_elems( args );
-    push_undefined();
+  }
+
+  /*! @decl string _encode()
+   *! @decl void _decode(string x)
+   *!
+   *! Encode and decode @[String.Buffer] objects.
+   *! Only the buffer data is kept, no other state is saved.
+   */
+  PIKEFUN string _encode()
+  {
+    f_Buffer_read( 0 );
+  }
+
+  PIKEFUN void _decode(string x)
+  {
+    if( THIS->str.s->len )
+      Pike_error("Cannot initialise twice.\n");
+    f_Buffer_add( 1 );
   }
 
   /*! @decl string|int cast( string type )
@@ -3048,10 +3592,11 @@ PIKECLASS Buffer
     if( type == literal_string_string )
     {
       pop_stack();
-      if( Pike_fp->current_object->refs != 1 )
+      if( Pike_fp->current_object->refs != 1
+       || THIS->str.s->refs != 1)
 	f_Buffer_get_copy( 0 );
       else
-	f_Buffer_get( 0 );
+	f_Buffer_read( 0 );
       return;
     }
 
@@ -3059,10 +3604,11 @@ PIKECLASS Buffer
     {
       struct Buffer_struct *str = THIS;
       pop_stack();
-      if( Pike_fp->current_object->refs != 1 )
+      if( Pike_fp->current_object->refs != 1
+       || THIS->str.s->refs != 1)
 	f_Buffer_get_copy( 0 );
       else
-	f_Buffer_get( 0 );
+	f_Buffer_read( 0 );
       o_cast_to_int( );
       return;
     }
@@ -3070,13 +3616,13 @@ PIKECLASS Buffer
     pop_stack();
     push_undefined();
   }
-  
+
   /*! @decl int `[](int index)
    */
   PIKEFUN int `[](int index)
   {
-    struct pike_string *s = THIS->str.s;
-    unsigned len = s->len;
+    struct Buffer_struct *str = THIS;
+    unsigned len = str->len;
 
     if (index<0)
       index += len;
@@ -3084,7 +3630,7 @@ PIKECLASS Buffer
       index_error("Buffer->`[]", Pike_sp, args, NULL, Pike_sp,
           "Index %"PRINTPIKEINT"d is out of array range 0..%d,\n",
           index, len-1);
-    RETURN generic_extract(s->str, s->size_shift, index);
+    RETURN generic_extract(str->buffer, str->shift, index);
   }
 
   /*! @decl Buffer `[..](int start, int start_type, int end, int end_type)
@@ -3092,10 +3638,9 @@ PIKECLASS Buffer
   PIKEFUN Buffer `[..](int start, int start_type, int end, int end_type)
   {
     struct Buffer_struct *str = THIS;
-    struct pike_string *s = str->str.s;
-    unsigned len = s->len, shift = s->size_shift;
-    char *p;
-
+    size_t len = str->len;
+    unsigned shift = str->shift;
+    struct pike_string *s;
     struct object *res;
     struct Buffer_struct *str2;
 
@@ -3110,15 +3655,19 @@ PIKECLASS Buffer
       start = 0;
     if (end >= len)
       end = len-1;
-    str2->initial = end -= start-1;
-    init_string_builder_alloc (&str2->str, end, shift);
-    p = s->str;
-    memcpy ((s = str2->str.s)->str, p+(start<<shift), end<<shift);
-    s->str[end<<shift] = 0;			// Ensure NUL-termination
-    s->len = end;
+
+    if (str->str.s) {
+      add_ref(str->str.s);
+      str2->str = str->str;
+    }
+    str2->offset = str->offset+start;
+    str2->len = end-start+1;
+    str2->buffer = str->buffer+(start<<shift);
+    str2->shift = str->shift;
+    str2->initial = str->initial;
     if( (Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT) )
-        res->flags |= OBJECT_CLEAR_ON_EXIT;
-    push_object (res);
+      res->flags |= OBJECT_CLEAR_ON_EXIT;
+    push_object(res);
   }
 
   /*! @decl int `[]=(int index, int ch)
@@ -3126,8 +3675,8 @@ PIKECLASS Buffer
   PIKEFUN int `[]=(int index, int ch)
   {
     struct Buffer_struct *str = THIS;
-    struct pike_string *s = str->str.s;
-    unsigned len = s->len;
+    struct pike_string *s;
+    size_t len = str->len;
 
     pop_n_elems(args);
 
@@ -3137,8 +3686,9 @@ PIKECLASS Buffer
       index_error("Buffer->`[]=", Pike_sp, args, NULL, Pike_sp,
         "Index %"PRINTPIKEINT"d is out of array range 0..%d,\n",
         index, len-1);
+    len = 0;
     if ((unsigned)ch >= 256)
-      switch(s->size_shift) {
+      switch(str->shift) {
         case 0:
           if ((unsigned)ch < 65536)
             len = 1;
@@ -3149,50 +3699,127 @@ PIKECLASS Buffer
             else
               break;
           }
-        string_build_mkspace(&str->str, 0, str->str.known_shift = len);
+          str->str.known_shift = len;
       }
-    low_set_index(str->str.s,index,ch);
+    s = buffer_mkspace(0, len);
+    low_set_index(s, index+str->offset, ch);
     push_int(ch);
   }
 
-  /*! @decl Buffer `+( string|Buffer what )
+  /*! @decl Buffer `+=( string|Buffer ... what )
    */
-  PIKEFUN Buffer `+( string|Buffer what )
+  PIKEFUN Buffer `+=( string|Buffer ... what )
     rawtype tFunc(tOr(tString, tObjIs_BUFFER), tObjIs_BUFFER);
   {
-    struct Buffer_struct *str = THIS, *str2;
-    struct object *res = fast_clone_object( Buffer_program );
-    str2 = OBJ2_BUFFER( res );
-    str2->initial = str->initial;
-    init_string_builder_copy (&str2->str, &str->str);
-    if( (Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT) )
-        res->flags |= OBJECT_CLEAR_ON_EXIT;
-    apply( res, "add", 1 );
-    RETURN res;
+    f_Buffer_add( args );
+    pop_stack();
+    ref_push_object(Pike_fp->current_object);
   }
 
-  /*! @decl Buffer `+=( string|Buffer what )
+  /*! @decl Buffer `+( string|Buffer ... what )
    */
-  PIKEFUN Buffer `+=( string|Buffer what )
+  PIKEFUN Buffer `+( string|Buffer ... what )
     rawtype tFunc(tOr(tString, tObjIs_BUFFER), tObjIs_BUFFER);
   {
-    f_Buffer_add( 1 );
-    REF_RETURN Pike_fp->current_object;
+    struct Buffer_struct *str = THIS, *str2;
+    struct pike_string *s = str->str.s;
+
+    if( Pike_fp->current_object->refs != 1
+     || s->refs != 1) {
+      struct Buffer_struct *str2;
+      unsigned shift = str->shift;
+      size_t len = str->len;
+      struct object *res = fast_clone_object( Buffer_program );
+      str2 = OBJ2_BUFFER( res );
+      str2->initial = str->initial;
+      str2->shift = shift;
+      init_string_builder_alloc (&str2->str, MAX(len, str2->initial), shift);
+      str2->str.s->len = str2->len = len;
+      memcpy(str2->buffer = str2->str.s->str, str->buffer, len<<shift);
+      if( (Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT) )
+        res->flags |= OBJECT_CLEAR_ON_EXIT;
+      apply( res, "add", args );
+      pop_stack();
+      ref_push_object(res);
+    } else
+      f_Buffer_cq__backtick_add_eq(args);
   }
 
-  static struct pike_string*getotherstr(struct svalue*other,unsigned stricttype)
+  /*! @decl Buffer ``+( string ... what )
+   */
+  PIKEFUN Buffer ``+( string ... what )
+    rawtype tFunc(tString, tObjIs_BUFFER);
   {
-    switch(TYPEOF(*other))
-    {
+    struct Buffer_struct *str = THIS, *str2;
+
+    if( Pike_fp->current_object->refs != 1
+     || str->str.s->refs != 1) {
+      struct Buffer_struct *str2;
+      struct object *res = fast_clone_object( Buffer_program );
+      str2 = OBJ2_BUFFER( res );
+      str2->initial = str->initial;
+      if( (Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT) )
+        res->flags |= OBJECT_CLEAR_ON_EXIT;
+      ref_push_object(Pike_fp->current_object);
+      apply( res, "add", args+1 );
+    } else {
+      char*dest;
+      struct svalue*pargs = Pike_sp-args;
+      int j,shift = 0;
+      size_t sum = 0, len;
+
+      pargs = Pike_sp;
+      j = args;
+      do {
+        struct pike_string *a = --pargs->u.string;
+        sum += a->len;
+        shift |= a->size_shift;
+      } while(--j);
+
+      shift |= str->shift;
+      shift = shift & ~(shift >> 1);
+      buffer_mkspace(sum - str->str.s->len + (len = str->len),  shift);
+      memmove(str->str.s->str+(sum<<shift), str->buffer, len<<shift);
+
+      str->buffer = dest = str->str.s->str;
+      str->offset = 0;
+      str->str.s->len = str->len = len + sum;
+
+      do {
+          struct pike_string *bs = pargs++->u.string;
+          size_t blen = bs->len;
+          generic_memcpy(MKPCHARP(dest, shift), MKPCHARP_STR( bs ), blen);
+          dest += blen<<shift;
+      } while(--args);
+
+      ref_push_object(Pike_fp->current_object);
+    }
+  }
+
+  static int buffer_cmp(struct svalue*other, unsigned stricttype)
+  {
+    struct Buffer_struct *str = THIS;
+    struct pike_string *s2;
+    PCHARP b;
+    ptrdiff_t blen;
+
+    switch(TYPEOF(*other)) {
       case PIKE_T_STRING:
-        return other->u.string;
+        b = MKPCHARP_STR(s2 = other->u.string);
+        blen = s2->len;
+        goto runcmp;
       case PIKE_T_OBJECT:
-        if (other->u.object->prog == Buffer_program)
-          return OBJ2_BUFFER(other->u.object)->str.s;
+        if (other->u.object->prog == Buffer_program) {
+          struct Buffer_struct *str2 = OBJ2_BUFFER(other->u.object);
+          b = MKPCHARP(str2->buffer, str2->shift);
+          blen = str2->len;
+runcmp:   return my_quick_pcharpcmp(
+             MKPCHARP(str->buffer, str->shift), str->len, b, blen);
+        }
     }
     if(stricttype)
       Pike_error("Unsupported types in comparison.\n");
-    return 0;
+    return 1;
   }
 
   /*! @decl int(0..1) `==( mixed other )
@@ -3202,8 +3829,7 @@ PIKECLASS Buffer
   PIKEFUN int(0..1) `==( mixed other )
     rawtype tFunc(tMixed, tInt01);
   {
-    struct pike_string *a;
-    RETURN ((a = getotherstr(other,0)) ? !my_quick_strcmp(THIS->str.s, a) : 0);
+    RETURN !buffer_cmp(other, 0);
   }
 
   /*! @decl int(0..1) `<( mixed other )
@@ -3213,7 +3839,7 @@ PIKECLASS Buffer
   PIKEFUN int(0..1) `<( mixed other )
     rawtype tFunc(tMixed, tInt01);
   {
-    RETURN my_quick_strcmp(THIS->str.s, getotherstr(other,1))<0;
+    RETURN buffer_cmp(other, 1)<0;
   }
 
   /*! @decl int(0..1) `>( mixed other )
@@ -3223,187 +3849,292 @@ PIKECLASS Buffer
   PIKEFUN int(0..1) `>( mixed other )
     rawtype tFunc(tMixed, tInt01);
   {
-    RETURN my_quick_strcmp(THIS->str.s, getotherstr(other,1))>0;
+    RETURN buffer_cmp(other, 1)>0;
+  }
+
+  static void buffer_putchar(int ch, unsigned mag) {
+    struct Buffer_struct *str = THIS;
+    void*dest;
+    unsigned shift;
+    pop_stack();
+    buffer_mkspace(1, mag);
+    shift = str->shift;
+    dest = str->buffer+(str->len++<<shift);
+    switch( shift ) {
+      case 0:  *(p_wchar0*)dest = ch; break;
+      case 1:  *(p_wchar1*)dest = ch; break;
+      default: *(p_wchar2*)dest = ch; break;
+    }
+    str->str.s->len = str->offset+str->len;
+    ref_push_object(Pike_fp->current_object);
   }
 
-  /*! @decl int addat(int(0..) pos, string|Buffer ... data)
+  /*! @decl Buffer putchar( int char )
+   *! @decl Buffer add_byte( int(0..255) i )
+   *! @decl Buffer add_int8( int(-128..127) i )
    *!
-   *!   Adds @[data] to the buffer, starting at position @[pos].
-   *!   It overwrites existing content at that offset, never truncates the
-   *!   buffer, possibly extends the buffer if the new content does not fit.
+   *! Appends a single character at the end of the string.
    *!
    *! @returns
-   *!   Returns the size of the buffer.
-   *!
-   *! @note
-   *!   If the starting position @[pos] extends beyond the end of the current
-   *!   buffer content, the gap will be filled with NUL-characters.
+   *!   Returns the buffer itself.
    *!
    *! @seealso
    *!   @[add()]
    */
-  PIKEFUN int addat(int(0..) pos, string|Buffer ... arg1 )
-    rawtype tFuncV(tIntPos, tOr(tString, tObjIs_BUFFER), tIntPos);
-  {
+  PIKEFUN Buffer putchar(int ch) {
+    buffer_putchar(ch, min_magnitude(ch));
+  }
+
+  PIKEFUN int add_byte( int(0..255) i ) {
+    buffer_putchar(i&255, 0);
+  }
+
+  PIKEFUN int add_int8( int(-128..127) i ) {
+    buffer_putchar(i&255, 0);
+  }
+
+  /*! @decl Buffer add_int( int i, int(0..) width )
+   *!
+   *! Appends a generic integer to the buffer as an (width*8)bit
+   *! network byteorder number.
+   *!
+   *! @returns
+   *!   Returns the buffer itself.
+   *!
+   *! @seealso
+   *!   @[add_int32()]
+   */
+  PIKEFUN Buffer add_int( int i, int width ) {
     struct Buffer_struct *str = THIS;
     struct pike_string *s;
-    ptrdiff_t sum = 0;
-    int j,shift = 0;
+    ptrdiff_t len;
 
-    if (pos < 0)
-      SIMPLE_BAD_ARG_ERROR("addat", 1, "int(0..)");
+    s = buffer_mkspace(width, 0);
+    len = str->offset+str->len;
+    if(width>=0)
+      for(str->len += width, s->len = len+=width; width--; i>>=8)
+        low_set_index(s, --len, i&0xff);
+    else
+      for(str->len -= width, s->len = len-width; width++; i>>=8)
+        low_set_index(s, len++, i&0xff);
 
-    for (j=1; j < args; j++) {
-      struct pike_string *a;
-      if (TYPEOF(Pike_sp[j-args]) == PIKE_T_STRING)
-        a = Pike_sp[j-args].u.string;
-      else if ((TYPEOF(Pike_sp[j-args]) != PIKE_T_OBJECT) ||
-         (Pike_sp[j-args].u.object->prog != Buffer_program))
-        SIMPLE_BAD_ARG_ERROR("addat", j+1, "string|String.Buffer");
-      else if(!(a = OBJ2_BUFFER(Pike_sp[j-args].u.object)->str.s))
-        continue;
-      sum += a->len;
-      shift |= a->size_shift;
-    }
+    pop_n_elems(args);
+    ref_push_object(Pike_fp->current_object);
+  }
 
-    s = str->str.s;
-    sum += pos;
-    shift |= str->str.known_shift;
-    shift = shift & ~(shift >> 1);
-    j = sum - s->len;
-    if (j>0) {
-      if (str->initial > sum)
-        j = str->initial-s->len;
-      string_build_mkspace(&str->str, j, shift);
-    }
-    else if (shift != str->str.known_shift)
-      string_build_mkspace(&str->str, 0, shift);
-    s = str->str.s;
-    /* We know it will be a string that really is this wide. */
-    str->str.known_shift = shift;
+  static void buffer_add_bignum( struct object *i, int width )
+  {
+    struct Buffer_struct *str = THIS;
+    struct pike_string *snum;
 
-    if (s->len < pos)				 // Clear the padding
-      memset(s->str + (s->len << s->size_shift),
-             0, (pos - s->len) << s->size_shift);
+    push_int(256);
+    apply( i, "digits", 1 );
 
-    for(j = 1; j<args; j++) {
-      struct pike_string *a;
-      if (TYPEOF(Pike_sp[j-args]) == PIKE_T_STRING)
-        a = Pike_sp[j-args].u.string;
-      else if(!(a = OBJ2_BUFFER(Pike_sp[j-args].u.object)->str.s))
-        continue;
-      pike_string_cpy(MKPCHARP_STR_OFF(s, pos), a);
-      pos += a->len;
-    }
+    snum = Pike_sp[-1].u.string;
 
-    if (s->len < pos) {
-      s->len = pos;
-      s->str[s->len << s->size_shift] = 0;	// Ensure NUL-termination
-    }
+    /* FIXME: little endian */
+
+    if( snum->len > width )
+      Pike_error("Number too large to store in %d bits\n", width*8);
+
+    push_int (str->len + width-snum->len);
+    stack_swap();
+    f_Buffer_appendat( 2 );
+  }
+
+  PIKEFUN Buffer add_int( object i, int width ) {
+    pop_stack(); /* width */
+    convert_stack_top_to_bignum();
+    buffer_add_bignum ( Pike_sp[-1].u.object, width);
+    stack_pop_keep_top(); /* i */
+  }
+
+  static void buffer_do_rewind_on_error( void*arg )
+  {
+    struct Buffer_struct *str = THIS;
+    str->str.s->len = str->offset+(str->len = arg-(void*)str->buffer);
+  }
+
+  static void buffer_begin_rewind_on_error( ONERROR *x )
+  {
+    struct Buffer_struct *str = THIS;
+    SET_ONERROR( (*x), buffer_do_rewind_on_error, str->buffer+str->len );
+  }
 
-    RETURN s->len;
+  static void buffer_end_rewind_on_error( ONERROR *x )
+  {
+    UNSET_ONERROR( (*x) );
   }
 
-  /*! @decl int add(string|Buffer ... data)
+  /*! @decl Buffer add_ints( array(int) integers, int(0..255) len )
    *!
-   *!   Adds @[data] to the buffer.
+   *! Add the integers in the specified array, @[len] bytes per int.
+   *! Equivalent to add_int( integers[*], len ) but faster.
+   */
+  PIKEFUN Buffer add_ints( array(int) a, int bpi )
+  {
+    struct Buffer_struct *str = THIS;
+    size_t i,l = a->size;
+    struct svalue *it = a->item;
+
+    if( bpi < 0 )
+      Pike_error("Invalid int width\n");  /* FIXME: little endian? */
+
+    if (bpi <= SIZEOF_INT_TYPE) {
+      struct pike_string *s = buffer_mkspace(bpi*l, 0);
+      size_t len = str->offset+str->len - bpi;
+      for (i=l; i--; it++) {
+        INT_TYPE si;
+        unsigned j;
+
+        if( TYPEOF(*it) == PIKE_T_INT )
+          si = it->u.integer;
+        else if( is_bignum_object_in_svalue( it ) ) {
+          INT64 i64;
+          if( int64_from_bignum( &i64, it->u.object ) )
+            si = i64;
+          else
+            Pike_error("Too big bignum\n");  /* FIXME: support these anyway? */
+        } else
+          Pike_error("Non integer in array\n");
+
+        for(len+=(j=bpi)<<1; j--; si>>=8)
+          low_set_index(s, --len, si&0xff);
+      }
+      str->len = (s->len = len + bpi) - str->offset;
+    } else {						/* bignums. */
+      ONERROR e;
+      buffer_begin_rewind_on_error( &e );
+      for (i=l; i--;) {
+        push_svalue( it++ );
+        push_int( bpi );
+        f_Buffer_add_int(2);
+        pop_stack();
+      }
+      buffer_end_rewind_on_error( &e );
+    }
+    pop_n_elems(args);
+    ref_push_object(Pike_fp->current_object);
+  }
+
+  /*! @decl Buffer add_int16( int(-32768..32767) i )
+   *! @decl Buffer add_short( int(0..65535) i )
+   *!
+   *! Appends an int16 at the end of the string.
    *!
    *! @returns
-   *!   Returns the size of the buffer.
+   *!   Returns the buffer itself.
    *!
    *! @seealso
-   *!   @[addat()]
+   *!   @[add()],@[add_byte()],@[add_int8()]
    */
-  PIKEFUN int add( string|Buffer ... arg1 )
-    rawtype tFuncV(tNone, tOr(tString, tObjIs_BUFFER), tIntPos);
-  {
-    struct Buffer_struct *str = THIS;
-    push_int(str->str.s->len);
-    stack_revroll(++args);
-    f_Buffer_addat(args);
+  PIKEFUN Buffer add_int16( int(-32768..32767) i ) {
+    push_int(2);
+    f_Buffer_add_int(2);
   }
 
-  /*! @decl void putchar(int ch)
-   *! Appends the character @[ch] at the end of the string.
-   */
-  PIKEFUN void putchar(int ch) {
-    struct Buffer_struct *str = THIS;
-    string_builder_putchar(&str->str, ch);
+  PIKEFUN Buffer add_short( int(0..65535) i ) {
+    f_Buffer_add_int16(1);
   }
 
-  /*! @decl int sprintf(strict_sprintf_format format, sprintf_args ... args)
-   *! Appends the output from @[sprintf] at the end of the string.
-   *! Returns the resulting size of the String.Buffer.
+  /*! @decl Buffer add_int32( int(-2147483648..2147483647) i )
+   *! @decl Buffer add_long( int(0..4294967295) i )
+   *!
+   *! Appends an int32 at the end of the string.
+   *!
+   *! @returns
+   *!   Returns the buffer itself.
+   *!
+   *! @seealso
+   *!   @[add()],@[add_byte()],@[add_int8()]
    */
-  PIKEFUN int sprintf(mixed ... arguments)
-    rawtype tFuncV(tAttr("strict_sprintf_format", tOr(tStr, tObj)),
-		   tAttr("sprintf_args", tMix), tStr);
+  PIKEFUN Buffer add_int32( int(-2147483648..2147483647) i ) {
+    push_int(4);
+    f_Buffer_add_int(2);
+  }
 
-  {
-    // FIXME: Reset length on exception?
-    struct Buffer_struct *str = THIS;
-    low_f_sprintf(args, 0, &str->str);
-    RETURN str->str.s->len;
+  PIKEFUN Buffer add_long( int(0..4294967295) i ) {
+    f_Buffer_add_int32(1);
   }
 
-  /*! @decl string get_copy()
+  /*! @decl Buffer add_hstring( string|Buffer|System.Memory data, int size_size )
    *!
-   *!   Get the data from the buffer. Significantly slower than @[get],
-   *!   but does not clear the buffer.
+   *! Adds length/data for @[data] to the buffer.
+   *!
+   *! This is identical to @[sprintf("%"+size_size+"H",data)] but
+   *! significantly faster.
+   *!
+   *! @[size_size] must be less than Int.NATIVE_MAX.
+   *!
+   *! @returns
+   *!   Returns the buffer itself.
    *!
    *! @seealso
-   *!   @[get()]
+   *!   @[add()],@[sprintf()]
    */
-  PIKEFUN string get_copy()
-  {
-    struct pike_string *str = THIS->str.s;
-    ptrdiff_t len;
-    if((len = str->len) > 0 )
-    {
-      char *d = (char *)str->str;
-      switch( str->size_shift )
+  PIKEFUN Buffer add_hstring( string|Buffer|object str, int size_size ) {
+    size_t len;
+
+    switch(TYPEOF(*str)) {
+      case PIKE_T_STRING:
+        len = str->u.string->len;
+        break;
+      case PIKE_T_OBJECT:
       {
-        case 0:
-          str=make_shared_binary_string0((p_wchar0 *)d,len);
+        struct sysmem *sm;
+        if (str->u.object->prog == Buffer_program) {
+          len = OBJ2_BUFFER(str->u.object)->len;
           break;
-        case 1:
-          str=make_shared_binary_string1((p_wchar1 *)d,len);
-          break;
-        default:
-          str=make_shared_binary_string2((p_wchar2 *)d,len);
+        }
+        if (sm = system_memory(str->u.object)) {
+          len = sm->size;
           break;
+        }
       }
-      if( Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT )
-          str->flags |= STRING_CLEAR_ON_EXIT;
-      RETURN str;
+      default:
+        Pike_error("Bad argument 1 to add_hstring(),"
+                   " expected string|Buffer|System.Memory.\n");
     }
-    push_empty_string();
+    /* We know this is safe for size_size>=4, since pike strings are at
+     * most 0x7ffffff bytes long.
+     */
+    if( size_size < 4 &&
+        len > (1<<(8*(size_size<0?-size_size:size_size)))-1 )
+      Pike_error("String too long\n");
+    pop_stack();
+    push_int(len);
+    push_int(size_size);
+    f_Buffer_add_int( 2 );
+    pop_stack();
+    f_Buffer_append( 1 );
   }
 
-  /*! @decl string get()
-   *!
-   *!   Get the data from the buffer.
-   *!
-   *! @note
-   *!   This will clear the data in the buffer
-   *!
-   *! @seealso
-   *!   @[get_copy()], @[clear()]
+  /*! @decl int sprintf(strict_sprintf_format format, sprintf_args ... args)
+   *! Appends the output from @[sprintf] at the end of the string.
+   *! Returns the buffer itself.
    */
-  PIKEFUN string get( )
+  PIKEFUN Buffer sprintf(mixed ... arguments)
+    rawtype tFuncV(tAttr("strict_sprintf_format", tOr(tStr, tObj)),
+		   tAttr("sprintf_args", tMix), tStr);
+
   {
     struct Buffer_struct *str = THIS;
-    struct pike_string *s = finish_string_builder( &str->str );
-    if( Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT )
-      s->flags |= STRING_CLEAR_ON_EXIT;
-    push_string(s);
-			// Pick a smaller size to minimise fragmentation
-			// addat() will expand if it notices reuse of
-			// the Buffer
-    init_string_builder_alloc(&str->str, sizeof(struct svalue), 0);
+    struct pike_string *s;
+    size_t oldlen;
+    ONERROR e;
+    buffer_mkspace(0, 0);
+    oldlen = str->str.s->len;
+    buffer_begin_rewind_on_error( &e );
+    low_f_sprintf(args, 0, &str->str);
+    buffer_end_rewind_on_error( &e );
+    pop_n_elems(args);
+    s = str->str.s;
+    str->buffer = s->str + (str->offset<<(str->shift = s->size_shift));
+    str->len += s->len-oldlen;
+    ref_push_object(Pike_fp->current_object);
   }
 
-  /*! @decl Buffer|void cut(int start, int|void end, void|int discard)
+  /*! @decl Buffer|void cut(int start, int|void end, void|int(0..1) discard)
    *!
    *!   Cut and delete the range of data from the current buffer.
    *!   Returns a new buffer with the cut data, unless discard is true.
@@ -3411,46 +4142,56 @@ PIKECLASS Buffer
    *! @seealso
    *!   @[get()], @[get_copy()], @[clear()]
    */
-  PIKEFUN Buffer|void cut(int index, int|void end_or_none,void|int discard)
+  PIKEFUN Buffer|void cut(int index, int|void end_or_none,
+     void|int(0..1) discard)
   {
     struct Buffer_struct *str = THIS, *str2;
     struct object *res;
-    struct pike_string *s = str->str.s;
-    unsigned len = s->len,shift = s->size_shift;
-    char *p = s->str;
-    INT_TYPE end,vdiscard;
+    unsigned shift;
+    size_t len;
+    INT_TYPE end, vdiscard;
 
+    len = str->len; shift = str->str.s->size_shift;
     end = args==1 ? len-1 : end_or_none->u.integer;
     vdiscard = args==3 ? discard->u.integer : 0;
     pop_n_elems(args);
 
     if (index < 0)
       index = 0;
-    if (end >= len)
-      end = len-1;
-
-    p = (char*)p+(index<<shift);
-    index = end-index+1;
-    end = len-end;
-    len = index<<shift;
-    if (!vdiscard)
-    {
-      struct pike_string *s2;
+    if (++end > len)
+      end = len;
 
+    str->len -= len = end-index;
+    if (index)
+      index += str->offset, end+=str->offset;
+    if (!vdiscard) {
       res = fast_clone_object( Buffer_program );
       str2 = OBJ2_BUFFER( res );
-      init_string_builder_alloc(&str2->str, str2->initial = index, shift);
-      memcpy((s2=str2->str.s)->str, p, len);
-      s2->str[len] = 0;				// Ensure NUL-termination
-      s2->len = index;
+      str2->shift = str->shift;
+      str2->len = len;
+      if (index) {
+        init_string_builder_alloc(&str2->str, str2->initial = len, 0);
+        string_builder_append(&str2->str,
+         MKPCHARP_OFF(str->buffer, str->shift, index), len);
+        str2->buffer = str2->str.s->str;
+      } else {
+        str2->offset = str->offset;
+        str2->initial = str->initial;
+        str2->buffer = str->buffer;
+        if (str->str.s) {
+          add_ref(str->str.s);
+          str2->str = str->str;
+        }
+      }
     }
-    memmove(p, p+len, end<<shift); // Copy NUL-termination
-    if( s->flags & STRING_CLEAR_ON_EXIT)
-      guaranteed_memset(p+len+(end<<shift) , 0, len );
-    s->len -= index;
+    if(index) {
+      struct pike_string *s = buffer_mkspace(0, 0);
+      memmove(s->str+(index<<shift), s->str+(end<<shift), (s->len-end)<<shift);
+      s->len -= len;
+    } else
+      str->offset += end, str->buffer += end<<shift;
 
-    if(!vdiscard)
-    {
+    if(!vdiscard) {
       if( (Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT) )
         res->flags |= OBJECT_CLEAR_ON_EXIT;
       push_object(res);
@@ -3459,16 +4200,452 @@ PIKECLASS Buffer
       push_undefined();
   }
 
+  /*! @decl Buffer read_buffer(int n)
+   *!
+   *!   Same as @[read()], but returns the result as a @[Buffer].
+   *!
+   *! @seealso
+   *!   @[read()], @[get()], @[trim()]
+   */
+  PIKEFUN Buffer|void read_buffer(int n)
+  {
+    struct Buffer_struct *str = THIS, *str2;
+    struct pike_string *s = str->str.s;
+    size_t len = str->len;
+    struct object *res;
+
+    while (n<0 || n>len)
+      if (!buffer_range_error( n )) {
+        pop_n_elems(args);
+        push_undefined();
+        return;
+      }
+    pop_n_elems(args);
+    res = fast_clone_object( Buffer_program );
+    str2 = OBJ2_BUFFER( res );
+    if (s)
+      add_ref(s);
+    str2->str = str->str;
+    str2->buffer = str->buffer;
+    str2->offset = str->offset;
+    str2->shift = str->shift;
+    str2->len = n;
+    if( (Pike_fp->current_object->flags & OBJECT_CLEAR_ON_EXIT) )
+      res->flags |= OBJECT_CLEAR_ON_EXIT;
+    push_object(res);
+  }
+
+#define LONGEST_HIGH_BYTE (~(MAX_ULONGEST>>8))
+
+  static LONGEST buffer_read_number(size_t n)
+  {
+    struct Buffer_struct *str = THIS;
+    unsigned shift = str->shift;
+    unsigned char*buf = str->buffer;
+    LONGEST res = 0;
+
+    if (n>=0)
+      while (n--) {
+        if (LONGEST_HIGH_BYTE&res)
+          goto overflow;
+        res<<=8; res|=*buf; buf+=1<<shift;
+      }
+    else
+      for (buf+=n<<shift; n++; ) {
+        if (LONGEST_HIGH_BYTE&res)
+          goto overflow;
+        res<<=8; buf-=1<<shift; res|=*buf;
+      }
+    if (res<0)
+overflow:
+      Pike_error("Integer (%dbit) overflow.\n", SIZEOF_LONGEST*8);
+    return res;
+  }
+
+  static int buffer_rc_number(size_t n)
+  {
+    struct Buffer_struct *str = THIS;
+
+    pop_stack();
+    for(;;) {
+      LONGEST slen;
+      ptrdiff_t len = str->len;
+
+      if ((len -= n)<0)
+        goto rangeerror;
+      slen = buffer_read_number(n);
+      if ((len -= slen)<0) {
+rangeerror:
+        if( buffer_range_error( -len ) )
+          continue;
+        push_undefined();
+        return 0;
+      }
+      str->len -= n;
+      str->offset += n;
+      str->buffer += n<<str->shift;
+      push_int(slen);
+      return 1;
+    }
+  }
+
+  static int buffer_presscanf(const struct pike_string*format)
+  {
+    struct Buffer_struct *str = THIS;
+    ptrdiff_t num_used;
+
+    goto jin;
+    do {
+      if (!buffer_range_error(0))
+        return 0;
+jin:  low_sscanf_pcharp( MKPCHARP(str->buffer, str->shift), str->len,
+         MKPCHARP_STR(format), format->len, &num_used, 0);
+    } while (!num_used);
+
+    str->len -= num_used;
+    str->offset += num_used;
+    str->buffer += num_used<<str->shift;
+    return 1;
+  }
+
+  /*! @decl int read_int( int n )
+   *!
+   *! Read a network (if n is positive) or little endian (if n is
+   *! negative) byte order unsigned number of size n*8 bits, then
+   *! return it.
+   *!
+   *! @returns
+   *!   Will return -1 if there is not enough buffer space available
+   *!   unless error mode is set to throw errors.
+   */
+  PIKEFUN int(0..)|int(-1..-1) read_int( int n )
+  {
+    struct Buffer_struct *str = THIS;
+
+    while (str->len < n)
+      if (!buffer_range_error( (int)str->len-n )) {
+        Pike_sp[-1].u.integer = -1;
+        return;
+      }
+
+    if( n < SIZEOF_INT_TYPE ) {            /* will for sure fit. */
+      pop_stack();
+      push_int(buffer_read_number(n));
+      str->len -= n;
+      str->offset += n;
+    } else {
+      f_Buffer_read( 1 );
+      push_int(256);
+      push_object(clone_object( get_auto_bignum_program(), 2 ));
+      reduce_stack_top_bignum();
+    }
+  }
+
+  /*! @decl string read_hstring( int(0..) n )
+   *!
+   *! Identical in functionality to @[read](@[read_int](@[n])) but
+   *! faster.
+   *!
+   *! Read a network byte order number of size n*8 bits, then return the
+   *! indicated number of bytes as a string.
+   *! @returns
+   *! If there is not enough data available return 0.
+   *!
+   *! Note that a pike string can not be longer than 0x7fffffff bytes (~2Gb).
+   */
+  PIKEFUN string|void read_hstring( int n )
+  {
+    if(buffer_rc_number(n))
+      f_Buffer_read( 1 );
+  }
+
+  /*! @decl Buffer read_hbuffer( int n )
+   *!
+   *! Same as @[read_hstring], but returns the result as a Buffer.
+   *!
+   */
+  PIKEFUN Buffer|void read_hbuffer( int n )
+  {
+    if(buffer_rc_number(n))
+      f_Buffer_read_buffer( 1 );
+  }
+
+  /*! @decl array|void sscanf(string format)
+   *!
+   *! Reads data from the beginning of the buffer to match the
+   *! specifed format, then return an array with the matches.
+   *!
+   *! The non-matching data will be left in the buffer.
+   *!
+   *! See @[array_sscanf] for more information.
+   */
+  PIKEFUN array|void sscanf( string format )
+  {
+    struct svalue *start = Pike_sp;
+    unsigned matched, results;
+
+    matched = buffer_presscanf(format);
+    results = Pike_sp - start;
+
+    if(matched)
+      f_aggregate(results);
+    else {
+      pop_n_elems(results);
+      push_undefined();
+    }
+  }
+
+  /*! @decl mixed|void match(string format)
+   *!
+   *! Reads data from the beginning of the buffer to match the
+   *! specifed format, then return the match.
+   *!
+   *! The non-matching data will be left in the buffer.
+   *!
+   *! This function is very similar to @[sscanf], but the
+   *! result is the sum of the matches. Most useful to match
+   *! a single value.
+   *!
+   *! @example
+   *! @code
+   *!  // get the next whitespace separated word from the buffer.
+   *!  buffer->match("%*[ \t\r\n]%*[^ \t\r\n]");
+   *! @endcode
+   */
+  PIKEFUN string|int|float|array|void match( string format )
+  {
+    struct svalue *start = Pike_sp;
+    unsigned matched, results;
+
+    matched = buffer_presscanf(format);
+    results = Pike_sp - start;
+
+    if(matched)
+      f_add(results);
+    else {
+      pop_n_elems(results);
+      push_undefined();
+    }
+  }
+
+  /*! @decl int(0..)|int(-1..-1) consume(int(0..) n)
+   *!
+   *!   Discard the next @[n] bytes from the beginning of the buffer.
+   *!
+   *! @returns
+   *!   Returns the remaining size of the buffer, or -1 if you attempted
+   *!   to consume more than was available.
+   *!
+   *! @seealso
+   *!   @[cut()], @[unread()]
+   */
+  PIKEFUN int(-1..) consume( int(0..) n )
+  {
+    struct Buffer_struct *str = THIS;
+    int left;
+
+    while (n<0 || (left = str->len-n) < 0)
+      if (!buffer_range_error( -left )) {
+        left = -1;
+        goto ret;
+      }
+
+    str->len -= n;
+    str->offset += n;
+    str->buffer += n<<str->shift;
+ret:
+    Pike_sp[-1].u.integer = left;
+  }
+
+  /*! @decl int(0..)|int(-1..-1) unread(int(0..)|string|Buffer n)
+   *!
+   *!   Rewind the buffer @[n] bytes.  If passed a string of Buffer
+   *!   argument, it will push back that string at the front of the buffer.
+   *!
+   *! @returns
+   *!   Returns how many characters of buffer is available to rewind,
+   *!   or -1 if you attempted to rewind more than is available.
+   *!
+   *! @seealso
+   *!   @[consume()]
+   */
+  PIKEFUN int(-1..) unread( int(0..)|string|Buffer n )
+  {
+    struct Buffer_struct *str = THIS;
+    int ret = Pike_sp[-1].u.integer;
+    struct pike_string *s2;
+    PCHARP b;
+    ptrdiff_t blen;
+
+    switch (TYPEOF(Pike_sp[-1])) {
+      case PIKE_T_INT:
+        ret = Pike_sp[-1].u.integer;
+        while (ret<0 || ret>str->offset)
+          if (!buffer_range_error( ret-str->offset )) {
+            ret = -1;
+            goto rangx1;
+          }
+        str->buffer -= ret<<str->shift;
+        str->len += ret;
+        ret = str->offset -= ret;
+rangx1: break;
+      case PIKE_T_STRING:
+        s2 = Pike_sp[-1].u.string;
+        b = MKPCHARP_STR(s2);
+        blen = s2->len;
+        goto pastein;
+      case PIKE_T_OBJECT:
+        if (Pike_sp[-1].u.object->prog == Buffer_program)
+        { struct Buffer_struct *str2 = OBJ2_BUFFER(Pike_sp[-1].u.object);
+          ptrdiff_t dif;
+
+          b = MKPCHARP(str2->buffer, str2->shift);
+          blen = str2->len;
+pastein:  if ((dif = (ptrdiff_t)str->offset-blen) < 0) {
+            struct pike_string *s = buffer_mkspace(-dif, b.shift);
+            unsigned shift = s->size_shift;
+            memmove(s->str+(blen<<shift), str->buffer, str->len<<shift);
+            str->buffer = s->str;
+            s->len = str->len += blen;
+            ret = str->offset = 0;
+          }
+          else {
+            buffer_mkspace(0, 0);
+            str->buffer -= blen<<str->shift;
+            str->len += blen;
+            ret = str->offset = dif;
+          }
+          generic_memcpy(MKPCHARP(str->buffer, str->shift), b, blen);
+          break;
+        }
+      default:
+        SIMPLE_BAD_ARG_ERROR("unread", 1, "int(0..)|string|String.Buffer");
+    }
+    pop_stack();
+    push_int(ret);
+  }
+
+  static int buffer_is_whitespace(struct Buffer_struct *str, size_t pos)
+  {
+    switch( generic_extract(str->buffer, str->shift, pos) ) {
+      SPACECASE8
+        return 1;
+    }
+    return 0;
+  }
+
+  /*! @decl mixed read_json(int|void require_whitespace_separator)
+   *!
+   *! Read a single JSON expression from the buffer and return it.
+   *!
+   *! If @[require_whitespace_separator] is true there must be a whitespace
+   *! after each json value (as an example, newline or space).
+   *!
+   *! The JSON is assumed to be utf-8 encoded.
+   *!
+   *! @returns
+   *! UNDEFINED if no data is available to read.
+   *! The read value otherwise.
+   *!
+   *! @note
+   *! Unless strwhitespaces are required this function only really work correctly
+   *! with objects, arrays and strings.
+   *!
+   *! There is really no  way to see where one value starts and the other ends
+   *! for most other cases
+   */
+  PIKEFUN mixed read_json(int|void require_whitespace)
+  {
+    struct Buffer_struct *str = THIS;
+    unsigned whites = 0;
+    static ptrdiff_t(*parse_json_pcharp)(PCHARP,size_t,int,char**);
+    if( require_whitespace ) {
+      whites = require_whitespace->u.integer;
+      pop_stack();
+    }
+
+    if( !parse_json_pcharp )
+      parse_json_pcharp
+       = PIKE_MODULE_IMPORT(Standards.JSON, parse_json_pcharp );
+    for(;;) {
+      char *err = NULL;
+      ptrdiff_t stop = parse_json_pcharp( MKPCHARP(str->buffer,str->shift),
+                                str->len, 1|8, &err ); /* json_utf8 */
+      if( stop < 0 )
+        if( -stop == str->len || (err && !strncmp(err,"Unterminated",12))) {
+          if (buffer_range_error( 0 ))
+            continue;
+          push_undefined();
+        } else				 /* FIXME: Use real json error? */
+          if( err )
+            Pike_error("Syntax error in json at offset %d: %s\n", -stop, err );
+          else
+            Pike_error("Syntax error in json at offset %d\n", -stop );
+      else if( whites &&
+          (stop == str->len || !buffer_is_whitespace(str,stop))
+          && !buffer_is_whitespace(str,stop-1))
+        if( stop == str->len ) {
+          if (buffer_range_error( 0 ))
+            continue;
+          pop_stack();
+          push_undefined();
+        } else
+          Pike_error("Missing whitespace between json values at offset %d\n",
+           stop );
+      else {
+        if( whites )
+          while( buffer_is_whitespace( str, stop ) )
+            stop++;
+        push_int( stop );
+        f_Buffer_consume( 1 );
+        pop_stack();
+      }
+      break;
+    }
+  }
+
+  /*! @decl void trim()
+   *!
+   *!   Compact buffer, free unused memory.
+   *!   When called on a subbuffer, it clears the subbuffer and releases
+   *!   the parent buffer to be modified again.
+   *!
+   *! @note
+   *!   Calling this function excessively might slow things down,
+   *!   since the data often has to be copied.
+   *!
+   *! @seealso
+   *!   @[clear()]]
+   */
+  PIKEFUN void trim()
+  {
+    struct Buffer_struct *str = THIS;
+    struct pike_string *s = str->str.s;
+    unsigned offset = str->offset, shift = s->size_shift;
+
+    if (str->readonly || !s || s->refs!=1)
+      buffer_mkspace(0, 0);
+    else {
+      if (str->offset)
+        memmove(s->str, str->buffer, str->len<<str->shift);
+      if (s = realloc_unlinked_string(s, str->len))
+        str->str.s = s, str->str.malloced = str->len;
+      str->buffer = s->str;
+      str->offset = 0;
+    }
+  }
+
   /*! @decl void clear()
    *!
    *!   Empty the buffer, and discard the old content.
    *!
    *! @seealso
-   *!   @[get()], @[cut()]
+   *!   @[get()], @[cut()], @[trim()]
    */
   PIKEFUN void clear()
   {
     struct Buffer_struct *str = THIS;
+    str->len = str->offset = 0;
     reset_string_builder(&str->str);
   }
 
@@ -3478,8 +4655,7 @@ PIKECLASS Buffer
    */
   PIKEFUN int _sizeof()
   {
-    struct Buffer_struct *str = THIS;
-    RETURN str->str.s->len;
+    RETURN THIS->len;
   }
 
   INIT
@@ -3492,9 +4668,14 @@ PIKECLASS Buffer
     gc_trivial;
     {
       struct Buffer_struct *str = THIS;
-      if( Pike_fp->flags & OBJECT_CLEAR_ON_EXIT )
-          guaranteed_memset( str->str.s->str, 0, str->str.s->len );
-      free_string_builder( &str->str );
+      struct pike_string *s;
+      if (s = str->str.s) {
+        if( Pike_fp->flags & OBJECT_CLEAR_ON_EXIT )
+          guaranteed_memset( s->str, 0, str->str.malloced );
+        free_string( s );
+      }
+      if( str->source )
+        free_object( str->source );
     }
 
   GC_RECURSE
@@ -3502,6 +4683,142 @@ PIKECLASS Buffer
     if (mc_count_bytes (Pike_fp->current_object))
       mc_counted_bytes += THIS->str.malloced;
   }
+
+#define BUFFER_BLOCK_SIZE	4096
+
+  /*! @decl int input_from( Stdio.Stream f, void|int n )
+   *!
+   *!   Read data from @[f] into this buffer.
+   */
+  PIKEFUN int(0..) input_from( object f, void|int _n )
+  {
+    struct Buffer_struct *str = THIS;
+    size_t bread = 0, n = (size_t)-1;
+    struct my_file *fd;
+
+    if( _n )
+      n = _n->u.integer;
+
+    buffer_mkspace(0, 0);
+    if( (fd = get_storage( f, file_program )) ) {
+      struct pike_string *s;
+      void *tbuf = 0;
+
+      if (str->shift) {
+        tbuf = malloc(MIN(n,BUFFER_BLOCK_SIZE));
+        if (!tbuf)
+          Pike_error("Out of memory allocating %d buffer space.\n",
+           BUFFER_BLOCK_SIZE);
+      }
+      do {
+        int res;
+        void *ptr;
+        size_t getnow = n-bread;
+        if (getnow < BUFFER_BLOCK_SIZE)
+          getnow = BUFFER_BLOCK_SIZE;
+        s = buffer_mkspace(getnow, 0);
+        ptr = tbuf? tbuf: s->str+s->len;  /* shift is 0 when no tbuf */
+
+        buffer_lock();
+        {
+            THREADS_ALLOW();
+            res = fd_read( fd->box.fd, ptr, getnow );
+            THREADS_DISALLOW();
+        }
+        buffer_release();
+
+        if( res == -1 && errno == EINTR )
+          continue;
+
+        if( res <= 0 )
+          break;
+
+	if (tbuf)
+          generic_memcpy(MKPCHARP_STR_OFF(s, s->len), MKPCHARP(tbuf,0), res);
+        s->len += res;
+        bread += res;
+        if( res != getnow )
+            break;
+      } while (n>bread);
+
+      if (tbuf)
+        free(tbuf);
+      str->buffer = s->str + (str->offset<<str->shift);
+      str->len += bread;
+    } else			/* some other object. Just call read */
+      for(;;) {
+        push_int( BUFFER_BLOCK_SIZE );
+        safe_apply( f, "read", 1 );
+        if( TYPEOF(Pike_sp[-1]) != PIKE_T_STRING 
+         || Pike_sp[-1].u.string->len == 0 )
+          break;
+        bread += Pike_sp[-1].u.string->len;
+        f_Buffer_add( 1 );
+        pop_stack();
+      }
+    RETURN bread;
+  }
+
+  /*! @decl int output_to( Stdio.Stream f, void|int n )
+   *!
+   *! Write data from the buffer to the indicated file.
+   *!
+   *! Will return the number of bytes that were successfully written.
+   */
+  PIKEFUN int(0..) output_to( object f, void|int _n )
+  {
+    struct Buffer_struct *str = THIS;
+    size_t sz = str->len;
+    size_t written = 0;
+    struct my_file *fd;
+    INT_TYPE *wr = &Pike_sp->u.integer;
+
+    if( _n )
+      sz = MIN(_n->u.integer, sz);
+
+    if( !str->shift && (fd = get_storage( f, file_program )) )
+    {
+      buffer_lock();
+
+      THREADS_ALLOW();
+      while( sz > written ) {
+        int res = fd_write( fd->box.fd, str->buffer, sz-written );
+        if( res == -1 && errno == EINTR )
+          continue;
+        if( res <= 0 )
+          break;
+        str->buffer += res;
+        written += res;
+      }
+      THREADS_DISALLOW();
+      str->len -= written;
+      str->offset += written;
+      buffer_release();
+    } else {
+      /* some other object. Just call write */
+      while( sz > written ) {
+        size_t rd = MIN(sz-written,BUFFER_BLOCK_SIZE);
+	push_int(rd);
+	f_Buffer_read( 1 );
+        if( TYPEOF(Pike_sp[-1]) != PIKE_T_STRING 
+         || Pike_sp[-1].u.string->len == 0 )
+          break;
+        /* when this throws we need to rewind the buffer correctly. */
+        safe_apply(f, "write", 1);
+        if( *wr <= 0 )
+          goto rewind;
+        written += *wr;
+        if( 0 < (rd-=*wr) ) {
+rewind:   str->buffer -= rd<<str->shift;
+          str->offset -= rd;
+          str->len += rd;
+          break;
+        }
+        pop_stack();
+      }
+    }
+    RETURN written;
+  }
 }
 
 /*! @endclass
@@ -6179,4 +7496,5 @@ void exit_builtin(void)
 #ifndef USE_SETENV
   if (env_allocs) free_mapping (env_allocs);
 #endif
+  if( shm_program ) free_program( shm_program );
 }
diff --git a/src/stralloc.h b/src/stralloc.h
index c98a89f5b24be448d98f23e933df49d0b11bfd12..4c8d009707c3f18d9461a49b81afa4ccb82c237c 100644
--- a/src/stralloc.h
+++ b/src/stralloc.h
@@ -335,6 +335,17 @@ ptrdiff_t generic_quick_binary_strcmp(const char *a,
                                       ptrdiff_t alen, int asize,
                                       const char *b,
                                       ptrdiff_t blen, int bsize) ATTRIBUTE((pure));
+
+/* Does not take locale into account */
+
+static INLINE ptrdiff_t __attribute__((unused))
+                      my_quick_pcharpcmp(const PCHARP a, ptrdiff_t alen,
+                                         const PCHARP b, ptrdiff_t blen)
+{
+  return generic_quick_binary_strcmp(a.ptr, alen, a.shift,
+                                     b.ptr, blen, b.shift);
+}
+
 ptrdiff_t generic_find_binary_prefix(const char *a,
                                      ptrdiff_t alen, int asize,
                                      const char *b,