Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

bitfields updates (further)

issues dealt with:
4935 - std.bitmanip: bitfields!() template with trailing unnamed field
does not work
8474 - bitfields doesn't work with 32 bit fields
6686 - bitmanip bitfields are broken at 64 bits
5942 - bitfields overwritten erroneously:
5520 - bitfieldsOn
4425 - More bells & whistles for bitfields

Added static check to revert a bitfield to normal type if it is the only
one (and at full size). 32bit as first field no longer errors. Bitfields
should be separated and the no longer overwrite the whole thing (masking
fixed).

bitfields are now pure, safe and nothrow.

bitfieldsOn added (not pure).
  • Loading branch information...
commit 620ba57cc0a860245a2bf03f7b7f5d6a1bb58312 1 parent 729c96e
Ryan Cecil authored

Showing 1 changed file with 194 additions and 67 deletions. Show diff stats Hide diff stats

  1. 261  std/bitmanip.d
261  std/bitmanip.d
@@ -10,7 +10,8 @@ WIKI = StdBitarray
10 10
 Copyright: Copyright Digital Mars 2007 - 2011.
11 11
 License:   <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
12 12
 Authors:   $(WEB digitalmars.com, Walter Bright),
13  
-           $(WEB erdani.org, Andrei Alexandrescu)
  13
+           $(WEB erdani.org, Andrei Alexandrescu),
  14
+           Era Scarecrow
14 15
 Source: $(PHOBOSSRC std/_bitmanip.d)
15 16
 */
