question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Proposal: Fixed-size buffers enhancements

See original GitHub issue

Background

Interop with code implemented with C and C++ often involves fixed-size buffers. For multiple releases C# has supported the creation of fixed-size arrays on the stack, using the ‘stackalloc’ keyword:

static unsafe void SomeMethod()
{
    int* block = stackalloc int[100];   // array of 100 integers on the stack
    …
}

It’s also supported the creation of fixed-size arrays embedded within structures, using the ‘fixed’ keyword:

internal unsafe struct MyBuffer
{
    public fixed char FixedBuffer[128]; // array of 128 chars in the struct
}

Problem

In both of these cases, however, the creation of the fixed-size array may only happen in an unsafe context, hence the usage of the ‘unsafe’ keyword on the method and struct, respectively, in each of these examples. Further, in the case of fixed-size buffers in structs, the type can only be of a limited set: bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float or double.

As C#'s reach is extended and interop becomes even more common-place, it’s desirable to be able to work with fixed-size arrays, in particular embedded in structures, while still getting the benefits of safe code, such as bounds checking.

Solution

If the language supports ref returns (#118), C# can support this without further syntax, with simply relaxing the existing constraints on fixed-size buffers and changing the code generated for these types. For example, consider the following type:

internal struct SYSTEM_POWER_LEVEL
{
    public byte Enable;
    public fixed byte Spare[3]; // inline array, as in C
    public uint BatteryLevel;
    public POWER_ACTION_POLICY PowerPolicy;
    public SYSTEM_POWER_STATE MinSystemState;
}

Exactly how this is implemented is up to the compiler, but logically the support could be thought of as being implemented as an anonymous struct with a field for every element and an indexer for accessing them:

internal struct SYSTEM_POWER_LEVEL
{
    public byte Enable;
    public <>s__ArrayStruct1 Spare;
    public uint BatteryLevel;
    public POWER_ACTION_POLICY PowerPolicy;
    public SYSTEM_POWER_STATE MinSystemState;
}

internal struct <>s__ArrayStruct1
{
    public byte Item0, Item1, Item2;
    public ref byte this[int index]
    {
        get {
            switch(index) {
                case 0: return ref Item0;
                case 1: return ref Item1;
                case 2: return ref Item2;
                default: Environment.FailFast();
            }
        }
    }
}

In the past, developers have resorted to hand-crafting such a solution, implementing helper types like this manually. Instead, the compiler would be capable of implementing all of that boilerplate for the developer in as efficient a manner as possible. (If there were concerns around the efficiency of the new code generation, it could potentially only be used when using a type that was previously unsupported or when using a fixed-size buffer in a safe context. Or alternatively new syntax for expressing this new form could be devised.)

Issue Analytics

  • State:closed
  • Created 9 years ago
  • Reactions:15
  • Comments:21 (10 by maintainers)

github_iconTop GitHub Comments

2reactions
stephentoubcommented, Jan 29, 2015

To me the very concept of trying to directly manipulate memory for interop purposes is inherently “unsafe”, even if you manage to avoid the keyword

At some level most code one writes in C# is doing interop; the question is how far down you can push and isolate the unsafeness. Yes, with this proposal I can still mess up my DllImport signature and cause problems, but if I get the DllImport correct, then the resulting struct can be used in safe code, with bounds-checked accesses to its contained fixed-size arrays, etc. In contrast, today even if I get the DllImport correct, I’m still required to use unsafe code to access the resulting struct, and I’m still able to cause serious problems when I use the resulting struct because I really am just indexing into whatever memory I want, without bounds checks, etc.

1reaction
MadsTorgersencommented, Aug 15, 2016

This feels like something that should be done as a CLR change. The code gen proposed is worrisome in its complexity.

Instead the CLR could allow fixed buffers with bounds checking, and the compiler could then target that.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Low level struct improvements - C# 11.0 draft feature ...
This proposal is an aggregation of several different proposals for struct performance improvements: ref fields and the ability to override ...
Read more >
Fixed size double-ended queue - c++
I require hundreds of equal, small sized buffers in my current project. The bulk of the computation in the program operates on data...
Read more >
Buffer Sizing for 802.11 Based Networks
We demonstrate that the use of fixed size buffers in 802.11 networks inevitably leads to either undesirable channel under-utilization or unnecessary high delays ......
Read more >
Longs Peak Update: Buffer Object Improvements
This enhancement to the lpMapBuffer path allows more efficient partial edits to a buffer object even when the CPU is sourcing the data...
Read more >
Buffer Sizing for 802.11 Based Networks
We demonstrate that the use of fixed size buffers in 802.11 networks inevitably leads to either undesirable channel under-utilization or ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found