4

I have an array of shorts:

short[] data;

And I have a function that writes bytes to a file:

void Write(byte[] data);

I do not control this function and cannot change it. Is there a way to write my array of shorts without making a redundant copy first to convert it to bytes?

Something like that: Write((byte[])data);

I do not care about endianness. I want memory representation of shorts written to a file in whatever the machine representation of short is. I understand this kind of cast cannot work for any non-POD type that contains references, but shorts should be perfectly convertible. The cast should result in a byte array twice the size that points to the same memory.

If this is impossible in C#, is there anything in CLR that makes this impossible, or is it just C# limitation?

20
  • 2
    There's nothing 'redundant' about something your code depends on later on. What is the issue with making a copy, out of curiosity? Commented Nov 28, 2016 at 13:34
  • 9
    A short is two byte, a byte is ... one byte. How do you intend to convert that? Commented Nov 28, 2016 at 13:35
  • 2
    Array.ConvertAll(array, item => (byte)item) is as optimal as you're going to get. This ensures that the array is iterated over only once. Let the compiler deal with the performance implications. If you cared about this kind of low level stuff, you wouldn't be writing in C#. Commented Nov 28, 2016 at 13:37
  • 1
    Then the performance implications of using C# are unacceptable. This is how it works. And no, of course the copy will not be bit-identical to what you have. Arrays are first-class types in the CLR, and they are type-aware. It'll have a different length, for starters. This isn't C. You aren't just passing a pointer to the first element. If you wanted to write the kind of low level code that you're describing (I don't blame you; I think this way, too), then you shouldn't have chosen C#. The type system "straightjackets" you to provide safety: it is all by design. You just don't like the design. Commented Nov 28, 2016 at 13:56
  • 1
    You are trying to make C# into a different language than it is trying to be. Arrays are not POD types. They are objects, implicitly inheriting from System.Array. They are first-class types in the CLR, and nothing in C# is going to change that. Commented Nov 28, 2016 at 14:19

3 Answers 3

5

I do not care about endianness. I want memory representation of shorts written to a file in whatever the machine representation of short is.

This is the first impossible thing - endianness changes the memory representation, so reading from successive byte addresses starting at the address of the first short in the array will result in different byte patterns depending on the machine endianness.

The second impossible thing is that arrays in the CLR have type and length information encoded with the data. You cannot change this header information, or else you would break the garbage collector. So given a short[] array, you cannot convert it to a byte[] array. You might get to a byte pointer using C++ clr or unsafe code, but you still won't get to a CLR array.

If you really cannot control the code which takes the byte array, you might be able to change the code manipulating the shorts. Using a MemoryStream over the byte array would allow you to read and write data to it, you could wrap the array as an IList<short>, or you could just create accessor extension functions to get the data as shorts.

public sealed class ShortList :IList<short>
{
    private readonly byte[] _array;

    public short this[int index]
    {
        get { return (short)_array[index/2]<<8 | _array[index/2+1] ; }
    }

    public int Count
    {
        get { return _array.Length/2; }
    }

    ... many more methods in IList
Sign up to request clarification or add additional context in comments.

3 Comments

Marking this as answer. I didn't realize array length and type are stored along with the data in CLR. If so, it's impossible in C# or any CLR-based language to have 2 Array objects of different lengths and types pointing to the same data.
"I do not care about endianness" means that I do not care in what order the bytes will be written. The data will only ever be read by the same device that wrote it. Plus I actually know all my platforms are little endian anyway.
@kaalus it's impossible in C# or any CLR-based language to have 2 Array objects of different lengths and types pointing to the same data. It's not, actually. .NET supports array covariance, it just doesn't support it for value types, only reference types. Well, they'd be the same length; treating one reference type array as another reference type would never result in it having a different length, but the (compile time) types would be different.
1

There is way that's very low-level, you have to change the bytes in memory that tell the CLR that it's a short[] and replace them with some bytes that tell him it's a byte[]. After you have to adjust the array size and create a pointer of the right type to have the compiler support.

Have a look here http://blogs.ugidotnet.org/leonardo/archive/2023/11/24/water-in-wine-csharp-array-cast.aspx

Comments

-3

What about

Write(data.SelectMany(x => BitConverter.GetBytes(x)).ToArray());

4 Comments

This makes a copy of the array, which the OP indicated he doesn't want to do.
Your answer makes two, if not three copies of the data. The array is hundreds of megabytes. Code runs on a mobile phone.
@kaalus It only makes one copy of the data, no more. If you use a write operation that doesn't require all of the data to be in a materialized array, and instead use one that takes a stream or an IEnumerable, you can perform an operation like this with O(1) additional memory.
copy 1 - BitConverter.GetBytes - copies data from each short into its own short-lived array; copies 2 and a half - ToArray() on an IEnumerable which is not backed by an ICollection ( which is the case as SelectMany uses yield ) will grow an array in the Buffer from 4 to the required size increasing powers of two, so will copy about half the data twice on average.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.