16 17
 /*
@@ -25,9 +26,14 @@ module std.bitmanip;
25 26
 
26 27
 import core.bitop;
27 28
 import std.traits;
  29
+import std.algorithm;
  30
+import std.string;
  31
+import std.ascii;
28 32
 import std.stdio : writeln, writefln, writef;
  33
+import core.stdc.stdio;
29 34
 
30  
-private pure int indexOf(string str, char letter)
  35
+//CTFE has issues if you use the std.string version at present
  36
+private int indexOf(string str, char letter) pure
31 37
 {
32 38
     foreach(i, c; str)
33 39
         if (c == letter)
@@ -35,17 +41,7 @@ private pure int indexOf(string str, char letter)
35 41
     return -1;
36 42
 }
37 43
 
38  
-//remove spaces from beginning/end of string
39  
-private pure string trim(string str)
40  
-{
41  
-    while (str.length && str[0] == ' ')
42  
-        str = str[1 .. $];
43  
-    while (str.length && str[$-1] == ' ')
44  
-        str = str[0 .. $-1];
45  
-    return str;
46  
-}
47  
-
48  
-private pure string myToStringHex(ulong n)
  44
+private string myToStringHex(ulong n) pure
49 45
 {
50 46
     enum s = "0123456789abcdef";
51 47
     enum len = s.length;
@@ -56,7 +52,7 @@ private pure string myToStringHex(ulong n)
56 52
 }
57 53
 
58 54
 //for debugging
59  
-private pure string myToStringx(long n)
  55
+private string myToStringx(long n) pure
60 56
 {
61 57
     enum s = "0123456789";
62 58
     enum len = s.length;
@@ -70,7 +66,7 @@ private pure string myToStringx(long n)
70 66
         return myToStringx(n / len) ~ myToStringx(n % len);
71 67
 }
72 68
 
73  
-private pure ulong myToLongFromHex(string str)
  69
+private ulong myToLongFromHex(string str) pure
74 70
 {
75 71
     enum l = "0123456789abcdef";
76 72
     enum u = "0123456789ABCDEF";
@@ -90,7 +86,7 @@ private pure ulong myToLongFromHex(string str)
90 86
     return sum;
91 87
 }
92 88
 
93  
-private pure ulong myToLong(string str)
  89
+private ulong myToLong(string str) pure
94 90
 {
95 91
     ulong sum;
96 92
 
@@ -144,33 +140,34 @@ unittest
144 140
     assert(myToString(45) == "45");
145 141
     assert(myToString(1UL << 32) == "0x100000000UL");
146 142
 
147  
-    assert(trim("   123   ") == "123");
  143
+    assert(strip("   123   ") == "123");
148 144
 }
149 145
 
150  
-private pure string myToString(long n)
  146
+
  147
+//within a certain size we don't need to bother with hex, right?
  148
+//if you print the mixin data out it makes a bit more sense this way.
  149
+private string myToString(long n) pure
151 150
 {
152  
-    //within a certain size we don't need to bother with hex, right?
153 151
     if (n >= short.min && n <= short.max)
154 152
         return myToStringx(n);
155 153
 
156  
-    return myToStringHex(n) ~ ((n > uint.max || n < uint.min) ? "UL" : "U");
  154
+    return myToStringHex(n) ~ "UL";
157 155
 }
158 156
 
159 157
 //pair to split name=value into their two halfs safely.
160  
-//add built in 'trim' to them?
161  
-private pure string getName(string nameAndValue)
  158
+private string getName(string nameAndValue)
162 159
 {
163 160
     int equalsChar = nameAndValue.indexOf('=');
164 161
     if (equalsChar != -1)
165  
-        return trim(nameAndValue[0 .. equalsChar]);
166  
-    return trim(nameAndValue);
  162
+        return strip(nameAndValue[0 .. equalsChar]);
  163
+    return strip(nameAndValue);
167 164
 }
168 165
 
169  
-private pure ulong getValue(string nameAndValue)
  166
+private ulong getValue(string nameAndValue)
170 167
 {
171 168
     int equalsChar = nameAndValue.indexOf('=');
172 169
     if (equalsChar != -1)
173  
-        return myToLong(trim(nameAndValue[equalsChar+1 .. $]));
  170
+        return myToLong(strip(nameAndValue[equalsChar+1 .. $]));
174 171
     return 0;
175 172
 }
176 173
 
@@ -188,7 +185,7 @@ unittest
188 185
 
189 186
 
190 187
 private template createAccessors(
191  
-    string store, T, string nameAndValue, size_t len, size_t offset)
  188
+    string store, string attributes, T, string nameAndValue, size_t len, size_t offset)
192 189
 {
193 190
     enum name = getName(nameAndValue),
194 191
                 defaultValue = getValue(nameAndValue);
@@ -210,9 +207,9 @@ private template createAccessors(
210 207
         else
211 208
             alias ulong MasksType;
212 209
         enum MasksType
213  
-            maskAllElse = ((1uL << len) - 1u) << offset,
  210
+            maskAllElse = ((1uL << len) - 1) << offset,
214 211
             signBitCheck = 1uL << (len - 1),
215  
-            extendSign = ~((cast(MasksType)1u << len) - 1);
  212
+            extendSign = cast(MasksType) ~((1uL << len) - 1);
216 213
         static if (T.min < 0)
217 214
         {
218 215
             enum long minVal = -(1uL << (len - 1));
@@ -245,26 +242,27 @@ private template createAccessors(
245 242
             enum result =
246 243
             // constants
247 244
                 //only optional for cleaner namespace
248  
-                (defVal ? "enum " ~ name ~ "_def = " ~ myToString(defVal) ~ ";\n" : "\n")
  245
+                //two underscores used since much less likely to clash with other fields
  246
+                (defVal ? "enum " ~ name ~ "__def = " ~ myToString(defVal) ~ ";\n" : "\n")
249 247
             // getter
250  
-                ~"@property @safe pure nothrow bool " ~ name ~ "() const { return "
  248
+                ~"bool " ~ name ~ "() " ~ attributes ~ " const { return "
251 249
                 ~"("~store~" & "~myToString(maskAllElse)~") != 0;}\n"
252 250
             // setter
253  
-                ~"@property @safe pure nothrow void " ~ name ~ "(bool v){"
  251
+                ~"void " ~ name ~ "(bool v) " ~ attributes ~ " {"
254 252
                 ~"if (v) "~store~" |= "~myToString(maskAllElse)~";"
255 253
                 ~"else "~store~" &= ~"~myToString(maskAllElse)~";}\n";
256 254
         }
257 255
         else
258 256
         {
259 257
             // constants
260  
-            enum result = "enum "~T.stringof~" "~name~"_min = cast("~T.stringof~")"
  258
+            enum result = "enum "~T.stringof~" "~name~"__min = cast("~T.stringof~")"
261 259
                 ~(minVal < 0 ? myToString(cast(long) minVal) : myToString(minVal))~"; "
262  
-                ~" enum "~T.stringof~" "~name~"_max = cast("~T.stringof~")"
  260
+                ~" enum "~T.stringof~" "~name~"__max = cast("~T.stringof~")"
263 261
                 ~myToString(maxVal)~"; "
264 262
                 //only optional for cleaner namespace
265  
-                ~ (defVal ? "enum " ~ name ~ "_def = " ~ myToString(defVal) ~ ";\n" : "\n")
  263
+                ~ (defVal ? "enum " ~ name ~ "__def = " ~ myToString(defVal) ~ ";\n" : "\n")
266 264
             // getter
267  
-                ~ "@property @safe "~T.stringof~" "~name~"() pure nothrow const { auto result = "
  265
+                ~ ""~T.stringof~" "~name~"() " ~ attributes ~ " const { auto result = "
268 266
                 ~ "("~store~" & "
269 267
                 ~ myToString(maskAllElse) ~ ") >>"
270 268
                 ~ myToString(offset) ~ ";"
@@ -274,9 +272,9 @@ private template createAccessors(
274 272
                    : "")
275 273
                 ~ " return cast("~T.stringof~") result;}\n"
276 274
             // setter
277  
-                ~"@property @safe void "~name~"("~T.stringof~" v) pure nothrow { "
278  
-                ~"assert(v >= "~name~"_min); "
279  
-                ~"assert(v <= "~name~"_max); "
  275
+                ~"void "~name~"("~T.stringof~" v) " ~ attributes ~ " { "
  276
+                ~"assert(v >= "~name~"__min, \"bitfield '" ~ name ~ "' assignment < min\"); "
  277
+                ~"assert(v <= "~name~"__max, \"bitfield '" ~ name ~ "' assignment > max\"); "
280 278
                 ~store~" = cast(typeof("~store~"))"
281 279
                 " (("~store~" & ~"~myToString(maskAllElse)~")"
282 280
                 " | ((cast(typeof("~store~")) v << "~myToString(offset)~")"
@@ -300,7 +298,7 @@ unittest
300 298
     assert(createStoreName!(int, " abc = -5 ", 1, int, " def = true ", 1) == "_abc_def");
301 299
 }
302 300
 
303  
-private template createFields(string store, size_t offset, string defaults, Ts...)
  301
+private template createFields(string store, bool suppliedName, size_t offset, string defaults, Ts...)
304 302
 {
305 303
     static if (!Ts.length)
306 304
     {
@@ -317,23 +315,26 @@ private template createFields(string store, size_t offset, string defaults, Ts..
317 315
             static assert(false, "Field widths must sum to 8, 16, 32, or 64");
318 316
             alias ulong StoreType; // just to avoid another error msg
319 317
         }
320  
-        //if we have any defaults, auto assign, otherwise blank.
321  
-        //bitmanip used in a union we will have a 'overlapping initialization' otherwise
322  
-        enum result = "private " ~ StoreType.stringof ~ " " ~ store ~
323  
-                (defaults.length ? " = " ~ defaults : "") ~ ";";
  318
+        static if (!suppliedName)
  319
+            //if we have any defaults, auto assign, otherwise blank.
  320
+            //bitmanip used in a union we will have a 'overlapping initialization' otherwise
  321
+            enum result = "private " ~ StoreType.stringof ~ " " ~ store ~
  322
+                    (defaults.length ? " = " ~ defaults : "") ~ ";";
  323
+        else
  324
+            enum result = "";
324 325
     }
325 326
     else
326 327
     {
327 328
         enum result
328  
-            = createAccessors!(store, Ts[0], Ts[1], Ts[2], offset).result
329  
-            ~ createFields!(store, offset + Ts[2], defaults ~
  329
+            = createAccessors!(store, "@property @safe nothrow " ~ (suppliedName ? "pure" : ""), Ts[0], Ts[1], Ts[2], offset).result
  330
+            ~ createFields!(store, suppliedName, offset + Ts[2], defaults ~
330 331
                 //if we have a bitfield name
331 332
                 (Ts[1].length ? (
332 333
                     //and it has a value
333 334
                     getValue(Ts[1]) ? (
334 335
                         //if we have a previous value, OR it, appending our new value
335 336
                         (defaults.length ? " | " : "")
336  
-                        ~getName(Ts[1]) ~ "_def"
  337
+                        ~getName(Ts[1]) ~ "__def"
337 338
                     ) : ""
338 339
                 ) : ""),
339 340
                 Ts[3 .. $]).result;
@@ -392,11 +393,56 @@ struct A
392 393
 The type of a bit field can be any integral type or enumerated
393 394
 type. The most efficient type to store in bitfields is $(D_PARAM
394 395
 bool), followed by unsigned types, followed by signed types.
  396
+
  397
+If there is only one field offered that fills the full 8/16/32/64 bit area
  398
+then it defaults to it's own type. Example:
  399
+
  400
+----
  401
+struct B
  402
+{
  403
+    mixin(bitfields!(int, "something", 32);
  404
+    //is equal to:
  405
+    int something;
  406
+}
  407
+----
395 408
 */
