September 17, 2021

On Thursday, 16 September 2021 at 18:02:44 UTC, Steven Schveighoffer wrote:

>

Are you sure? Be very pedantic about what C functions do with the data you send it. Sometimes they store it somewhere to use later. Sometimes they expect it to be allocated by the C heap, etc.

Without seeing how you use it, I can't tell you if it's wrong or not.

If you want to have a look the original C-library is here
https://github.com/rdoeffinger/iec16022

I'm only using the encoder function iec16022ecc200f.

>

If it's a literal, you don't need to toStringz (which also allocates). All string literals are zero-terminated (and actually implicitly castable to immutable char *).

-Steve

Thanks, I'm just careful with casting.
Does it really allocate from a literal if it's used on the stack only? Is -vgc switch reliable?

September 17, 2021

On Friday, 17 September 2021 at 06:27:40 UTC, frame wrote:

>

Thanks, I'm just careful with casting.
Does it really allocate from a literal if it's used on the stack only? Is -vgc switch reliable?

looks to me like it calls

// object
private U[] _dup(T, U)(scope T[] a) pure nothrow @trusted if (__traits(isPOD, T))
{
    if (__ctfe)
        return _dupCtfe!(T, U)(a);

    import core.stdc.string : memcpy;
    auto arr = _d_newarrayU(typeid(T[]), a.length);
    memcpy(arr.ptr, cast(const(void)*) a.ptr, T.sizeof * a.length);
    return *cast(U[]*) &arr;
}

->

// rt.lifetime
extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow @weak
{
...
    auto info = __arrayAlloc(size, ti, tinext);
...
}

->

// rt.lifetime
BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure
{
...
    auto bi = GC.qalloc(padded_size, attr, tinext);
...
}

->

// gc.impl.conservative.gc
    BlkInfo qalloc( size_t size, uint bits, const TypeInfo ti) nothrow
    {

        if (!size)
        {
            return BlkInfo.init;
        }

        BlkInfo retval;

        retval.base = runLocked!(mallocNoSync, mallocTime, numMallocs)(size, bits, retval.size, ti);

        if (!(bits & BlkAttr.NO_SCAN))
        {
            memset(retval.base + size, 0, retval.size - size);
        }

        retval.attr = bits;
        return retval;
    }

which you can also follow in an objdump. Conclusion: -vgc is missing this GC allocation.

To stack-allocate a mutable copy of a string literal go with

@safe @nogc nothrow unittest {
    enum S = "hello world";
    char[S.length+1] s1 = S;
    char* s2 = &s1[0];

    // Normally you'd expect s1.ptr[s1.length] here,
    // but the '\0' byte is explicitly part of s1 due to length+1 above.
    assert(s1[s1.length-1] == '\0');

    (() @trusted {
        import core.stdc.stdio : puts;
        import std.string : fromStringz;

        puts(s2);
        assert(s2.fromStringz == S);
    })();
}
September 17, 2021

On Friday, 17 September 2021 at 06:58:01 UTC, jfondren wrote:

>

On Friday, 17 September 2021 at 06:27:40 UTC, frame wrote:

>

Thanks, I'm just careful with casting.
Does it really allocate from a literal if it's used on the stack only? Is -vgc switch reliable?

looks to me like it calls
...

Interesting analysis, thanks for your effort!

September 17, 2021

On 9/17/21 2:27 AM, frame wrote:

>

On Thursday, 16 September 2021 at 18:02:44 UTC, Steven Schveighoffer wrote:

>

Are you sure? Be very pedantic about what C functions do with the data you send it. Sometimes they store it somewhere to use later. Sometimes they expect it to be allocated by the C heap, etc.

Without seeing how you use it, I can't tell you if it's wrong or not.

If you want to have a look the original C-library is here
https://github.com/rdoeffinger/iec16022

I'm only using the encoder function iec16022ecc200f.

Looking at that signature, it does not appear that it uses zero-termination at all, as it takes a length. So using dup and therefore the gc is totally unnecessary.

I'm assuming that string is the barcode argument?

What does your call look like?

> >

If it's a literal, you don't need to toStringz (which also allocates). All string literals are zero-terminated (and actually implicitly castable to immutable char *).

Thanks, I'm just careful with casting.
Does it really allocate from a literal if it's used on the stack only? Is -vgc switch reliable?

The -vgc switch appears to only identify allocations that the compiler invokes via hooks, not ones that other functions invoke (or ones that are direct calls into the GC).

In other words, it helps you find your direct allocations using the compiler, not ones that are buried in already-compiled code.

Try this with -vgc, and it reports nothing:

import core.memory;
void main()
{
   auto x = GC.malloc(10);
}

This makes sense, as it may not have the function code to analyze, and it also cannot infer GC allocation from a lack of @nogc (a non-@nogc function may allocate, but does not necessarily allocate). It also would be quite useless to see some function buried inside druntime that allocates, not knowing what the call stack was.

The docs for -vgc should really be updated to clarify. It currently just says "List all gc allocations including hidden ones".

-Steve

September 18, 2021

On Friday, 17 September 2021 at 14:29:23 UTC, Steven Schveighoffer wrote:

>

Looking at that signature, it does not appear that it uses zero-termination at all, as it takes a length. So using dup and therefore the gc is totally unnecessary.

I'm assuming that string is the barcode argument?

No, the string appears inside the C-function. I'm calling the function with .ptr and the .length property as it expects.

