On Saturday, 19 January 2013 at 09:07:03 UTC, Dmitry Olshansky wrote:
opBinaryAssign --> opOpAssign. opSlice/opSlice assign. In any case it seems to me that (I think you already did this trick before) you can reuse a lot of code by making an operator string a template parameter (for functions that implement =, |=, &=, ^=, and therefore ^, |, &) and use string mixins.

Yes I did when I could; The slice versions can only forwards them to the bitarray one.

Other then this - don't forget the '~' _unary_ operator as it makes a lot of sense for bit-vectors. Now since slices don't support '~' concatenation it won't look so bad. Worst case:

auto ba = BitArray(...);
auopt slice = ba[...];
ba ~= ~slice;

Only works if you create a new version and append it; And all opBinary operations make duplicates due to their nature. So the above couldn't work if slice is a separate type, and instead:

  auto slice = ba[...];   //slice type of BitArraySlice
  auto bc ~= ~slice;      //ba/bc type of BitArray

  ba = (ba ~ (~slice))[]; //type of BitArraySlice


And it's not much of problem.

Hmmm.. Well as mentioned I don't think a separate slice/range type is the best design or as practical; I have it half working but one problem I'm really having is trying to make sense of the errors after I converted to use a pointer. It's confusing since it was compiling before and is an annoyance more-so now; And suddenly can't deduce the type when it should be able to, and if I explicitly call it with the type it doesn't help any.


[quote]
Error: template bitmanip.BitArray!(Fixed!(1024)).BitArray.opEquals(alias SL)(const SL rhs) if (hasMember!(SL, "isBitArray") || hasMember!(SL, "isBitArraySlice")) forward reference to template opEquals(alias SL)(const SL rhs) if (hasMember!(SL, "isBitArray") || hasMember!(SL, "isBitArraySlice")) Error: template bitmanip.BitArray!(Fixed!(1024)).BitArray.opEquals cannot deduce template function from argument types !()(BitArraySlice!(BitArray!(Fixed!(1024)), true),const(ulong),const(ulong))

Error: cannot implicitly convert expression ((*this.slice).toString(this.start, this.end)) of type string to string
[/quote]

[code]
  //isBitArray and isBitArraySlice are enums in the structs
  //almost all functions templated to handle fixed/dynamic
  //types as compatible. Plus constness for range(s).

  //auto ref would be nice/preferred in a lot in these functions..
//Otherwise copying is required unless i do more duplication/code forwarding

//if opSlice is @system, then almost all these are @system, or @trusted

  //slice forwarding
  bool opEquals(SL)(const SL rhs) const
if (hasMember!(SL, "isBitArray") || hasMember!(SL, "isBitArraySlice"))
  {
    //opSlice always returns BitArraySlice
    return opEquals(rhs[], 0, length);
  }

bool opEquals(SL)(const SL rhs, ulong sliceStart, ulong sliceEnd) const
  if (hasMember!(SL, "isBitArraySlice"))
  {
    if ((sliceEnd-sliceStart) != rhs.length)
      return false;

      return opCmp(rhs, sliceStart, sliceEnd) == 0;
  }

  //slice forwarding
  int opCmp(SL)(const SL rhs) const
  if (hasMember!(SL, "isBitArray"))
  {
    //opSlice always returns BitArraySlice
    return opCmp(rhs[], 0, length);
  }

int opCmp(SL)(const SL rhs, ulong sliceStart, ulong sliceEnd) const
  if (hasMember!(SL, "isBitArraySlice"));
[/code]



A small consideration was coming to mind in cases where storage management if you only needed to prepend a few bits or append just after for a few bits but don't need to do a full reallocation. In that case perhaps a before/after allocated memory would serve nice.

  size_t beforeArray, afterArray;
int beforeUsed, afterUsed; //course would likely be bitfield or ubyte

Now if we use that it comes to mind that although it wouldn't be perfect storage for small compact arrays (as was the original complaint(s)), you could however rely on them just as much as the main array and the first size_t bits (*2 if you prepend/append to the array a lot. It would also be useful to append past the end of a sliced area without tampering with data outside it's access area until it's forced to resize.


Fixed BitArraySizes seem far more wanted where you don't want to do extra memory allocation, so using reference counting wouldn't work, but with dynamic arrays you could. So if you did all the slices to a particular bit array could always point to the proper memory.

If you did do that, prepending to an array would be a nightmare unless you could offset it with a virtual offset to represent the actual beginning. So...

  //start in the middle
  struct STORE {
    //virtualOffset instead?
    ulong startOffset = ulong.max / 2;
    size_t[] store;
  }

  //in slice/Bitarray, hope that's right
  RefCounted!(STORE) store;
  ulong start, end;

With these three you can safely do dynamic slices while all slices point to the same data; in cases where there's only one reference store, start, end & startOffset can freely be changed.

 Assume you slice. So..

  BitArray ba = BitArray(100);
  auto sl = ba[10 .. 20];

  //assume startOffset says say 1024, start, 1024 & end = 1124.
  //slice holds start 1034, end 1044.

 Now if we have it prepend a whole lot.

  foreach(i; 0 .. 100)
    ba = true ~ ba;

 It had to be resized, so if it gave it say 512 more bits then...
  //now startOffset = 512.

slice[0] = true; //start-startOffset = actual bit offset in store[]
  assert(ba[110]); //was set by the slice

Full reference could be preserved, although if you prepend to a slice vs the main array then either you overlap data or need some temporary storage until duping/reallocating is required, or just dup/allocate new memory. Or have a separate end/before arrays which won't force reallocation but could make for interesting overlapping as they wouldn't all always point to the same thing.


 The interesting problems that crop up again.


Another thought, is what if we give an ID to fixed arrays so they know if they are pointing to proper data or junk; This would likely mostly be for the known fixed array slices. So..

  size_t ID; //in both slice and fixed array.
  size_t[1024] fixedSize;

Now add a variant to ensure the ID's match on each and every access. If the ID gets corrupted the data is corrupted too, although not a guarantee it's pretty darn close if the ID is random.


But as you mentioned making it @system then we could have it silently convert the BitArray from fixed to dynamic; And make the slice @system where normally it's @safe (although immediately duping the memory location after it's using the dynamic form would be safe).


Reminds me... Should initialization/blit dup or reference/preserve the memory? I'd think it would point to the old memory unless explicitly told to (or going from const to non-const versions).

Reply via email to