396 409
 
397 410
 template bitfields(T...)
398 411
 {
399  
-    enum { bitfields = createFields!(createStoreName!(T), 0, "", T).result }
  412
+    //if there's one entry and it maxes out, don't bother with bitfields
  413
+    //shift operator will complain anyways.
  414
+    static if (T.length == 3 && T[0].sizeof * 8 == T[2])
  415
+        enum { bitfields = T[0].stringof ~ " " ~ T[1] ~ ";" }
  416
+    else
  417
+        enum { bitfields = createFields!(createStoreName!(T), false, 0, "", T).result }
  418
+}
  419
+
  420
+
  421
+/**
  422
+Same as bitfields template, with the exception that you can set the
  423
+variable you want to be the target.
  424
+----
  425
+struct X {
  426
+    struct Y { uint something; }
  427
+    Y y;
  428
+    mixin(bitfieldsOn!("y.something", //target variable
  429
+        int, "a", 10,
  430
+        int, "b", 6));
  431
+}
  432
+
  433
+X x;
  434
+assert(x.y.something == 0);
  435
+x.a = 100;
  436
+x.b = 10;
  437
+
  438
+assert(x.y.something);   //check it was changed
  439
+assert(x.a == 100);      //check against our values.
  440
+assert(x.b == 10);
  441
+----
  442
+*/
  443