>

The -vgc switch appears to only identify allocations that the compiler invokes via hooks, not ones that other functions invoke (or ones that are direct calls into the GC).
...

The docs for -vgc should really be updated to clarify. It currently just says "List all gc allocations including hidden ones".

-Steve

Thanks for clarification!

September 18, 2021

On 9/18/21 5:40 AM, frame wrote:

>

On Friday, 17 September 2021 at 14:29:23 UTC, Steven Schveighoffer wrote:

>

Looking at that signature, it does not appear that it uses zero-termination at all, as it takes a length. So using dup and therefore the gc is totally unnecessary.

I'm assuming that string is the barcode argument?

No, the string appears inside the C-function. I'm calling the function with .ptr and the .length property as it expects.

Oh wow, I totally misunderstood what you are doing. I thought you were calling that function, but you are reimplementing that code in D.

Have you tried:

const(char)* s2 = "...";

This will work because string literals are zero terminated and implicitly castable to immutable(char)*, which will also implicitly cast to const(char)*.

That should allow s2 to be reassigned but not modify the data. IIRC, string literal data even in C is put into a read-only section by many compilers, so the code shouldn't be changing it.

Now that I understand what you are doing, it becomes clear that this isn't a situation of C code being called. Or are you calling other parts of the C library with that translated function?

The first rule of porting -- just translate, don't change anything. I would try to do exactly what C does without using the GC at all. Continue to use malloc/free. If you have issues with type representation, you may need to adjust for that.

-Steve

September 18, 2021

On Saturday, 18 September 2021 at 11:47:52 UTC, Steven Schveighoffer wrote:

>

Have you tried:

const(char)* s2 = "...";

This will work because string literals are zero terminated and implicitly castable to immutable(char)*, which will also implicitly cast to const(char)*.

That should allow s2 to be reassigned but not modify the data. IIRC, string literal data even in C is put into a read-only section by many compilers, so the code shouldn't be changing it.
...

The first rule of porting -- just translate, don't change anything. I would try to do exactly what C does without using the GC at all. Continue to use malloc/free. If you have issues with type representation, you may need to adjust for that.

This is what I try to achieve - not to change much.
But I see there is a mistake by me, s2 is const in the original C-code too.

Unfortunately, with const(char)*, strchr() did complain and then I would have to cast it to char* - so I didn't used it in first place because I really thought .dup wouldn't allocate here.

There were also parts where the pointer is used in calculations - which is accepted by the compiler - it just complains about implicitly long to char* cast:

// const char *e
// char *w
out[p++] = ((w - e) + 3) % 40;

Why doesn't the compiler complain about
char* - const(char)*?

September 18, 2021

On 9/18/21 12:52 PM, frame wrote:

>

On Saturday, 18 September 2021 at 11:47:52 UTC, Steven Schveighoffer wrote:

>

Have you tried:

const(char)* s2 = "...";

This will work because string literals are zero terminated and implicitly castable to immutable(char)*, which will also implicitly cast to const(char)*.

That should allow s2 to be reassigned but not modify the data. IIRC, string literal data even in C is put into a read-only section by many compilers, so the code shouldn't be changing it.
...

The first rule of porting -- just translate, don't change anything. I would try to do exactly what C does without using the GC at all. Continue to use malloc/free. If you have issues with type representation, you may need to adjust for that.

This is what I try to achieve - not to change much.
But I see there is a mistake by me, s2 is const in the original C-code too.

Unfortunately, with const(char)*, strchr() did complain and then I would have to cast it to char* - so I didn't used it in first place because I really thought .dup wouldn't allocate here.

dup definitely allocates. But when I look at the dlang docs for strchr, it properly adds the inout type modifier which should correct that problem. Are you defining the prototype for strchr yourself instead of importing it from core.stdc.string?

>

There were also parts where the pointer is used in calculations - which is accepted by the compiler - it just complains about implicitly long to char* cast:

// const char *e
// char *w
out[p++] = ((w - e) + 3) % 40;

Why doesn't the compiler complain about
char* - const(char)*?
long doesn't implicitly cast to char *. But of course, subtracting two pointers works for the same base type.

Note that long in C is not the same as long in D.

You should take a look at https://dlang.org/spec/interfaceToC.html#data_type_compat

-Steve

September 18, 2021

On 9/18/21 12:52 PM, frame wrote:

>

There were also parts where the pointer is used in calculations - which is accepted by the compiler - it just complains about implicitly long to char* cast:

// const char *e
// char *w
out[p++] = ((w - e) + 3) % 40;

Did you mean "long to char" cast? In that case, yes, you have to cast it.

Note, out is a keyword, it can't be used as a variable, but you probably already figured that out. But if out here is a char *, then yes, you need a cast there.

-Steve

September 18, 2021

On Saturday, 18 September 2021 at 18:48:07 UTC, Steven Schveighoffer wrote:

>

Are you defining the prototype for strchr yourself instead of importing it from core.stdc.string?

Not really :D but without cast it complains:

Error: cannot implicitly convert expression strchr(e, cast(int)c) of type const(char)* to char*
>

Did you mean "long to char" cast? In that case, yes, you have to cast it.

Note, out is a keyword, it can't be used as a variable, but you probably already figured that out. But if out here is a char *, then yes, you need a cast there.

-Steve

Yes, of course. out(put) is a char[6] here.