What is _swift_stdlib_getNormData

I'm using embedded Swift and it asked me to implement this runtime function. What is it and where can I get a list of the others, since they only seem to error when I happen to use them?

My code was working a few months ago but now the number of functions required appears to have increased.

Oh no, it's String Using `String` in Embedded Swift mode reports missing symbols when using Strings as keys to dictionaries · Issue #75678 · swiftlang/swift · GitHub

Are there any workarounds or do I have to keep using [CChar] :(

[CChar] is your best bet right now! cc @kubamracek for visibility

I see, thanks. Though now that I look at this issue, mine appears worse as I don't use any dictionaries, I just use String to draw text to the screen. Odd.

I used this scary hack (and the appropriate StringLiteral extensions):

public typealias String = CString
public typealias Character = CChar

Thankfully my String usage isn't complicated enough to need any String specific methods so it compiles.

If you compare strings at all either through == or < (and friends) or hash them in any way (it doesn't sound like you're doing this), then it will want to reference this symbol.

1 Like

Unless I'm missing something subtle this code doesn't compare strings in any way:

public struct TileFont<Source: Drawable> {
    public let inner: DrawableGrid<Source>
    public let map: @Sendable (Character) -> (x: Int, y: Int)?
    public let spacing: Int
    
    public init(
        source: Source,
        charWidth: Int,
        charHeight: Int,
        spacing: Int = 1,
        map: @escaping @Sendable (Character) -> (x: Int, y: Int)?
    ) {
        self.inner = source.grid(itemWidth: charWidth, itemHeight: charHeight)
        self.spacing = spacing
        self.map = map
    }
    
    public subscript(char: Character) -> DrawableSlice<Source>? {
        if let symbol = map(char) {
            return inner[symbol.x, symbol.y]
        } else {
            return nil
        }
    }
}

But, I am comparing Character

public struct TileFonts {
    public static let pico =
        TileFont(
            source: UnsafeTGAPointer(PICOFONT_TGA),
            charWidth: 3,
            charHeight: 5
        ) { char in switch char {
                case "0": (0, 0)
                case "1": (1, 0)
                case "2": (2, 0)
                case "3": (3, 0)
                case "4": (4, 0)
                case "5": (5, 0)
                case "6": (6, 0)
                case "7": (7, 0)
                case "8": (8, 0)
                case "9": (9, 0)

                ... and so on
1 Like

Character is just a String, so comparing that is comparing a string. What you most likely want here is to compare Unicode.Scalars or the raw byte instead. IIRC we have an ascii initializer on UInt8 if you decide to just care about the raw bytes:

switch char {
case UInt8(ascii: "0"): (0, 0)
... etc
}

that way you don't need to know the underlying ascii value (if you don't want to).

1 Like

I'm mapping characters to their position on a sprite sheet, so all I want to do is return the position for supported characters and nil otherwise. I'm not entirely sure if these are equivalent as I don't know much about unicode

Does this mean all my Character instances are actually just strings and get dynamically allocated..?

It really depends how the source of these "characters" is representing these things. If it's just a pointer to ASCII bytes, then the UInt8(ascii:) is a good choice to switch over per byte in this buffer, but if it's pointing to a valid UTF-8 buffer then you'd need to decode these scalars and compare against Unicode.Scalar instead. (or I suppose it could be pointing at a UTF-16 buffer, etc.)

String has an optimization to stuff 15 UTF-8 bytes inline so that it doesn't always need to do an allocation (at least on 64 bit systems). I don't remember the exact threshold for the 32 bit layout, but it has the same optimization nonetheless to a different degree. So if these strings/Character's were only a byte to begin with then there was never an allocation.

2 Likes

I found that I actually need string interpolation, and that won't work with [CChar]. What does _swift_stdlib_getNormData do? I found the signature so I could just implement it for now instead of changing all my code to avoid comparing characters

And now it needs memcpy... Again, is there a list of all functions required? It's tedious because they get reported one at a time

Is the bug that prevents implementing functions like memcpy as cdecl fixed yet?

Okay it gets worse. I left the awful type aliases but added this overload to still have string interpolation

extension MutableDrawable {
    mutating func text(
        _ string: Swift.String,
        x: Int, y: Int,
        color: Color = .white,
        font: TileFont<some Drawable> = TileFonts.pico
    ) {
        string.withCString { ptr in
            text(
                Array(UnsafeBufferPointer(start: ptr, count: string.count)),
                x: x, y: y, color: color, font: font
            )
        }
    }
}

But now withCString requires _swift_stdlib_getGraphemeBreakProperty :frowning:


after doing this:

        var uselessCopy = string
        uselessCopy.withUTF8 { ptr in
            ptr.withMemoryRebound(to: Int8.self) { ptr in
                text(
                    Array(ptr),
                    x: x, y: y, color: color, font: font
                )
            }
        }

The code compiles, but the useless copy is not optimized away as it asked me for memmove now.

Why is withUTF8 marked mutating when it provides a const pointer same as withCString?
is there a way to unsafely access the bytes without a mutating method that potentially reallocates the string

You can find them here: swift/docs/EmbeddedSwift/UserManual.md at main · swiftlang/swift · GitHub

2 Likes

This doesn't seem accurate, it suddenly started asking me for round which is not mentioned in the manual

That seems to be llvm defaulting to this function for builtins instead of using wasm rounding instructions though.