+template bitfieldsOn(string storeName, T...)
  444
+{
  445
+    enum { bitfieldsOn = createFields!(storeName, true, 0, "", T).result }
400 446
 }
401 447
 
402 448
 unittest
@@ -454,39 +500,39 @@ unittest
454 500
         mixin(bitfields!(
455 501
                 bool, "b_f=false", 1,
456 502
                 bool, "b_t=true", 1,
457  
-                uint, "i_min=0", 3,
458  
-                uint, "i_max=7", 3,
  503
+                uint, "ii_min=0", 3,
  504
+                uint, "ii_max=7", 3,
459 505
                 short, "  s_min  =  -8  ", 4,   //test spaces
460 506
                 short, "s_max=7", 4));
461 507
 
462 508
         mixin(bitfields!(
463  
-                bool, "b_", 1,
464  
-                uint, "i_", 3,
465  
-                short, "  s_  ", 4));
  509
+                bool, "b", 1,
  510
+                uint, "ii", 3,
  511
+                short, "  s  ", 4));
466 512
     }
467 513
     WithDefaults wd;
468 514
 
469 515
     with(wd) {
470 516
         //non-specified variables go to 0
471  
-        assert(b_ == false);
472  
-        assert(i_ == 0);
473  
-        assert(s_ == 0);
474  
-        
  517
+        assert(b == false);
  518
+        assert(ii == 0);
  519
+        assert(s == 0);
  520
+
475 521
         //assigned defaults should be set.
476 522
         assert(b_f == false);
477 523
         assert(b_t == true);
478  
-        assert(i_min == 0);
479  
-        assert(i_max == 7);
  524
+        assert(ii_min == 0);
  525
+        assert(ii_max == 7);
480 526
         assert(s_min == -8);
481 527
         assert(s_max == 7);
482  
-        
483  
-        assert(i_min_max == i_max_max);
484  
-        assert(i_max_min == i_min_min);
485  
-        
486  
-        assert(i_min_min == i_min);
487  
-        assert(i_max_max == i_max);
488  
-        assert(s_min_min == s_min);
489  
-        assert(s_max_max == s_max);
  528
+
  529
+        assert(ii_min__max == ii_max__max);
  530
+        assert(ii_max__min == ii_min__min);
  531
+
  532
+        assert(ii_min__min == ii_min);
  533
+        assert(ii_max__max == ii_max);
  534
+        assert(s_min__min == s_min);
  535
+        assert(s_max__max == s_max);
490 536
     }
