{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/corefx/issues/32269","repository_url":"https://api.github.com/repos/dotnet/corefx","labels_url":"https://api.github.com/repos/dotnet/corefx/issues/32269/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/corefx/issues/32269/comments","events_url":"https://api.github.com/repos/dotnet/corefx/issues/32269/events","html_url":"https://github.com/dotnet/corefx/issues/32269","id":359727922,"node_id":"MDU6SXNzdWUzNTk3Mjc5MjI=","number":32269,"title":"Proposal: Expose Bit Manipulation functions ","user":{"login":"grant-d","id":10214147,"node_id":"MDQ6VXNlcjEwMjE0MTQ3","avatar_url":"https://avatars1.githubusercontent.com/u/10214147?v=4","gravatar_id":"","url":"https://api.github.com/users/grant-d","html_url":"https://github.com/grant-d","followers_url":"https://api.github.com/users/grant-d/followers","following_url":"https://api.github.com/users/grant-d/following{/other_user}","gists_url":"https://api.github.com/users/grant-d/gists{/gist_id}","starred_url":"https://api.github.com/users/grant-d/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/grant-d/subscriptions","organizations_url":"https://api.github.com/users/grant-d/orgs","repos_url":"https://api.github.com/users/grant-d/repos","events_url":"https://api.github.com/users/grant-d/events{/privacy}","received_events_url":"https://api.github.com/users/grant-d/received_events","type":"User","site_admin":false},"labels":[{"id":165998434,"node_id":"MDU6TGFiZWwxNjU5OTg0MzQ=","url":"https://api.github.com/repos/dotnet/corefx/labels/api-ready-for-review","name":"api-ready-for-review","color":"5319e7","default":false},{"id":174276033,"node_id":"MDU6TGFiZWwxNzQyNzYwMzM=","url":"https://api.github.com/repos/dotnet/corefx/labels/area-System.Runtime","name":"area-System.Runtime","color":"d4c5f9","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":36,"created_at":"2018-09-13T03:21:37Z","updated_at":"2018-09-28T17:41:43Z","closed_at":null,"author_association":"COLLABORATOR","body":"[Picking up an item from `up-for-grabs`]\r\n\r\nBit manipulation routines are common enough that we should expose a subset as platform primitives. \r\nWhile some of them may be simple to write, it's much harder to achieve the requisite performance desired, especially since they tend to be used within tight loops.\r\n\r\nThe aim of this proposal is to scope & design a minimal set of functions, to be implemented with a bias towards correctness. Per @tannergooding \r\n> The point of these APIs is to provide a general-purpose API that works on all platforms (which means providing a software fallback) and is generally-usable. Hardware Intrinsics are for performance oriented scenarios where you require hardware acceleration and need more direct control of the code that is emitted.\r\n\r\n### Rationale and Usage\r\nThis is a nice-to-have, but the functionality is re-implemented many times over, often with suboptimal performance. Something like `ExtractBit` is trivial to implement, but `PopCount` is more complex and thus prone to logic and performance issues. Hiding these complex formulae behind friendly signatures makes using them more approachable.\r\n\r\nHere's an example of a simple function which negates a specific bit in a given `byte` value.\r\nFor such a simple concept, the algorithm may still be difficult to grasp for the uninitiated due to the performance tricks it uses. The problem is even greater in functions like [PopCount](https://github.com/grant-d/corefx/blob/85e53a352db5118fcc477854412f422d9b02e811/src/Common/src/System/BitOps.cs#L663).\r\n```csharp\r\n[MethodImpl(MethodImplOptions.AggressiveInlining)]\r\npublic static byte ComplementBit(byte value, int offset)\r\n{\r\n    // Algorithm avoids branching\r\n\r\n    var shft = offset & 7; // uint.shift is natively mod-32, but need explicit mod-8 for byte\r\n    var mask = 1U << shft;\r\n\r\n    // See Truth table\r\n    var val = (byte)~(~mask ^ value);\r\n    return val;\r\n}\r\n```\r\nHowever making a call to it meets our goal of abstraction and performance:\r\n```csharp\r\nbyte b = 40;\r\nb = BitOps.ComplementBit(b, 6);\r\n```\r\n\r\n### Background\r\nSee the original issue authored by @mburbea here: [Proposal: Add a BitManipulation class](https://github.com/dotnet/corefx/issues/12425)\r\nIt has contributions from a wide audience, which helped fine-tune the design below.\r\n\r\n### Proposed API\r\nThe proposed API is purposefully kept lean. We can add more methods in later design iterations. We should view this as an opportunity to get simple, base functionality out the door and not stray into the dangerous territory of adding every bit twiddling hack that exists.\r\n\r\nAssume all methods are decorated with `[MethodImpl(MethodImplOptions.AggressiveInlining)]`\r\n\r\n```csharp\r\npublic static class BitOps\r\n{\r\n    /// <summary>\r\n    /// Reads whether the specified bit in a mask is set.\r\n    /// </summary>\r\n    /// <param name=\"value\">The bit mask.</param>\r\n    /// <param name=\"bitOffset\">The ordinal position of the bit to read.</param>\r\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\r\n    bool ExtractBit(byte value, int bitOffset);\r\n    bool ExtractBit(ushort value, int bitOffset);\r\n    bool ExtractBit(uint value, int bitOffset);\r\n    bool ExtractBit(ulong value, int bitOffset);\r\n\r\n    /// <summary>\r\n    /// Sets the specified bit in a mask and returns the updated value.\r\n    /// </summary>\r\n    /// <param name=\"value\">The bit mask.</param>\r\n    /// <param name=\"bitOffset\">The ordinal position of the bit to set.</param>\r\n    byte InsertBit(byte value, int bitOffset);\r\n    ushort InsertBit(ushort value, int bitOffset);\r\n    uint InsertBit(uint value, int bitOffset);\r\n    ulong InsertBit(ulong value, int bitOffset);\r\n\r\n    /// <summary>\r\n    /// Sets the specified bit in a mask and returns whether the bit was originally set.\r\n    /// This mimics the behavior of hardware intrinsic BTS.\r\n    /// </summary>\r\n    /// <param name=\"value\">The bit mask.</param>\r\n    /// <param name=\"bitOffset\">The ordinal position of the bit to set.</param>\r\n    bool InsertBit(ref byte value, int bitOffset);\r\n    bool InsertBit(ref ushort value, int bitOffset);\r\n    bool InsertBit(ref uint value, int bitOffset);\r\n    bool InsertBit(ref ulong value, int bitOffset);\r\n\r\n    /// <summary>\r\n    /// Clears the specified bit in a mask and returns the updated value.\r\n    /// </summary>\r\n    /// <param name=\"value\">The bit mask.</param>\r\n    /// <param name=\"bitOffset\">The ordinal position of the bit to clear.</param>\r\n    byte ClearBit(byte value, int bitOffset);\r\n    ushort ClearBit(ushort value, int bitOffset);\r\n    uint ClearBit(uint value, int bitOffset);\r\n    ulong ClearBit(ulong value, int bitOffset);\r\n\r\n    /// <summary>\r\n    /// Clears the specified bit in a mask and returns whether the bit was originally set.\r\n    /// This mimics the behavior of hardware intrinsic BTR.\r\n    /// </summary>\r\n    /// <param name=\"value\">The bit mask.</param>\r\n    /// <param name=\"bitOffset\">The ordinal position of the bit to clear.</param>\r\n    bool ClearBit(ref byte value, int bitOffset);\r\n    bool ClearBit(ref ushort value, int bitOffset);\r\n    bool ClearBit(ref uint value, int bitOffset);\r\n    bool ClearBit(ref ulong value, int bitOffset);\r\n\r\n    /// <summary>\r\n    /// Complements the specified bit in a mask and returns the updated value.\r\n    /// </summary>\r\n    /// <param name=\"value\">The bit mask.</param>\r\n    /// <param name=\"bitOffset\">The ordinal position of the bit to complement.</param>\r\n    byte ComplementBit(byte value, int bitOffset);\r\n    ushort ComplementBit(ushort value, int bitOffset);\r\n    uint ComplementBit(uint value, int bitOffset);\r\n    ulong ComplementBit(ulong value, int bitOffset);\r\n\r\n    /// <summary>\r\n    /// Complements the specified bit in a mask and returns whether the bit was originally set.\r\n    /// This mimics the behavior of hardware intrinsic BTC.\r\n    /// </summary>\r\n    /// <param name=\"value\">The bit mask.</param>\r\n    /// <param name=\"bitOffset\">The ordinal position of the bit to complement.</param>\r\n    bool ComplementBit(ref byte value, int bitOffset);\r\n    bool ComplementBit(ref ushort value, int bitOffset);\r\n    bool ComplementBit(ref uint value, int bitOffset);\r\n    bool ComplementBit(ref ulong value, int bitOffset);\r\n\r\n    /// <summary>\r\n    /// Writes the specified bit in a mask and returns the new value.\r\n    /// </summary>\r\n    /// <param name=\"value\">The mask.</param>\r\n    /// <param name=\"bitOffset\">The ordinal position of the bit to clear.\r\n    /// <param name=\"on\"/>True to set the bit to 1, or false to set it to 0.</param>\r\n    byte WriteBit(byte value, int bitOffset, bool on);\r\n    ushort WriteBit(ushort value, int bitOffset, bool on);\r\n    uint WriteBit(uint value, int bitOffset, bool on);\r\n    ulong WriteBit(ulong value, int bitOffset, bool on);\r\n        // Implemented without branching\r\n\r\n    /// <summary>\r\n    /// Writes the specified bit in a mask and returns whether it was originally set.\r\n    /// </summary>\r\n    /// <param name=\"value\">The mask.</param>\r\n    /// <param name=\"bitOffset\">The ordinal position of the bit to clear.\r\n    /// <param name=\"on\"/>True to set the bit to 1, or false to set it to 0.</param>\r\n    bool WriteBit(ref byte value, int bitOffset, bool on);\r\n    bool WriteBit(ref ushort value, int bitOffset, bool on);\r\n    bool WriteBit(ref uint value, int bitOffset, bool on);\r\n    bool WriteBit(ref ulong value, int bitOffset, bool on);\r\n\r\n    /// <summary>\r\n    /// Rotates the specified mask left by the specified number of bits.\r\n    /// </summary>\r\n    /// <param name=\"value\">The value to rotate.</param>\r\n    /// <param name=\"bitOffset\">The number of bits to rotate by.</param>\r\n    byte RotateLeft(byte value, int bitOffset);\r\n    ushort RotateLeft(ushort value, int bitOffset);\r\n    uint RotateLeft(uint value, int bitOffset);\r\n    ulong RotateLeft(ulong value, int bitOffset);\r\n\r\n    /// <summary>\r\n    /// Rotates the specified mask right by the specified number of bits.\r\n    /// </summary>\r\n    /// <param name=\"value\">The value to rotate.</param>\r\n    /// <param name=\"bitOffset\">The number of bits to rotate by.</param>\r\n    byte RotateRight(byte value, int bitOffset);\r\n    ushort RotateRight(ushort value, int bitOffset);\r\n    uint RotateRight(uint value, int bitOffset);\r\n    ulong RotateRight(ulong value, int bitOffset);\r\n\r\n    /// <summary>\r\n    /// Returns the population count (number of bits set) in a mask.\r\n    /// </summary>\r\n    /// <param name=\"value\">The bit mask.</param>\r\n    int PopCount(uint value);\r\n    int PopCount(ulong value);\r\n        // byte and ushort overloads are redundant\r\n    \r\n    /// <summary>\r\n    /// Returns 1 if the bit count is odd, else 0.\r\n    /// Logically equivalent to PopCount mod 2.\r\n    /// </summary>\r\n    /// <param name=\"value\">The value.</param>\r\n    int Parity(uint value);\r\n    int Parity(ulong value);\r\n        // byte and ushort overloads are redundant\r\n\r\n    /// <summary>\r\n    /// Converts a bool to a byte value, without branching.\r\n    /// Returns <paramref name=\"trueValue\"/> if True, else returns <paramref name=\"falseValue\"/>.\r\n    /// </summary>\r\n    /// <param name=\"condition\">The value to convert.</param>\r\n    /// <param name=\"trueValue\">The value to return if True.</param>\r\n    /// <param name=\"falseValue\">The value to return if False.</param>\r\n    uint Evaluate(bool condition, uint trueValue, uint falseValue);\r\n    uint Evaluate(bool condition, uint trueValue);\r\n        // For brevity, int|long|ulong overloads not shown\r\n        // byte and ushort overloads are redundant\r\n        // Explicit overload faster due to implementation (vs uint falseValue=0)\r\n        // Implemented without branching\r\n        // Benchmark: 0.67 - 0.79 cost of idiomatic bool expression (condition ? 1 : 0)\r\n    \r\n    /// <summary>\r\n    /// Returns True if the value is a power of 2, else False.\r\n    /// </summary>\r\n    /// <param name=\"value\">The value.</param>\r\n    bool IsPowerOf2(uint value);\r\n    bool IsPowerOf2(ulong value);\r\n        // byte and ushort overloads are redundant\r\n\r\n    /// <summary>\r\n    /// Count the number of leading zero bits in a mask.\r\n    /// </summary>\r\n    /// <param name=\"value\">The mask.</param>\r\n    int LeadingZeros(byte value);\r\n        // For brevity, ushort|uint|ulong overloads not shown\r\n\r\n    /// <summary>\r\n    /// Count the number of leading one bits in a mask.\r\n    /// </summary>\r\n    /// <param name=\"value\">The mask.</param>\r\n    int LeadingOnes(byte value);\r\n        // For brevity, ushort|uint|ulong overloads not shown\r\n\r\n    /// <summary>\r\n    /// Count the number of trailing zero bits in a mask.\r\n    /// </summary>\r\n    /// <param name=\"value\">The mask.</param>\r\n    int TrailingZeros(byte value);\r\n        // For brevity, ushort|uint|ulong overloads not shown\r\n\r\n    /// <summary>\r\n    /// Count the number of trailing one bits in a mask.\r\n    /// </summary>\r\n    /// <param name=\"value\">The mask.</param>\r\n    int TrailingOnes(byte value);\r\n        // For brevity, ushort|uint|ulong overloads not shown\r\n\r\n    /// <summary>\r\n    /// Reads whether the specified bit in a mask is set.\r\n    /// </summary>\r\n    /// <param name=\"value\">The mask.</param>\r\n    /// <param name=\"offset\">The ordinal position of the bit to read.</param>\r\n    bool ExtractBit(ReadOnlySpan<byte> value, uint offset);\r\n        // For brevity, ushort|uint|ulong overloads not shown\r\n\r\n    /// <summary>\r\n    /// Sets the specified bit in a mask and returns whether the bit was originally set.\r\n    /// </summary>\r\n    /// <param name=\"value\">The mask.</param>\r\n    /// <param name=\"bitOffset\">The ordinal position of the bit to set.</param>\r\n    bool InsertBit(Span<byte> value, uint bitOffset);\r\n        // For brevity, ushort|uint|ulong overloads not shown\r\n\r\n    /// <summary>\r\n    /// Clears the specified bit in a mask and returns whether the bit was originally set.\r\n    /// </summary>\r\n    /// <param name=\"value\">The mask.</param>\r\n    /// <param name=\"bitOffset\">The ordinal position of the bit to clear.</param>\r\n    bool ClearBit(Span<byte> value, uint bitOffset);\r\n        // For brevity, ushort|uint|ulong overloads not shown\r\n\r\n    /// <summary>\r\n    /// Complements the specified bit in a mask and returns whether the bit was originally set.\r\n    /// </summary>\r\n    /// <param name=\"value\">The mask.</param>\r\n    /// <param name=\"bitOffset\">The ordinal position of the bit to complement.</param>\r\n    bool ComplementBit(Span<byte> value, uint bitOffset);\r\n        // For brevity, ushort|uint|ulong overloads not shown\r\n\r\n    /// <summary>\r\n    /// Reads the specified byte from a span, given the bit offset.\r\n    /// </summary>\r\n    /// <param name=\"span\">The span.</param>\r\n    /// <param name=\"bitOffset\">The ordinal position to read.</param>\r\n    byte ExtractByte(ReadOnlySpan<byte> span, int bitOffset);\r\n    byte ExtractByte(ReadOnlySpan<ushort> span, int bitOffset);\r\n    byte ExtractByte(ReadOnlySpan<uint> span, int bitOffset);\r\n    byte ExtractByte(ReadOnlySpan<ulong> span, int bitOffset);\r\n\r\n    ushort ExtractUInt16(ReadOnlySpan<byte> span, int bitOffset);\r\n        // For brevity, ushort|uint|ulong overloads not shown\r\n\r\n    uint ExtractUInt32(ReadOnlySpan<byte> span, int bitOffset);\r\n        // For brevity, ushort|uint|ulong overloads not shown\r\n\r\n    ulong ExtractUInt64(ReadOnlySpan<byte> span, int bitOffset);\r\n        // For brevity, ushort|uint|ulong overloads not shown\r\n\r\n    /// <summary>\r\n    /// Writes the specified value to a span, given the bit offset, and returns the original value.\r\n    /// </summary>\r\n    /// <param name=\"span\">The span.</param>\r\n    /// <param name=\"bitOffset\">The ordinal position to write.</param>\r\n    /// <param name=\"value\">The value to write.</param>\r\n    byte InsertByte(Span<byte> span, int bitOffset, byte value);\r\n    byte InsertByte(Span<ushort> span, int bitOffset, byte value);\r\n    byte InsertByte(Span<uint> span, int bitOffset, byte value);\r\n    byte InsertByte(Span<ulong> span, int bitOffset, byte value);\r\n\r\n    byte InsertUInt16(Span<byte> span, int bitOffset, ushort value);\r\n        // For brevity, ushort|uint|ulong overloads not shown\r\n\r\n    byte InsertUInt32(Span<byte> span, int bitOffset, uint value);\r\n        // For brevity, ushort|uint|ulong overloads not shown\r\n\r\n    byte InsertUInt64(Span<byte> span, int bitOffset, ulong value);\r\n        // For brevity, ushort|uint|ulong overloads not shown\r\n\r\n    // Alternative pattern that mitigates throwing OutOfRange exceptions\r\n    bool TryInsertByte(Span<byte> span, int bitOffset, byte value);\r\n    bool TryExtractByte(ReadOnlySpan<byte> span, int bitOffset, out byte value);\r\n}\r\n```\r\n\r\n### Details\r\n* Initial implementation will not be vectorized; this can be fixed in a later PR. **The initial focus will be on correctness** with a reasonable focus on performance.\r\n* It is not sufficient to only provide operators for `uint` and `ulong`. The data being manipulated may also be `byte` or `ushort`.\r\n* Support `signed` integers only via _explicit cast_ - ie the callsite would need to convert appropriately. \r\n```csharp\r\nint foo = 123;\r\nvar val = BitOps.ExtractBit((uint)foo, 6);\r\n```\r\n* Operations that return a count, such as `PopCount`, return an `int` for scalar operations and `long` for `ReadOnlySpan` since the cardinality is higher on the latter.\r\n* `BitConverter` has already been span-ified, and as such has an overlapping api surface. For example, `bool TryWriteBytes(Span<byte> destination, long value)` is similar to signatures here.\r\n* Support `ReadOnlySpan` for getters, and `Span` for mutators. Not sure if we need to explicitly support `Span` for getters too; I would hope not.\r\n* Somewhat of an overlap in functionality between `InsertBit`/`ClearBit` and `WriteBit` where the latter conditionally executes the equivalent of either former (using twiddling to do so without branching). But there's enough twiddling in `Write` to avoid any branching that it's maybe worth keeping both variants.\r\n* Since these functions are used in performance-sensitive applications, there has been debate on how much input checking (and `exception`s) we use. The current design tries to dispense with the requirement by sharpening input types, contractual assumptions (eg out-of-bounds `offset` will use `mod n` in some functions or result in a `no-op` in others) and specific design choices. See notes in example below. \r\n``` csharp\r\npublic static byte ComplementBit(byte value, int offset)\r\n{\r\n    // For perf, we want these functions to be as simple & concise as possible so \r\n    // that they can be inlined. So we try not to have any guard clauses\r\n    // Or we manipulate the input to ensure it is valid per below.\r\n    \r\n    var shft = offset & 7; // <-- mod 8\r\n    // This line is redundant in uint/ulong contexts, but instructive here.\r\n    // In this routine `offset % 8` will give the exact same result as `offset`.\r\n    // This helps avoid param guards.\r\n\r\n    var mask = 1U << shft;\r\n…\r\n}\r\n```\r\n\r\n### Decisions\r\n* No idea what `namespace` this should be in. **Resolved**: `namespace System`.\r\n* The class name of `BitOps` is self-documenting, terse (it might be specified frequently in a callsite, if not aliased with `using`) and both terms are well-known names or abbreviations.\r\n* We favor well-known names over the alternatives. For example, `PopCount` could be called `CountSetBits` but that's not the lingo used by twiddlers. \r\n* Likewise, method names should also be as concise as possible, for example, `LeadingZeroCount` may be more concisely described as `LeadingZeros`.\r\n\r\n### Questions\r\n* Decide on class name. Suggestions include `BitOps`, `Bits`, `BitUtility` and `BitOperations`. Alternatively, augment `BitConverter`.\r\n* Proposal targets `CoreFX`.  Do we rather want it in `CoreLib`?\r\n* Should we support the `TryRead/Write` pattern. Versus the current shape which is `Read/Write` but then has to `throw` for OutOfRange conditions. In other words, since this is a perf-sensitive feature, using `Try` helps avoid `exception`s.\r\n* Apparently VB cannot deal with non-CLS-compliant signatures. Not sure what to do about that (besides `[CLSCompliant(false)]`)\r\n* For Span overloads, is there a need for overloads that work atomically,  eg `LeadingZeros(new byte[50]{ }) == 400`. Versus overloads that index into a specific byte. **Suggestion: No**.\r\n* See conversation item regarding methods like `RotateLeft` being **ambiguous**.\r\n* The following style of method group may have redundancy, since the caller can convert the latter 3 to `Span<byte>` first?\r\n```csharp\r\n    byte ExtractByte(ReadOnlySpan<byte> span, int bitOffset);\r\n    // Redundant?\r\n    // Caller can use MemoryMarshal.AsBytes(span) first, though they then have\r\n    // to take care of the offset math themselves. Unless we provide an offset helper.\r\n    byte ExtractByte(ReadOnlySpan<ushort> span, int bitOffset);\r\n    byte ExtractByte(ReadOnlySpan<uint> span, int bitOffset);\r\n    byte ExtractByte(ReadOnlySpan<ulong> span, int bitOffset);\r\n```\r\n* Do we care about endianness (ie do we need BE and LE variants of relevant methods). The current proposal is only LE, and is shaped in such a way that we are not future-proof wrt supporting BE. Discussion proposes an alternative.\r\n\r\n\r\n### Scope\r\n**Not** included, but can be spec'd and implemented in a future iteration:\r\n\r\n* Signed overloads such as `PopCount(int value)` (callsite has trivial workaround)\r\n* Endianess and CLS-Compliance\r\n* Other primitives\r\n\r\n### POC\r\nUnits all pass. Here's the code (may have drifted slightly due to the spec being a moving target)\r\n\r\n[BitOps.cs](https://github.com/grant-d/corefx/blob/BitOps/src/Common/src/System/BitOps.cs)\r\n[BitOps.Span.cs](https://github.com/grant-d/corefx/blob/BitOps/src/Common/src/System/BitOps.Span.cs)\r\n\r\n### Updates\r\n* Initial proposal submitted\r\n* Methods such as `InsertBit` accepted a bool that determined whether it set or cleared the bit in question. Such methods have now been refactored in two, `InsertBit` and `ClearBit`.\r\n* Some name changes based on suggestions (eg `FlipBit` became `ComplementBit`).\r\n* TestAndSet methods have been refactored into simple scalar functions, for performance.\r\n* The offset parameter is `int` instead of `byte`. All POC units pass.\r\n* Added `WriteBit(value, offset, bool on)` overloads that conditionally set/clear the specified bit.\r\n* Added `Parity(value)` overloads. Not sure if they are needed in v1.\r\n* Added `IsPowerOf2(value)` overloads. Not sure if they are needed in v1.\r\n* Added `ExtractByte` and friends\r\n* Added `ToByte(bool)` "},"comment":{"url":"https://api.github.com/repos/dotnet/corefx/issues/comments/425512745","html_url":"https://github.com/dotnet/corefx/issues/32269#issuecomment-425512745","issue_url":"https://api.github.com/repos/dotnet/corefx/issues/32269","id":425512745,"node_id":"MDEyOklzc3VlQ29tbWVudDQyNTUxMjc0NQ==","user":{"login":"grant-d","id":10214147,"node_id":"MDQ6VXNlcjEwMjE0MTQ3","avatar_url":"https://avatars1.githubusercontent.com/u/10214147?v=4","gravatar_id":"","url":"https://api.github.com/users/grant-d","html_url":"https://github.com/grant-d","followers_url":"https://api.github.com/users/grant-d/followers","following_url":"https://api.github.com/users/grant-d/following{/other_user}","gists_url":"https://api.github.com/users/grant-d/gists{/gist_id}","starred_url":"https://api.github.com/users/grant-d/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/grant-d/subscriptions","organizations_url":"https://api.github.com/users/grant-d/orgs","repos_url":"https://api.github.com/users/grant-d/repos","events_url":"https://api.github.com/users/grant-d/events{/privacy}","received_events_url":"https://api.github.com/users/grant-d/received_events","type":"User","site_admin":false},"created_at":"2018-09-28T17:41:43Z","updated_at":"2018-09-28T17:41:43Z","author_association":"COLLABORATOR","body":"My concern (perhaps unwarranted, please correct me) is that when inserting an `int` into, say, a `Span<byte>` then LE/BE may be an issue?"}}
{"actor":{"display_login":"grant-d"}}