@@ -475,6 +475,354 @@ VAL idris_strRev(VM* vm, VAL str) {
475475 return cl;
476476}
477477
478+VAL MKBUFFERc(VM* vm, Buffer* buf) {
479+ Closure* cl = allocate(vm, sizeof(Closure) + sizeof *buf + buf->cap, 1);
480+ SETTY(cl, BUFFER);
481+ cl->info.buf = (Buffer*)((void*)cl + sizeof(Closure));
482+ memmove(cl->info.buf, buf, sizeof *buf + buf->fill);
483+ return cl;
484+}
485+
486+static VAL make_buf_offset(VM* vm, BufOffset* off, int outerlock) {
487+ Closure* cl = allocate(vm, sizeof(Closure) + sizeof *off, outerlock);
488+ SETTY(cl, BUFOFFSET);
489+ cl->info.buf_offset = (BufOffset*)((void*)cl + sizeof(Closure));
490+ memmove(cl->info.buf_offset, off, sizeof *off);
491+ return cl;
492+}
493+
494+static VAL MKBUFOFFSET(VM* vm, BufOffset* off) {
495+ return make_buf_offset(vm, off, 0);
496+}
497+
498+VAL MKBUFOFFSETc(VM* vm, BufOffset* off) {
499+ return make_buf_offset(vm, off, 1);
500+}
501+
502+static VAL internal_allocate(VM *vm, size_t hint) {
503+ size_t size = hint + sizeof(Closure) + sizeof(Buffer);
504+
505+ // Round up to a power of 2
506+ --size;
507+ size_t i;
508+ for (i = 0; i <= sizeof size; ++i)
509+ size |= size >> (1 << i);
510+ ++size;
511+
512+ Closure* cl = allocate(vm, size, 0);
513+ SETTY(cl, BUFFER);
514+ cl->info.buf = (Buffer*)((void*)cl + sizeof(Closure));
515+ cl->info.buf->cap = size - (sizeof(Closure) + sizeof(Buffer));
516+ return cl;
517+}
518+
519+// Following functions cast uint64_t to size_t, which may narrow!
520+VAL idris_allocate(VM* vm, VAL hint) {
521+ Closure* cl = internal_allocate(vm, hint->info.bits64);
522+ cl->info.buf->fill = 0;
523+ return cl;
524+}
525+
526+VAL idris_copy(VM* vm, VAL len, VAL src) {
527+ size_t length = len->info.bits64;
528+ Closure* cl = internal_allocate(vm, length);
529+ memmove(cl->info.buf->store, (GETTY(src) == BUFOFFSET ?
530+ src->info.buf_offset->buf->info.buf->store + src->info.buf_offset->offset :
531+ src->info.buf->store
532+ ), length);
533+ cl->info.buf->fill = length;
534+ return cl;
535+}
536+
537+static void internal_memset(void *dest, const void *src, size_t size, size_t num) {
538+ while (num--) {
539+ memmove(dest, src, size);
540+ dest += size;
541+ }
542+}
543+
544+static void internal_prepare_append(VM* vm, VAL buf, size_t bufLen, size_t appLen, void** storePtr, Closure** valPtr) {
545+ size_t totalLen = bufLen + appLen;
546+ // Might be able to get rid of some duplication here...
547+ if (GETTY(buf) == BUFOFFSET) {
548+ if (buf->info.buf_offset->offset + bufLen !=
549+ buf->info.buf_offset->buf->info.buf->fill ||
550+ buf->info.buf_offset->offset + totalLen >
551+ buf->info.buf_offset->buf->info.buf->cap) {
552+ // We're not at the fill or are over cap, so need a new buffer
553+ *valPtr = internal_allocate(vm, totalLen);
554+ memmove((*valPtr)->info.buf->store,
555+ buf->info.buf_offset->buf->info.buf->store + buf->info.buf_offset->offset,
556+ bufLen);
557+ (*valPtr)->info.buf->fill = totalLen;
558+ *storePtr = (*valPtr)->info.buf->store + bufLen;
559+ } else {
560+ // Hooray, can just bump the fill
561+ *valPtr = MKBUFOFFSET(vm, buf->info.buf_offset);
562+ buf->info.buf_offset->buf->info.buf->fill += appLen;
563+ *storePtr = buf->info.buf_offset->buf->info.buf->store + buf->info.buf_offset->offset + bufLen;
564+ }
565+ } else {
566+ Buffer* newBuf;
567+ if (bufLen != buf->info.buf->fill ||
568+ totalLen > buf->info.buf->cap) {
569+ // We're not at the fill or are over cap, so need a new buffer
570+ *valPtr = internal_allocate(vm, totalLen);
571+ newBuf = (*valPtr)->info.buf;
572+ memmove(newBuf->store,
573+ buf->info.buf->store,
574+ bufLen);
575+ newBuf->fill = totalLen;
576+ } else {
577+ // Hooray, can just bump the fill
578+ BufOffset off = { buf, 0 };
579+ *valPtr = MKBUFOFFSET(vm, &off);
580+ newBuf = buf->info.buf;
581+ newBuf->fill += appLen;
582+ }
583+ *storePtr = newBuf->store + bufLen;
584+ }
585+}
586+
587+VAL idris_appendBuffer(VM* vm, VAL fst, VAL fstLen, VAL cnt, VAL sndLen, VAL snd) {
588+ size_t firstLength = fstLen->info.bits64;
589+ size_t secondLength = sndLen->info.bits64;
590+ size_t count = cnt->info.bits64;
591+ void* store;
592+ Closure *cl;
593+ internal_prepare_append(vm, fst, firstLength, count * secondLength, &store, &cl);
594+ internal_memset(store, GETTY(snd) == BUFOFFSET ?
595+ snd->info.buf_offset->buf->info.buf->store + snd->info.buf_offset->offset :
596+ snd->info.buf->store
597+ , secondLength, count);
598+ return cl;
599+}
600+
601+VAL idris_peekBuffer(VM* vm, VAL src, VAL off) {
602+ size_t offset = off->info.bits64;
603+ BufOffset b;
604+ if (GETTY(src) == BUFOFFSET) {
605+ b.buf = src->info.buf_offset->buf;
606+ b.offset = src->info.buf_offset->offset + offset;
607+ } else {
608+ b.buf = src;
609+ b.offset = offset;
610+ }
611+ return MKBUFOFFSET(vm, &b);
612+}
613+
614+// Special cased because we can use memset
615+VAL idris_appendB8Native(VM* vm, VAL buf, VAL len, VAL cnt, VAL val) {
616+ size_t bufLen = len->info.bits64;
617+ size_t count = cnt->info.bits64;
618+ void* store;
619+ Closure* cl;
620+ internal_prepare_append(vm, buf, bufLen, count, &store, &cl);
621+ memset(store, val->info.bits8, count);
622+ return cl;
623+}
624+
625+VAL idris_appendB8LE(VM* vm, VAL buf, VAL len, VAL cnt, VAL val) {
626+ return idris_appendB8Native(vm, buf, len, cnt, val);
627+}
628+
629+VAL idris_appendB8BE(VM* vm, VAL buf, VAL len, VAL cnt, VAL val) {
630+ return idris_appendB8Native(vm, buf, len, cnt, val);
631+}
632+
633+static VAL internal_append_bits(VM* vm, VAL buf, VAL bufLen, VAL cnt, const void* val, size_t val_len) {
634+ size_t len = bufLen->info.bits64;
635+ size_t count = cnt->info.bits64;
636+ void* store;
637+ Closure* cl;
638+ internal_prepare_append(vm, buf, len, count * val_len, &store, &cl);
639+ internal_memset(store, val, val_len, count);
640+ return cl;
641+}
642+
643+VAL idris_appendB16Native(VM* vm, VAL buf, VAL len, VAL cnt, VAL val) {
644+ return internal_append_bits(vm, buf, len, cnt, &val->info.bits16, sizeof val->info.bits16);
645+}
646+
647+VAL idris_appendB16LE(VM* vm, VAL buf, VAL len, VAL cnt, VAL val) {
648+ // On gcc 4.8 -O3 compiling for x86_64, using leVal like this is
649+ // optimized away. Presumably the same holds for other sizes and
650+ // conversly on BE systems
651+ unsigned char leVal[sizeof val] = { val->info.bits16
652+ , (val->info.bits16 >> 8)
653+ };
654+ return internal_append_bits(vm, buf, len, cnt, leVal, sizeof leVal);
655+}
656+
657+VAL idris_appendB16BE(VM* vm, VAL buf, VAL len, VAL cnt, VAL val) {
658+ unsigned char beVal[sizeof val] = { (val->info.bits16 >> 8)
659+ , val->info.bits16
660+ };
661+ return internal_append_bits(vm, buf, len, cnt, beVal, sizeof beVal);
662+}
663+
664+VAL idris_appendB32Native(VM* vm, VAL buf, VAL len, VAL cnt, VAL val) {
665+ return internal_append_bits(vm, buf, len, cnt, &val->info.bits32, sizeof val->info.bits32);
666+}
667+
668+VAL idris_appendB32LE(VM* vm, VAL buf, VAL len, VAL cnt, VAL val) {
669+ unsigned char leVal[sizeof val] = { val->info.bits32
670+ , (val->info.bits32 >> 8)
671+ , (val->info.bits32 >> 16)
672+ , (val->info.bits32 >> 24)
673+ };
674+ return internal_append_bits(vm, buf, len, cnt, leVal, sizeof leVal);
675+}
676+
677+VAL idris_appendB32BE(VM* vm, VAL buf, VAL len, VAL cnt, VAL val) {
678+ unsigned char beVal[sizeof val] = { (val->info.bits32 >> 24)
679+ , (val->info.bits32 >> 16)
680+ , (val->info.bits32 >> 8)
681+ , val->info.bits32
682+ };
683+ return internal_append_bits(vm, buf, len, cnt, beVal, sizeof beVal);
684+}
685+
686+VAL idris_appendB64Native(VM* vm, VAL buf, VAL len, VAL cnt, VAL val) {
687+ return internal_append_bits(vm, buf, len, cnt, &val->info.bits64, sizeof val->info.bits64);
688+}
689+
690+VAL idris_appendB64LE(VM* vm, VAL buf, VAL len, VAL cnt, VAL val) {
691+ unsigned char leVal[sizeof val] = { val->info.bits64
692+ , (val->info.bits64 >> 8)
693+ , (val->info.bits64 >> 16)
694+ , (val->info.bits64 >> 24)
695+ , (val->info.bits64 >> 32)
696+ , (val->info.bits64 >> 40)
697+ , (val->info.bits64 >> 48)
698+ , (val->info.bits64 >> 56)
699+ };
700+ return internal_append_bits(vm, buf, len, cnt, leVal, sizeof leVal);
701+}
702+
703+VAL idris_appendB64BE(VM* vm, VAL buf, VAL len, VAL cnt, VAL val) {
704+ unsigned char beVal[sizeof val] = { (val->info.bits64 >> 56)
705+ , (val->info.bits64 >> 48)
706+ , (val->info.bits64 >> 40)
707+ , (val->info.bits64 >> 32)
708+ , (val->info.bits64 >> 24)
709+ , (val->info.bits64 >> 16)
710+ , (val->info.bits64 >> 8)
711+ , val->info.bits64
712+ };
713+ return internal_append_bits(vm, buf, len, cnt, beVal, sizeof beVal);
714+}
715+
716+VAL idris_peekB8Native(VM* vm, VAL buf, VAL off) {
717+ size_t offset = off->info.bits64;
718+ uint8_t* val = GETTY(buf) == BUFOFFSET ?
719+ buf->info.buf_offset->buf->info.buf->store + buf->info.buf_offset->offset + offset :
720+ buf->info.buf->store + offset;
721+ return MKB8(vm, *val);
722+}
723+
724+VAL idris_peekB8LE(VM* vm, VAL buf, VAL off) {
725+ return idris_peekB8Native(vm, buf, off);
726+}
727+
728+VAL idris_peekB8BE(VM* vm, VAL buf, VAL off) {
729+ return idris_peekB8Native(vm, buf, off);
730+}
731+
732+VAL idris_peekB16Native(VM* vm, VAL buf, VAL off) {
733+ size_t offset = off->info.bits64;
734+ uint16_t* val = (uint16_t *) ((GETTY(buf) == BUFOFFSET) ?
735+ buf->info.buf_offset->buf->info.buf->store + buf->info.buf_offset->offset + offset :
736+ buf->info.buf->store + offset);
737+ return MKB16(vm, *val);
738+}
739+
740+VAL idris_peekB16LE(VM* vm, VAL buf, VAL off) {
741+ size_t offset = off->info.bits64;
742+ unsigned char* val = (GETTY(buf) == BUFOFFSET) ?
743+ buf->info.buf_offset->buf->info.buf->store + buf->info.buf_offset->offset + offset :
744+ buf->info.buf->store + offset;
745+ return MKB16(vm, ((uint16_t) val[0]) +
746+ (((uint16_t) val[1]) << 8));
747+}
748+
749+VAL idris_peekB16BE(VM* vm, VAL buf, VAL off) {
750+ size_t offset = off->info.bits64;
751+ unsigned char* val = (GETTY(buf) == BUFOFFSET) ?
752+ buf->info.buf_offset->buf->info.buf->store + buf->info.buf_offset->offset + offset :
753+ buf->info.buf->store + offset;
754+ return MKB16(vm, ((uint16_t) val[1]) +
755+ (((uint16_t) val[0]) << 8));
756+}
757+
758+VAL idris_peekB32Native(VM* vm, VAL buf, VAL off) {
759+ size_t offset = off->info.bits64;
760+ uint32_t* val = (uint32_t *) ((GETTY(buf) == BUFOFFSET) ?
761+ buf->info.buf_offset->buf->info.buf->store + buf->info.buf_offset->offset + offset :
762+ buf->info.buf->store + offset);
763+ return MKB32(vm, *val);
764+}
765+
766+VAL idris_peekB32LE(VM* vm, VAL buf, VAL off) {
767+ size_t offset = off->info.bits64;
768+ unsigned char* val = (GETTY(buf) == BUFOFFSET) ?
769+ buf->info.buf_offset->buf->info.buf->store + buf->info.buf_offset->offset + offset :
770+ buf->info.buf->store + offset;
771+ return MKB32(vm, ((uint32_t) val[0]) +
772+ (((uint32_t) val[1]) << 8) +
773+ (((uint32_t) val[2]) << 16) +
774+ (((uint32_t) val[3]) << 24));
775+}
776+
777+VAL idris_peekB32BE(VM* vm, VAL buf, VAL off) {
778+ size_t offset = off->info.bits64;
779+ unsigned char* val = (GETTY(buf) == BUFOFFSET) ?
780+ buf->info.buf_offset->buf->info.buf->store + buf->info.buf_offset->offset + offset :
781+ buf->info.buf->store + offset;
782+ return MKB32(vm, ((uint32_t) val[3]) +
783+ (((uint32_t) val[2]) << 8) +
784+ (((uint32_t) val[1]) << 16) +
785+ (((uint32_t) val[0]) << 24));
786+}
787+
788+VAL idris_peekB64Native(VM* vm, VAL buf, VAL off) {
789+ size_t offset = off->info.bits64;
790+ uint64_t* val = (uint64_t *) ((GETTY(buf) == BUFOFFSET) ?
791+ buf->info.buf_offset->buf->info.buf->store + buf->info.buf_offset->offset + offset :
792+ buf->info.buf->store + offset);
793+ return MKB64(vm, *val);
794+}
795+
796+VAL idris_peekB64LE(VM* vm, VAL buf, VAL off) {
797+ size_t offset = off->info.bits64;
798+ unsigned char* val = (GETTY(buf) == BUFOFFSET) ?
799+ buf->info.buf_offset->buf->info.buf->store + buf->info.buf_offset->offset + offset :
800+ buf->info.buf->store + offset;
801+ return MKB64(vm, ((uint64_t) val[0]) +
802+ (((uint64_t) val[1]) << 8) +
803+ (((uint64_t) val[2]) << 16) +
804+ (((uint64_t) val[3]) << 24) +
805+ (((uint64_t) val[4]) << 32) +
806+ (((uint64_t) val[5]) << 40) +
807+ (((uint64_t) val[6]) << 48) +
808+ (((uint64_t) val[7]) << 56));
809+}
810+
811+VAL idris_peekB64BE(VM* vm, VAL buf, VAL off) {
812+ size_t offset = off->info.bits64;
813+ unsigned char* val = (GETTY(buf) == BUFOFFSET) ?
814+ buf->info.buf_offset->buf->info.buf->store + buf->info.buf_offset->offset + offset :
815+ buf->info.buf->store + offset;
816+ return MKB64(vm, ((uint64_t) val[7]) +
817+ (((uint64_t) val[6]) << 8) +
818+ (((uint64_t) val[5]) << 16) +
819+ (((uint64_t) val[4]) << 24) +
820+ (((uint64_t) val[3]) << 32) +
821+ (((uint64_t) val[2]) << 40) +
822+ (((uint64_t) val[1]) << 48) +
823+ (((uint64_t) val[0]) << 56));
824+}
825+
478826typedef struct {
479827 VM* vm; // thread's VM
480828 VM* callvm; // calling thread's VM
@@ -552,6 +900,21 @@ VAL copyTo(VM* vm, VAL x) {
552900 case STRING:
553901 cl = MKSTRc(vm, x->info.str);
554902 break;
903+ case BUFFER:
904+ cl = MKBUFFERc(vm, x->info.buf);
905+ break;
906+ case BUFOFFSET:
907+ // The underlying Buffer might already exist in the new heap
908+ // but we can't do anything reasonable about it
909+ if (x->info.buf_offset->offset == 0)
910+ cl = MKBUFFERc(vm, x->info.buf_offset->buf->info.buf);
911+ else {
912+ // Could theoretically make this a bit more efficient
913+ // by allocating the BufOffset and Buffer together
914+ cl = MKBUFOFFSETc(vm, x->info.buf_offset);
915+ cl->info.buf_offset->buf = MKBUFFERc(vm, x->info.buf_offset->buf->info.buf);
916+ }
917+ break;
555918 case BIGINT:
556919 cl = MKBIGMc(vm, x->info.ptr);
557920 break;