491 537
 
492 538
     /*
@@ -506,6 +552,87 @@ unittest
506 552
     }
507 553
 }
508 554
 
  555
+//bug 8474, 32bits size at beginning of mixin gives trouble.
  556
+//also 64bit total (long/ulong)
  557
+unittest {
  558
+    struct X {
  559
+        //single 16bit
  560
+        mixin(bitfields!(
  561
+            short, "ss",  16,
  562
+        ));
  563
+        
  564
+        //single 32bit
  565
+        mixin(bitfields!(
  566
+            uint, "ui",  32,
  567
+        ));
  568
+        
  569
+        //two 32bits
  570
+        mixin(bitfields!(
  571
+            int, "si1",  32,
  572
+            int, "si2",  32,
  573
+        ));
  574
+        
  575
+        //full 64bit
  576
+        mixin(bitfields!(
  577
+            ulong, "ul",  64,
  578
+        ));
  579
+        
  580
+        //check assignment
  581
+        //single 32bit
  582
+        mixin(bitfields!(
  583
+            uint, "ui_def=42",  32,
  584
+        ));
  585
+    }
  586
+    
  587
+    X x;
  588
+    
  589
+    assert(x.ui_def == 42);
  590
+}
  591
+
  592
+//issue 5942 - Bitfields are overwritten erroneously
  593
+unittest {
  594
+    struct S {
  595
+        mixin(bitfields!(
  596
+                int, "a" , 32,
  597
+                int, "b" , 32
  598
+            ));
  599
+    }
  600
+
  601
+    S data;
  602
+    
  603
+    data.b = 42;
  604
+    data.a = 1;
  605
+
  606
+    assert(data.b == 42);
  607
+}
  608
+
  609
+//issue 5520 - bitfieldsOn, supplying location for int/value to be affected.
  610
+unittest {
  611
+    struct X {
  612
+        struct Y { uint something; }
  613
+        Y y;
  614
+        mixin(bitfieldsOn!("y.something",
  615
+            int, "a", 10,
  616
+            int, "b", 10,
  617
+            int, "c", 10,
  618
+            uint, "d", 2
  619
+        ));
  620
+    }
  621
+
  622
+    X x;
  623
+    assert(x.y.something == 0);
  624
+    x.a = 10;
  625
+    x.b = 100;
  626
+    x.c = 500;
  627
+    x.d = 2;
  628
+    
  629
+    assert(x.y.something);  //check it was changed
  630
+    assert(x.a == 10);      //check against our values.
  631
+    assert(x.b == 100);
  632
+    assert(x.c == 500);
  633
+    assert(x.d == 2);
  634
+}
  635
+
509 636
 /**
510 637
    Allows manipulating the fraction, exponent, and sign parts of a
511 638
    $(D_PARAM float) separately. The definition is:

0 notes on commit 620ba57

Please sign in to comment.
Something went wrong with that request. Please try again.