diff --git a/src/builtins/sfns.c b/src/builtins/sfns.c index 31697d16..c976b73c 100644 --- a/src/builtins/sfns.c +++ b/src/builtins/sfns.c @@ -836,9 +836,9 @@ B join_c2(B t, B w, B x) { if (wr==1 && inplace_add(w, x)) return w; x = m_atomUnit(x); } - usz wia = a(w)->ia; usz* wsh = a(w)->sh; - usz xia = a(x)->ia; usz* xsh = a(x)->sh; ur xr = rnk(x); + ur xr = rnk(x); B f = fill_both(w, x); + ur c = wr>xr?wr:xr; if (c==0) { HArr_p r = m_harrUv(2); @@ -847,27 +847,47 @@ B join_c2(B t, B w, B x) { return qWithFill(r.b, f); } if (c-wr > 1 || c-xr > 1) thrF("∾: Argument ranks must differ by 1 or less (%i≡=𝕨, %i≡=𝕩)", wr, xr); + + bool reusedW; + B r = arr_join_inline(w, x, false, &reusedW); if (c==1) { - B r = vec_join_inline(w, x); if (rnk(r)==0) srnk(r,1); - return qWithFill(r, f); - } - MAKE_MUT(r, wia+xia); mut_init(r, el_or(TI(w,elType), TI(x,elType))); - MUTG_INIT(r); - mut_copyG(r, 0, w, 0, wia); - mut_copyG(r, wia, x, 0, xia); - Arr* ra = mut_fp(r); - usz* sh = arr_shAlloc(ra, c); - if (sh) { + } else { + assert(c>1); + ur rnk0 = rnk(r); + ShArr* sh0 = shObj(r); + usz wia; + usz* wsh; + if (wr==1 && reusedW) { + wia = a(w)->ia-a(x)->ia; + wsh = &wia; + } else { + wsh = a(w)->sh; // when wr>1, shape object won't be disturbed by arr_join_inline + } + usz* xsh = a(x)->sh; + srnk(r, 0); // otherwise shape allocation failing may break things + usz* rsh = arr_shAlloc(a(r), c); + #if PRINT_JOIN_REUSE + printf(reusedW? "reuse:1;" : "reuse:0;"); + #endif for (i32 i = 1; i < c; i++) { usz s = xsh[i+xr-c]; - if (wsh[i+wr-c] != s) { mut_pfree(r, wia+xia); thrF("∾: Lengths not matchable (%H ≡ ≢𝕨, %H ≡ ≢𝕩)", w, x); } - sh[i] = s; + if (RARE(wsh[i+wr-c] != s)) { + B msg = make_fmt("∾: Lengths not matchable (%2H ≡ ≢𝕨, %H ≡ ≢𝕩)", wr, wsh, x); + if (rnk0>1) decShObj(sh0); + mm_free((Value*)shObjS(rsh)); + arr_shVec(a(r)); + thr(msg); + } + rsh[i] = s; } - sh[0] = (wr==c? wsh[0] : 1) + (xr==c? xsh[0] : 1); + rsh[0] = (wr==c? wsh[0] : 1) + (xr==c? xsh[0] : 1); + if (rnk0>1) decShObj(sh0); } - decG(w); decG(x); - return qWithFill(taga(ra), f); + + decG(x); + if (!reusedW) decG(w); + return qWithFill(r, f); } diff --git a/src/core/stuff.c b/src/core/stuff.c index ae87d7a6..c5e163b3 100644 --- a/src/core/stuff.c +++ b/src/core/stuff.c @@ -248,10 +248,19 @@ NOINLINE B do_fmt(B s, char* p, va_list a) { s = appendRaw(s, bqn_repr(inc(b))); break; } + case '2': case 'H': { - B o = va_arg(a, B); - ur r = isArr(o)? rnk(o) : 0; - usz* sh = isArr(o)? a(o)->sh : NULL; + ur r; + usz* sh; + if (c=='2') { + if ('H' != *p++) err("Invalid format string: expected H after %2"); + r = va_arg(a, int); + sh = va_arg(a, usz*); + } else { + B o = va_arg(a, B); + r = isArr(o)? rnk(o) : 0; + sh = isArr(o)? a(o)->sh : NULL; + } if (r==0) AU("⟨⟩"); else if (r==1) AFMT("⟨%s⟩", sh[0]); else { diff --git a/src/core/stuff.h b/src/core/stuff.h index 2c5e3d43..915a6c98 100644 --- a/src/core/stuff.h +++ b/src/core/stuff.h @@ -26,7 +26,8 @@ typedef struct ShArr { static ShArr* shObjS(usz* x) { return RFLD(x, ShArr, a); } static ShArr* shObj (B x) { return RFLD(a(x)->sh, ShArr, a); } static ShArr* shObjP(Value* x) { return RFLD(((Arr*)x)->sh, ShArr, a); } -static void decSh(Value* x) { if (RARE(prnk(x)>1)) tptr_dec(shObjP(x), mm_free); } +static void decShObj(ShArr* x) { tptr_dec(x, mm_free); } +static void decSh(Value* x) { if (RARE(prnk(x)>1)) decShObj(shObjP(x)); } // some array stuff @@ -51,10 +52,11 @@ static Arr* arr_shVec(Arr* x) { x->sh = &x->ia; return x; } -static usz* arr_shAlloc(Arr* x, ur r) { // sets rank, allocates & returns shape (or null if r<2) +static usz* arr_shAlloc(Arr* x, ur r) { // sets rank, allocates & returns shape (or null if r<2); assumes x has rank≤1 (which will be the case for new allocations) + assert(prnk(x)<=1); if (r>1) { - usz* sh = x->sh = m_shArr(r)->a; - sprnk(x,r); // is m_shArr OOMs, rank is gonna stay the 0 from the initial write in allocL, which is "safe" + usz* sh = x->sh = m_shArr(r)->a; // if m_shArr fails, the assumed rank≤1 guarantees the uninitialized x->sh won't break + sprnk(x,r); return sh; } sprnk(x,r); diff --git a/src/utils/mut.c b/src/utils/mut.c index 42377b5b..ff9a099a 100644 --- a/src/utils/mut.c +++ b/src/utils/mut.c @@ -54,7 +54,8 @@ NOINLINE void mut_pfree(Mut* m, usz n) { // free the first n elements B vec_join(B w, B x) { - return vec_join_inline(w, x); + bool unused; + return arr_join_inline(w, x, true, &unused); } diff --git a/src/utils/mut.h b/src/utils/mut.h index 47841eec..f41c73f3 100644 --- a/src/utils/mut.h +++ b/src/utils/mut.h @@ -157,7 +157,10 @@ static void bit_cpy(u64* r, usz rs, u64* x, usz xs, usz l) { } B vec_join(B w, B x); // consumes both -FORCE_INLINE B vec_join_inline(B w, B x) { + +// if `consume==true`, consumes w,x and expects both args to be vectors +// else, doesn't consume x, and decrements refcount of w iif *reusedW (won't free because the result will be w) +FORCE_INLINE B arr_join_inline(B w, B x, bool consume, bool* reusedW) { usz wia = a(w)->ia; usz xia = a(x)->ia; usz ria = wia+xia; @@ -166,14 +169,14 @@ FORCE_INLINE B vec_join_inline(B w, B x) { u8 wt = v(w)->type; // TODO f64∾i32, i32∾i8, c32∾c8 etc switch (wt) { - case t_bitarr: if (BITARR_SZ( ria)ia=ria; bit_cpy(bitarr_ptr(w),wia,bitarr_ptr(x),0,xia); decG(x); return FL_KEEP(w,fl_squoze); } break; - case t_i8arr: if (TYARR_SZ(I8, ria)ia=ria; memcpy(i8arr_ptr (w)+wia, i8any_ptr (x), xia*1); decG(x); return FL_KEEP(w,fl_squoze); } break; - case t_i16arr: if (TYARR_SZ(I16,ria)ia=ria; memcpy(i16arr_ptr(w)+wia, i16any_ptr(x), xia*2); decG(x); return FL_KEEP(w,fl_squoze); } break; - case t_i32arr: if (TYARR_SZ(I32,ria)ia=ria; memcpy(i32arr_ptr(w)+wia, i32any_ptr(x), xia*4); decG(x); return FL_KEEP(w,fl_squoze); } break; - case t_c8arr: if (TYARR_SZ(C8, ria)ia=ria; memcpy(c8arr_ptr (w)+wia, c8any_ptr (x), xia*1); decG(x); return FL_KEEP(w,fl_squoze); } break; - case t_c16arr: if (TYARR_SZ(C16,ria)ia=ria; memcpy(c16arr_ptr(w)+wia, c16any_ptr(x), xia*2); decG(x); return FL_KEEP(w,fl_squoze); } break; - case t_c32arr: if (TYARR_SZ(C32,ria)ia=ria; memcpy(c32arr_ptr(w)+wia, c32any_ptr(x), xia*4); decG(x); return FL_KEEP(w,fl_squoze); } break; - case t_f64arr: if (TYARR_SZ(F64,ria)ia=ria; memcpy(f64arr_ptr(w)+wia, f64any_ptr(x), xia*8); decG(x); return FL_KEEP(w,fl_squoze); } break; + case t_bitarr: if (BITARR_SZ( ria)ia=ria; bit_cpy(bitarr_ptr(w),wia,bitarr_ptr(x),0,xia); goto rw; } break; + case t_i8arr: if (TYARR_SZ(I8, ria)ia=ria; memcpy(i8arr_ptr (w)+wia, i8any_ptr (x), xia*1); goto rw; } break; + case t_i16arr: if (TYARR_SZ(I16,ria)ia=ria; memcpy(i16arr_ptr(w)+wia, i16any_ptr(x), xia*2); goto rw; } break; + case t_i32arr: if (TYARR_SZ(I32,ria)ia=ria; memcpy(i32arr_ptr(w)+wia, i32any_ptr(x), xia*4); goto rw; } break; + case t_c8arr: if (TYARR_SZ(C8, ria)ia=ria; memcpy(c8arr_ptr (w)+wia, c8any_ptr (x), xia*1); goto rw; } break; + case t_c16arr: if (TYARR_SZ(C16,ria)ia=ria; memcpy(c16arr_ptr(w)+wia, c16any_ptr(x), xia*2); goto rw; } break; + case t_c32arr: if (TYARR_SZ(C32,ria)ia=ria; memcpy(c32arr_ptr(w)+wia, c32any_ptr(x), xia*4); goto rw; } break; + case t_f64arr: if (TYARR_SZ(F64,ria)ia=ria; memcpy(f64arr_ptr(w)+wia, f64any_ptr(x), xia*8); goto rw; } break; case t_harr: if (fsizeof(HArr,a,B,ria)ia = ria; B* rp = harr_ptr(w)+wia; @@ -190,23 +193,28 @@ FORCE_INLINE B vec_join_inline(B w, B x) { case el_c16: { u16* xp=c16any_ptr(x); for (usz i=0; iia; usz ria = wia+1; diff --git a/test/ffi/ffiTest.c b/test/ffi/ffiTest.c index ee1c0cae..a3f46390 100644 --- a/test/ffi/ffiTest.c +++ b/test/ffi/ffiTest.c @@ -120,7 +120,7 @@ int32_t directAccess(BQNV x) { uint32_t res = 0; if (e==elt_i32) { size_t bound = bqn_bound(x); - int32_t* els = bqn_directI32(x); + const int32_t* els = bqn_directI32(x); for (size_t i = 0; i < bound; i++) res = res*31 + (uint32_t)els[i]; } else { printf("not elt_i32!\n"); diff --git a/test/joinReuse.bqn b/test/joinReuse.bqn new file mode 100644 index 00000000..e0551083 --- /dev/null +++ b/test/joinReuse.bqn @@ -0,0 +1,30 @@ +⟨V⇐Variation ⋄ LV⇐ListVariations⟩←•internal +AllEq ← !¨ (<∘⊑≡¨⊢) + +# run with -DPRINT_JOIN_REUSE +# all that's important in the output is that there are at least some "reuse:1"s and "reuse:0"s for each item + +{ w𝕊x: + •Out "next" + AllEq t ← {(⥊ ⋈ ≢) (𝕨 V w)∾𝕩 V x}⌜´ LV¨ 𝕨‿𝕩 + •Out "" ⋄ •Show ⊑t +}´¨ ⟨ + "ab"‿["01","23"] + ["ab","cd"]‿"01" + ["ab","cd","ef"]‿["01","23"] + [["ab","cd"]⋄["ef","gh"]⋄["ij","kl"]]‿["01","23"] +⟩ + +{𝕊: + + { w𝕊x: + •Out "next" + AllEq t ← {(𝕨 V w)∾𝕩 V x ⋄ •Out "Expected error, didn't get one; fail" ⋄ •Exit 1}⎊{𝕊:•CurrentError@}⌜´ LV¨ 𝕨‿𝕩 + •Out "" ⋄ •Show ⊑t + }´¨ ⟨ + "abc"‿["01","23"] + ["ab","cd"]‿"012" + ["ab","cd","ef"]‿["012","345"] + [["ab","cd"]⋄["ef","gh"]⋄["ij","kl"]]‿["012","345"] + ⟩ +}⍟⊢ 0=≠•args \ No newline at end of file