Add wuffs_base__uxx__rotate_etc functions
diff --git a/fuzz/c/std/bmp_fuzzer.c b/fuzz/c/std/bmp_fuzzer.c
index 139e109..2f0f45d 100644
--- a/fuzz/c/std/bmp_fuzzer.c
+++ b/fuzz/c/std/bmp_fuzzer.c
@@ -66,7 +66,7 @@
   wuffs_base__status status = wuffs_bmp__decoder__initialize(
       &dec, sizeof dec, WUFFS_VERSION,
       (hash & 1) ? WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED : 0);
-  hash >>= 1;
+  hash = wuffs_base__u64__rotate_right(hash, 1);
   if (!wuffs_base__status__is_ok(&status)) {
     return wuffs_base__status__message(&status);
   }
diff --git a/fuzz/c/std/cbor_fuzzer.c b/fuzz/c/std/cbor_fuzzer.c
index 1df5b55..6b49617 100644
--- a/fuzz/c/std/cbor_fuzzer.c
+++ b/fuzz/c/std/cbor_fuzzer.c
@@ -221,12 +221,13 @@
 }
 
 uint64_t  //
-buffer_limit(uint64_t hash_6_bits, uint64_t min, uint64_t max) {
+buffer_limit(uint64_t hash, uint64_t min, uint64_t max) {
+  hash &= 0x3F;
   uint64_t n;
-  if (hash_6_bits < 0x20) {
-    n = min + hash_6_bits;
+  if (hash < 0x20) {
+    n = min + hash;
   } else {
-    n = max - (0x3F - hash_6_bits);
+    n = max - (0x3F - hash);
   }
   if (n < min) {
     return min;
@@ -237,15 +238,15 @@
 }
 
 const char*  //
-fuzz_complex(wuffs_base__io_buffer* full_src, uint64_t hash_56_bits) {
+fuzz_complex(wuffs_base__io_buffer* full_src, uint64_t hash) {
   uint64_t tok_limit = buffer_limit(
-      hash_56_bits & 0x3F, WUFFS_CBOR__DECODER_DST_TOKEN_BUFFER_LENGTH_MIN_INCL,
+      hash & 0x3F, WUFFS_CBOR__DECODER_DST_TOKEN_BUFFER_LENGTH_MIN_INCL,
       TOK_BUFFER_ARRAY_SIZE);
-  uint64_t hash_50_bits = hash_56_bits >> 6;
+  hash = wuffs_base__u64__rotate_right(hash, 6);
 
-  uint64_t src_limit =
-      buffer_limit(hash_50_bits & 0x3F,
-                   WUFFS_CBOR__DECODER_SRC_IO_BUFFER_LENGTH_MIN_INCL, 4096);
+  uint64_t src_limit = buffer_limit(
+      hash & 0x3F, WUFFS_CBOR__DECODER_SRC_IO_BUFFER_LENGTH_MIN_INCL, 4096);
+  hash = wuffs_base__u64__rotate_right(hash, 6);
 
   // ----
 
@@ -407,7 +408,7 @@
   // The fuzz_complex implementation adds many more Wuffs API specific checks
   // (e.g. that the sum of the tokens' lengths do not exceed the input length).
   if ((hash & 0xFF) != 0xA5) {
-    return fuzz_complex(full_src, hash >> 8);
+    return fuzz_complex(full_src, wuffs_base__u64__rotate_right(hash, 8));
   }
   return fuzz_simple(full_src);
 }
diff --git a/fuzz/c/std/gif_fuzzer.c b/fuzz/c/std/gif_fuzzer.c
index 2da0d1b..1dcfb44 100644
--- a/fuzz/c/std/gif_fuzzer.c
+++ b/fuzz/c/std/gif_fuzzer.c
@@ -61,7 +61,7 @@
 #include "../fuzzlib/fuzzlib.c"
 #include "../fuzzlib/fuzzlib_image_decoder.c"
 
-void set_quirks(wuffs_gif__decoder* dec, uint64_t hash_55_bits) {
+void set_quirks(wuffs_gif__decoder* dec, uint64_t hash) {
   uint32_t quirks[] = {
       WUFFS_GIF__QUIRK_DELAY_NUM_DECODED_FRAMES,
       WUFFS_GIF__QUIRK_FIRST_FRAME_LOCAL_PALETTE_MEANS_BLACK_BACKGROUND,
@@ -75,8 +75,8 @@
 
   uint32_t i;
   for (i = 0; quirks[i]; i++) {
-    uint64_t bit = 1 << (i % 55);
-    if (hash_55_bits & bit) {
+    uint64_t bit = 1 << (i & 63);
+    if (hash & bit) {
       wuffs_gif__decoder__set_quirk_enabled(dec, quirks[i], true);
     }
   }
@@ -88,13 +88,13 @@
   wuffs_base__status status = wuffs_gif__decoder__initialize(
       &dec, sizeof dec, WUFFS_VERSION,
       (hash & 1) ? WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED : 0);
-  hash >>= 1;
+  hash = wuffs_base__u64__rotate_right(hash, 1);
   uint64_t hash_8_bits = hash & 0xFF;
-  uint64_t hash_55_bits = hash >> 8;
+  hash = wuffs_base__u64__rotate_right(hash, 8);
   if (!wuffs_base__status__is_ok(&status)) {
     return wuffs_base__status__message(&status);
   }
-  set_quirks(&dec, hash_55_bits);
+  set_quirks(&dec, hash);
   return fuzz_image_decoder(
       src, hash_8_bits,
       wuffs_gif__decoder__upcast_as__wuffs_base__image_decoder(&dec));
diff --git a/fuzz/c/std/json_fuzzer.c b/fuzz/c/std/json_fuzzer.c
index 0c22678..1a6a858 100644
--- a/fuzz/c/std/json_fuzzer.c
+++ b/fuzz/c/std/json_fuzzer.c
@@ -209,12 +209,13 @@
 }
 
 uint64_t  //
-buffer_limit(uint64_t hash_6_bits, uint64_t min, uint64_t max) {
+buffer_limit(uint64_t hash, uint64_t min, uint64_t max) {
+  hash &= 0x3F;
   uint64_t n;
-  if (hash_6_bits < 0x20) {
-    n = min + hash_6_bits;
+  if (hash < 0x20) {
+    n = min + hash;
   } else {
-    n = max - (0x3F - hash_6_bits);
+    n = max - (0x3F - hash);
   }
   if (n < min) {
     return min;
@@ -224,7 +225,7 @@
   return n;
 }
 
-void set_quirks(wuffs_json__decoder* dec, uint64_t hash_44_bits) {
+void set_quirks(wuffs_json__decoder* dec, uint64_t hash) {
   uint32_t quirks[] = {
       WUFFS_JSON__QUIRK_ALLOW_BACKSLASH_A,
       WUFFS_JSON__QUIRK_ALLOW_BACKSLASH_CAPITAL_U,
@@ -247,24 +248,23 @@
 
   uint32_t i;
   for (i = 0; quirks[i]; i++) {
-    uint64_t bit = 1 << (i % 44);
-    if (hash_44_bits & bit) {
+    uint64_t bit = 1 << (i & 63);
+    if (hash & bit) {
       wuffs_json__decoder__set_quirk_enabled(dec, quirks[i], true);
     }
   }
 }
 
 const char*  //
-fuzz_complex(wuffs_base__io_buffer* full_src, uint64_t hash_56_bits) {
+fuzz_complex(wuffs_base__io_buffer* full_src, uint64_t hash) {
   uint64_t tok_limit = buffer_limit(
-      hash_56_bits & 0x3F, WUFFS_JSON__DECODER_DST_TOKEN_BUFFER_LENGTH_MIN_INCL,
+      hash & 0x3F, WUFFS_JSON__DECODER_DST_TOKEN_BUFFER_LENGTH_MIN_INCL,
       TOK_BUFFER_ARRAY_SIZE);
-  uint64_t hash_50_bits = hash_56_bits >> 6;
+  hash = wuffs_base__u64__rotate_right(hash, 6);
 
-  uint64_t src_limit =
-      buffer_limit(hash_50_bits & 0x3F,
-                   WUFFS_JSON__DECODER_SRC_IO_BUFFER_LENGTH_MIN_INCL, 4096);
-  uint64_t hash_44_bits = hash_50_bits >> 6;
+  uint64_t src_limit = buffer_limit(
+      hash & 0x3F, WUFFS_JSON__DECODER_SRC_IO_BUFFER_LENGTH_MIN_INCL, 4096);
+  hash = wuffs_base__u64__rotate_right(hash, 6);
 
   // ----
 
@@ -275,7 +275,7 @@
   if (!wuffs_base__status__is_ok(&status)) {
     return wuffs_base__status__message(&status);
   }
-  set_quirks(&dec, hash_44_bits);
+  set_quirks(&dec, hash);
 
   wuffs_base__token tok_array[TOK_BUFFER_ARRAY_SIZE];
   wuffs_base__token_buffer tok = ((wuffs_base__token_buffer){
@@ -427,7 +427,7 @@
   // The fuzz_complex implementation adds many more Wuffs API specific checks
   // (e.g. that the sum of the tokens' lengths do not exceed the input length).
   if ((hash & 0xFF) != 0xA5) {
-    return fuzz_complex(full_src, hash >> 8);
+    return fuzz_complex(full_src, wuffs_base__u64__rotate_right(hash, 8));
   }
   return fuzz_simple(full_src);
 }
diff --git a/fuzz/c/std/png_fuzzer.c b/fuzz/c/std/png_fuzzer.c
index 1191fda..d6cc049 100644
--- a/fuzz/c/std/png_fuzzer.c
+++ b/fuzz/c/std/png_fuzzer.c
@@ -70,7 +70,7 @@
   wuffs_base__status status = wuffs_png__decoder__initialize(
       &dec, sizeof dec, WUFFS_VERSION,
       (hash & 1) ? WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED : 0);
-  hash >>= 1;
+  hash = wuffs_base__u64__rotate_right(hash, 1);
   if (!wuffs_base__status__is_ok(&status)) {
     return wuffs_base__status__message(&status);
   }
diff --git a/internal/cgen/base/fundamental-public.h b/internal/cgen/base/fundamental-public.h
index 7d4a0e7..67ada28 100644
--- a/internal/cgen/base/fundamental-public.h
+++ b/internal/cgen/base/fundamental-public.h
@@ -581,6 +581,56 @@
 
 // --------
 
+static inline uint8_t  //
+wuffs_base__u8__rotate_left(uint8_t x, uint32_t n) {
+  n &= 7;
+  return (x << n) | (x >> (8 - n));
+}
+
+static inline uint8_t  //
+wuffs_base__u8__rotate_right(uint8_t x, uint32_t n) {
+  n &= 7;
+  return (x >> n) | (x << (8 - n));
+}
+
+static inline uint16_t  //
+wuffs_base__u16__rotate_left(uint16_t x, uint32_t n) {
+  n &= 15;
+  return (x << n) | (x >> (16 - n));
+}
+
+static inline uint16_t  //
+wuffs_base__u16__rotate_right(uint16_t x, uint32_t n) {
+  n &= 15;
+  return (x >> n) | (x << (16 - n));
+}
+
+static inline uint32_t  //
+wuffs_base__u32__rotate_left(uint32_t x, uint32_t n) {
+  n &= 31;
+  return (x << n) | (x >> (32 - n));
+}
+
+static inline uint32_t  //
+wuffs_base__u32__rotate_right(uint32_t x, uint32_t n) {
+  n &= 31;
+  return (x >> n) | (x << (32 - n));
+}
+
+static inline uint64_t  //
+wuffs_base__u64__rotate_left(uint64_t x, uint32_t n) {
+  n &= 63;
+  return (x << n) | (x >> (64 - n));
+}
+
+static inline uint64_t  //
+wuffs_base__u64__rotate_right(uint64_t x, uint32_t n) {
+  n &= 63;
+  return (x >> n) | (x << (64 - n));
+}
+
+// --------
+
 // Saturating arithmetic (sat_add, sat_sub) branchless bit-twiddling algorithms
 // are per https://locklessinc.com/articles/sat_arithmetic/
 //
diff --git a/internal/cgen/data/data.go b/internal/cgen/data/data.go
index 2dc1bf7..16a3420 100644
--- a/internal/cgen/data/data.go
+++ b/internal/cgen/data/data.go
@@ -91,6 +91,9 @@
 	"int32_t  //\nwuffs_base__i32__max(int32_t x, int32_t y) {\n  return x > y ? x : y;\n}\n\nstatic inline int64_t  //\nwuffs_base__i64__min(int64_t x, int64_t y) {\n  return x < y ? x : y;\n}\n\nstatic inline int64_t  //\nwuffs_base__i64__max(int64_t x, int64_t y) {\n  return x > y ? x : y;\n}\n\nstatic inline uint8_t  //\nwuffs_base__u8__min(uint8_t x, uint8_t y) {\n  return x < y ? x : y;\n}\n\nstatic inline uint8_t  //\nwuffs_base__u8__max(uint8_t x, uint8_t y) {\n  return x > y ? x : y;\n}\n\nstatic inline uint16_t  //\nwuffs_base__u16__min(uint16_t x, uint16_t y) {\n  return x < y ? x : y;\n}\n\nstatic inline uint16_t  //\nwuffs_base__u16__max(uint16_t x, uint16_t y) {\n  return x > y ? x : y;\n}\n\nstatic inline uint32_t  //\nwuffs_base__u32__min(uint32_t x, uint32_t y) {\n  return x < y ? x : y;\n}\n\nstatic inline uint32_t  //\nwuffs_base__u32__max(uint32_t x, uint32_t y) {\n  return x > y ? x : y;\n}\n\nstatic inline uint64_t  //\nwuffs_base__u64__min(uint64_t x, uint64_t y) {\n  return x < y ? x : y;\n}\n\nstatic inline uint64_t  //\nwuffs_base__u64__m" +
 	"ax(uint64_t x, uint64_t y) {\n  return x > y ? x : y;\n}\n\n" +
 	"" +
+	"// --------\n\nstatic inline uint8_t  //\nwuffs_base__u8__rotate_left(uint8_t x, uint32_t n) {\n  n &= 7;\n  return (x << n) | (x >> (8 - n));\n}\n\nstatic inline uint8_t  //\nwuffs_base__u8__rotate_right(uint8_t x, uint32_t n) {\n  n &= 7;\n  return (x >> n) | (x << (8 - n));\n}\n\nstatic inline uint16_t  //\nwuffs_base__u16__rotate_left(uint16_t x, uint32_t n) {\n  n &= 15;\n  return (x << n) | (x >> (16 - n));\n}\n\nstatic inline uint16_t  //\nwuffs_base__u16__rotate_right(uint16_t x, uint32_t n) {\n  n &= 15;\n  return (x >> n) | (x << (16 - n));\n}\n\nstatic inline uint32_t  //\nwuffs_base__u32__rotate_left(uint32_t x, uint32_t n) {\n  n &= 31;\n  return (x << n) | (x >> (32 - n));\n}\n\nstatic inline uint32_t  //\nwuffs_base__u32__rotate_right(uint32_t x, uint32_t n) {\n  n &= 31;\n  return (x >> n) | (x << (32 - n));\n}\n\nstatic inline uint64_t  //\nwuffs_base__u64__rotate_left(uint64_t x, uint32_t n) {\n  n &= 63;\n  return (x << n) | (x >> (64 - n));\n}\n\nstatic inline uint64_t  //\nwuffs_base__u64__rotate_right(uint64_t x, uint32_t n) {\n  n " +
+	"&= 63;\n  return (x >> n) | (x << (64 - n));\n}\n\n" +
+	"" +
 	"// --------\n\n// Saturating arithmetic (sat_add, sat_sub) branchless bit-twiddling algorithms\n// are per https://locklessinc.com/articles/sat_arithmetic/\n//\n// It is important that the underlying types are unsigned integers, as signed\n// integer arithmetic overflow is undefined behavior in C.\n\nstatic inline uint8_t  //\nwuffs_base__u8__sat_add(uint8_t x, uint8_t y) {\n  uint8_t res = (uint8_t)(x + y);\n  res |= (uint8_t)(-(res < x));\n  return res;\n}\n\nstatic inline uint8_t  //\nwuffs_base__u8__sat_sub(uint8_t x, uint8_t y) {\n  uint8_t res = (uint8_t)(x - y);\n  res &= (uint8_t)(-(res <= x));\n  return res;\n}\n\nstatic inline uint16_t  //\nwuffs_base__u16__sat_add(uint16_t x, uint16_t y) {\n  uint16_t res = (uint16_t)(x + y);\n  res |= (uint16_t)(-(res < x));\n  return res;\n}\n\nstatic inline uint16_t  //\nwuffs_base__u16__sat_sub(uint16_t x, uint16_t y) {\n  uint16_t res = (uint16_t)(x - y);\n  res &= (uint16_t)(-(res <= x));\n  return res;\n}\n\nstatic inline uint32_t  //\nwuffs_base__u32__sat_add(uint32_t x, uint32_t y) {\n  uint32" +
 	"_t res = (uint32_t)(x + y);\n  res |= (uint32_t)(-(res < x));\n  return res;\n}\n\nstatic inline uint32_t  //\nwuffs_base__u32__sat_sub(uint32_t x, uint32_t y) {\n  uint32_t res = (uint32_t)(x - y);\n  res &= (uint32_t)(-(res <= x));\n  return res;\n}\n\nstatic inline uint64_t  //\nwuffs_base__u64__sat_add(uint64_t x, uint64_t y) {\n  uint64_t res = (uint64_t)(x + y);\n  res |= (uint64_t)(-(res < x));\n  return res;\n}\n\nstatic inline uint64_t  //\nwuffs_base__u64__sat_sub(uint64_t x, uint64_t y) {\n  uint64_t res = (uint64_t)(x - y);\n  res &= (uint64_t)(-(res <= x));\n  return res;\n}\n\n" +
 	"" +
diff --git a/release/c/wuffs-unsupported-snapshot.c b/release/c/wuffs-unsupported-snapshot.c
index 2989dcd..c3eddad 100644
--- a/release/c/wuffs-unsupported-snapshot.c
+++ b/release/c/wuffs-unsupported-snapshot.c
@@ -804,6 +804,56 @@
 
 // --------
 
+static inline uint8_t  //
+wuffs_base__u8__rotate_left(uint8_t x, uint32_t n) {
+  n &= 7;
+  return (x << n) | (x >> (8 - n));
+}
+
+static inline uint8_t  //
+wuffs_base__u8__rotate_right(uint8_t x, uint32_t n) {
+  n &= 7;
+  return (x >> n) | (x << (8 - n));
+}
+
+static inline uint16_t  //
+wuffs_base__u16__rotate_left(uint16_t x, uint32_t n) {
+  n &= 15;
+  return (x << n) | (x >> (16 - n));
+}
+
+static inline uint16_t  //
+wuffs_base__u16__rotate_right(uint16_t x, uint32_t n) {
+  n &= 15;
+  return (x >> n) | (x << (16 - n));
+}
+
+static inline uint32_t  //
+wuffs_base__u32__rotate_left(uint32_t x, uint32_t n) {
+  n &= 31;
+  return (x << n) | (x >> (32 - n));
+}
+
+static inline uint32_t  //
+wuffs_base__u32__rotate_right(uint32_t x, uint32_t n) {
+  n &= 31;
+  return (x >> n) | (x << (32 - n));
+}
+
+static inline uint64_t  //
+wuffs_base__u64__rotate_left(uint64_t x, uint32_t n) {
+  n &= 63;
+  return (x << n) | (x >> (64 - n));
+}
+
+static inline uint64_t  //
+wuffs_base__u64__rotate_right(uint64_t x, uint32_t n) {
+  n &= 63;
+  return (x >> n) | (x << (64 - n));
+}
+
+// --------
+
 // Saturating arithmetic (sat_add, sat_sub) branchless bit-twiddling algorithms
 // are per https://locklessinc.com/articles/sat_arithmetic/
 //