homedark

Comparing Strings as Integers with @bitCast

Feb 20, 2025

In the last blog posts, we looked at different ways to compare strings in Zig. A few posts back, we introduced Zig's @bitCast. As a quick recap, @bitCast lets us force a specific type onto a value. For example, the following prints 1067282596:

const std = @import("std");
pub fn main() !void {
    const f: f32 = 1.23;
    const n: u32 = @bitCast(f);
    std.debug.print("{d}\n", .{n});
}

What's happening here is that Zig represents the 32-bit float value of 1.23 as: [4]u8{164, 112, 157, 63}. This is also how Zig represents the 32-bit unsigned integer value of 1067282596. Data is just bytes; it's the type system - the compiler's knowledge of what data is what type - that controls what and how that data is manipulated.

It might seem like there's something special about bitcasting from a float to an integer; they're both numbers after all. But you can @bitCast from any two equivalently sized types. Can you guess what this prints?:

const std = @import("std");
pub fn main() !void {
    const data = [_]u8{3, 0, 0, 0};
    const x: i32 = @bitCast(data);
    std.debug.print("{d}\n", .{x});
}

The answer is 3. Think about the above snippet a bit more. We're taking an array of bytes and telling the compiler to treat it like an integer. If we made data equal to [_]u8{'b', 'l', 'u', 'e'}, it would still work (and print 1702194274). We're slowly heading towards being able to compare strings as-if they were integers.

From the last post, we could use multiple std.mem.eql or, more simply, std.meta.stringToEnum to complete the following method:

fn parseMethod(value: []const u8) ?Method {
    // ...
}

const Method = enum {
    get,
    put,
    post,
    head,
};

We can also use @bitCast. Let's take it step-by-step.

The first thing we'll need to do is switch on value.len. This is necessary because the three-byte "GET" will need to be @bitCast to a u24, whereas the four-byte "POST" needs to be @bitCast to a u32:

fn parseMethod(value: []const u8) ?Method {
    switch (value.len) {
        3 => switch (@as(u24, @bitCast(value[0..3]))) {
            // TODO
            else => {},
        },
        4 => switch (@as(u32, @bitCast(value[0..4]))) {
            // TODO
            else => {},
        },
        else => {},
    }

    return null;
}

If you try to run this code, you'll get a compilation error: cannot @bitCast from '*const [3]u8'. @bitCast works on actual bits, but when we slice our []const u8 with a compile-time known range ([0..3]), we get a pointer to an array. We can't @bitCast a pointer, we can only @bitCast actual bits of data. For this to work, we need to derefence the pointer, i.e. use: value[0..3].*. This will turn our *const [3]u8 into a const [3]u8.

fn parseMethod(value: []const u8) ?Method {
    switch (value.len) {
        // changed: we now derefernce the value (.*)
        3 => switch (@as(u24, @bitCast(value[0..3].*))) {
            // TODO
            else => {},
        },
        // changed: we now dereference the value (.*)
        4 => switch (@as(u32, @bitCast(value[0..4].*))) {
            // TODO
            else => {},
        },
        else => {},
    }

    return null;
}

Also, you might have noticed the @as(u24, ...) and @as(u32, ...). @bitCast, like most of Zig's builtin functions, infers its return type. When we're assiging the result of a @bitCast to a variable of a known type, i.e: const x: i32 = @bitCast(data);, the return type of i32 is inferred. In the above switch, we aren't assigning the result to a varible. We have to use @as(u24, ...) in order for @bitCast to kknow what it should be casting to (i.e. what its return type should be).

The last thing we need to do is fill our switch blocks. Hopefully it's obvious that we can't just do:

3 => switch (@as(u24, @bitCast(value[0..3].*))) {
    "GET" => return .get,
    "PUT" => return .put,
    else => {},
},
...

But you might be thinking that, while ugly, something like this might work:

3 => switch (@as(u24, @bitCast(value[0..3].*))) {
    @as(u24, @bitCast("GET".*)) => return .get,
    @as(u24, @bitCast("PUT".*)) => return .put,
    else => {},
},
...

Because "GET" and "PUT" are string literals, they're null terminated and of type *const [3:0]u8. When we dereference them, we get a const [3:0]u8. It's close, but it means that the value is 4 bytes ([4]u8{'G', 'E', 'T', 0}) and thus cannot be @bitCast into a u24. This is ugly, but it works:

fn parseMethod(value: []const u8) ?Method {
    switch (value.len) {
        3 => switch (@as(u24, @bitCast(value[0..3].*))) {
            @as(u24, @bitCast(@as([]const u8, "GET")[0..3].*)) => return .get,
            @as(u24, @bitCast(@as([]const u8, "PUT")[0..3].*)) => return .put,
            else => {},
        },
        4 => switch (@as(u32, @bitCast(value[0..4].*))) {
            @as(u32, @bitCast(@as([]const u8, "HEAD")[0..4].*)) => return .head,
            @as(u32, @bitCast(@as([]const u8, "POST")[0..4].*)) => return .post,
            else => {},
        },
        else => {},
    }
    return null;
}

That's a mouthful, so we can add small function to help:

fn parseMethod(value: []const u8) ?Method {
    switch (value.len) {
        3 => switch (@as(u24, @bitCast(value[0..3].*))) {
            asUint(u24, "GET") => return .get,
            asUint(u24, "PUT") => return .put,
            else => {},
        },
        4 => switch (@as(u32, @bitCast(value[0..4].*))) {
            asUint(u32, "HEAD") => return .head,
            asUint(u32, "POST") => return .post,
            else => {},
        },
        else => {},
    }
    return null;
}

pub fn asUint(comptime T: type, comptime string: []const u8) T {
    return @bitCast(string[0..string.len].*);
}

Like the verbose version, the trick is to cast our null-terminated string literal into a string slice, []const u8. By passing it through the asUint function, we get this without needing to add the explicit @as([]const u8).

There is a more advanced version of asUint which doesn't take the uint type parameter (T). If you think about it, the uint type can be inferred from the string's length:

pub fn asUint(comptime string: []const u8) @Type(.{
    .int = .{
        // bits, not bytes, hence * 8
        .bits = string.len * 8,
        .signedness = .unsigned,
    },
}) {
    return @bitCast(string[0..string.len].*);
}

Which allows us to call it with a single parameter: asUint("GET"). This might be your first time seeing such a return type. The @Type builtin is the opposite of @typeInfo. The latter takes a type and returns information on it in the shape of a std.builtin.Type union. Whereas @Type takes the std.builtin.Type and returns an actual usable type. One of these days I'll find the courage to blog about std.builtin.Type!

As a final note, some people dislike the look of this sort of return type and rather encapsulate the logic in its own function. This is the same:

pub fn asUint(comptime string: []const u8) AsUintReturn(string) {
    return @bitCast(string[0..string.len].*);
}

// Remember that, in Zig, by convention, a function should be
// PascalCase if it returns a type (because types are PascalCase).
fn AsUintReturn(comptime string: []const u8) type {
    return @Type(.{
        .int = .{
            // bits, not bytes, hence * 8
            .bits = string.len * 8,
            .signedness = .unsigned,
        },
    });
}

Conclusion

Of the three approaches, this is the least readable and less approachable. Is it worth it? It depends on your input and the values you're comparing against. In my benchmarks, using @bitCast performs roughly the same as std.meta.stringToEnum. But there are some cases where @bitCast can outperform std.meta.stringToEnum by as much as 50%. Perhaps that's the real value of this approach: the performance is less dependent on the input or the values being matched against.

"But the grooms, or 'bettos,' as the Japanese call them, are not the only ones who indulge in tattooing. You will see many of the 'sendos,' or boat-coolies, thus marked, but in a less degree than the bettos. Perhaps it is because the grooms are obliged to run so much, and consequently wish to lay aside all garments. As they must wear something, they have their skins decorated in this way, and thus have a suit of clothing always about them. Joe Johnston's shattered army was at Jackson, about forty-five miles to northward; beleaguered Vicksburg was in the Northwest, a trifle farther away; Natchez lay southwest, still more distant; and nearly twice as far in the south was our heartbroken New Orleans. We had paused to recuperate our animals, and there was a rumor that we were to get new clothing. Anyhow we had rags with honor, and a right to make as much noise as we chose. "Nonsense, my dear fellow. Now let me open your eyes. Behold the great force of a man who is gifted with second sight. Where did you get those notes? Was it not on the same evening as the murder?" "Oh, nothing," came the reply. "Only I was so silly as to place the wrong end of my cigarette in my mouth and burnt my lips. What's tuberose?" Reviewing these mechanical conditions, we may at once see sufficient reasons for the platen movement of planing machines; and that it would be objectionable, if not impossible, to add a traversing or cutting action to tools already supported through the medium of eight joints. To traverse for cutting would require a moving gib joint in place of the bolted one, between the standards and main frame, leading to a complication of joints and movements quite impracticable. And disadvantaged aye begins the strife. We have already seen how this fundamental division is applied to the universe as a whole. But our philosopher is not content with classifying the phenomena as he finds360 them; he attempts to demonstrate the necessity of their dual existence; and in so doing is guilty of something very like a vicious circle. For, after proving from the terrestrial movements that there must be an eternal movement to keep them going, he now assumes the revolving aether, and argues that there must be a motionless solid centre for it to revolve round, although a geometrical axis would have served the purpose equally well. By a still more palpable fallacy, he proceeds to show that a body whose tendency is towards the centre, must, in the nature of things, be opposed by another body whose tendency is towards the circumference. In order to fill up the interval created by this opposition, two intermediate bodies are required, and thus we get the four elementsearth, water, air, and fire. These, again, are resolved into the antithetical couples, dry and wet, hot and cold, the possible combinations of which, by twos, give us the four elements once more. Earth is dry and cold, water cold and wet, air wet and hot, fire hot and dry; each adjacent pair having a quality in common, and each element being characterized by the excess of a particular quality; earth is especially dry, water cold, air wet, and fire hot. The common centre of each antithesis is what Aristotle calls the First Matter, the mere abstract unformed possibility of existence. This matter always combines two qualities, and has the power of oscillating from one quality to another, but it cannot, as a rule, simultaneously exchange both for their opposites. Earth may pass into water, exchanging dry for wet, but not so readily into air, which would necessitate a double exchange at the same moment. 192 He helped her out. "I have drifted in a way," he went on to explain. "I left home when I was a mere boy, and the spirit of savagery and unrest laid hold of me. I can't break away. And I'm not even sure that I want to. You, I dare say, can't understand." Yet he felt so sure, for some reason, that she could that he[Pg 71] merely nodded his head when she said briefly, "I can." "Then, too," he went on, "there is something in the Indian character that strikes a responsive chord in me. I come of lawless stock myself. I was born in Sidney." Then he stopped short. What business was it of hers where he had been born? He had never seen fit to speak of it before. Nevertheless he intended that she should understand now. So he made it quite plain. "Sidney was a convict settlement, you know," he said deliberately, "and marriages were promiscuous. My grandfather was an officer who was best away from England. My grandmother poisoned her first husband. That is on my mother's side. On my father's side it was about as mixed." He leaned back, crossing his booted legs and running his fingers into his cartridge belt. His manner asked with a certain defiance, what she was going to do about it, or to think. "Naw; git out. Don't bother me with no questions, I tell you," impatiently said a man in citizen's clothes, who with arms outspread was signalling the switching engines. "'Tain't my business to give information to people. Got all I kin do to furnish brains for them bull-headed engineers. Go to that Quartermaster you see over there in uniform. The Government pays him for knowin' things. It don't me." As little Pete dropped to the ground, his nervous finger touched the trigger and his gun went off up in the air. The others took this as a cue, and banged away as rapidly as they could get their muskets off. "I am no master," Cadnan said wearily. "I am a slave." "The people know," Dodd said. "It's out. It's all out. About the slavery. Is that what you mean?" Cadnan peered at him, half-fearfully. "You are a master." One did not give orders to masters, or argue with them. "Why not? There's naun shameful in it. Munds's brother did it for twenty years. And think of the difference it'll m?ake to usthirty pound or so a year, instead of the dead loss of Harry's keep and the wages of an extra man beside. I tell you, mother, I wur fair sick about the farm till I thought of this." "Ben, I swear I'm your true wife." Reuben started, and Pete awoke noisily. Harry was frightened and dropped his string, crying because he could not find it. The knock came again, and this time Pete crossed the room yawning, and opened the door. seecr26w.com.cn
sg6gw3r6.com.cn
cn200051743.com.cn
tazaizhe.com.cn
www.yjhl.net.cn
elify.com.cn
zshggs.com.cn
www.lxvk.com.cn
fjnfhy.com.cn
robocon.net.cn
中韩欧美一级一中文字暮 _黄片BB亚洲AV无码天堂www亚洲国产韩国欧美在线不卡一级 _毛片机地男男性行为免费视频播放九九欧美一级毛欧美片 _啪拍看片久色综合免费福利视频玖玖60岁欧美老妇一级毛 中韩欧美一级一中文字暮 中文字幕第十九页 中央游月中文字幕 中文字幕 第9页 中文字幕午夜福利 黄片BB亚洲AV无码天堂www亚洲国产韩国欧美在线不卡一级 中文字幕欧美日韩 中村知惠中文字幕 啪啪啪在线视频 中文字幕亚洲综合 中文字幕先锋资源 中文字幕 青青草 中文字幕资源网站 中韩欧美一级一中文字暮 中文字幕卡通动漫 中文字幕理论电影 免费视频播放 中文字幕资源在线 毛片免费观看 黄色一级电影片 中国美女一级看片 中文字幕先锋影音 黄片BB亚洲AV无码天堂www亚洲国产韩国欧美在线不卡一级 欧美在线不卡 中文字幕巨乳有码 啪拍看片久色综合免费福利视频玖玖60岁欧美老妇一级毛 中文字幕第86页 中文字幕91在线 黄色一级录像片 中文字幕久荜在线 黄色一级欧美片 毛片视频在线 中文字幕 第7页 中文字幕每日更新 毛片免费试看 中文字幕巨乱亚洲 中韩欧美一级一中文字暮 啪拍看片久色综合免费福利视频玖玖60岁欧美老妇一级毛 中韩欧美一级一中文字暮 毛片机地男男性行为免费视频播放九九欧美一级毛欧美片 黄色一级录像带 中文在线视频观看 中文字幕Av电影 啪啪啪男女视频 中文字幕 新妈妈 中国一级特黄大片 中文字幕久久视频 中文字幕黄色视频 中国黄色一级大片 中文字幕手机看片 中文字幕伦理在线 欧美一级毛 中韩欧美一级一中文字暮 免费视频播 啪啪免费视频网站 中文字幕男人天堂 黄色一级伦理片 中文字幕亚洲在线 中文字幕视频不卡 毛片免费网址 中国一级黄色大片 中文字幕亚洲情字 啪啪视频在线播放 啪拍看片久色综合免费福利视频玖玖60岁欧美老妇一级毛 中韩欧美一级一中文字暮 啪啪免费在线视频 亚洲AV无码天堂 中文字幕丝袜美腿 毛片免费视频 毛片在线电影 毛片在线不卡 啪啪啪在线播放 中文字幕伦理电影 中文字幕中文字幕 中文字幕一级在线 毛片免费基地 中文字幕综合影院 中文字幕手机在线 中文字幕视频在线 毛片机地男男性行为免费视频播放九九欧美一级毛欧美片 中国一级特大黄片 中文字幕乱码视频 中国成人在线视频 啪啪啪视频欧美 中国一级黄色电影 啪啪啪性爱动态图 啪啪啪欧美视频 黄色一级片播放 中文字幕乱码免费 黄片BB亚洲AV无码天堂www亚洲国产韩国欧美在线不卡一级 黄色一级毛毛片 中文娱乐在线视频 黄色一级黄色片 中文字幕乱伦电影 黄色一级片aa 中文字幕第十七页 男男性行为免费视频播放 黄色一级片观看 毛片免费电影
кŷһһĺ _ƬBBAVwww޹ŷ߲һ _ëƬΪƵžžŷһëŷƬ _žĿƬɫۺѸƵ60ŷϸһë кŷһһĺ Ļʮҳ Ļ Ļ 9ҳ Ļҹ ƬBBAVwww޹ŷ߲һ Ļŷպ д֪Ļ žžžƵ Ļۺ ĻȷԴ Ļ ĻԴվ кŷһһĺ Ļͨ Ļ۵Ӱ Ƶ ĻԴ ëƬѹۿ ɫһӰƬ йŮһƬ ĻȷӰ ƬBBAVwww޹ŷ߲һ ŷ߲ Ļ žĿƬɫۺѸƵ60ŷϸһë Ļ86ҳ Ļ91 ɫһ¼Ƭ Ļ ɫһŷƬ ëƬƵ Ļ 7ҳ Ļÿո ëƬԿ Ļ кŷһһĺ žĿƬɫۺѸƵ60ŷϸһë кŷһһĺ ëƬΪƵžžŷһëŷƬ ɫһ¼ Ƶۿ ĻAvӰ žžžŮƵ Ļ йһػƴƬ ĻþƵ ĻɫƵ йɫһƬ ĻֻƬ Ļ ŷһë кŷһһĺ Ƶ žžƵվ Ļ ɫһƬ Ļ ĻƵ ëƬַ йһɫƬ Ļ žžƵ߲ žĿƬɫۺѸƵ60ŷϸһë кŷһһĺ žžƵ AV Ļ˿ ëƬƵ ëƬߵӰ ëƬ߲ žžž߲ ĻӰ ĻĻ Ļһ ëƬѻ ĻۺӰԺ Ļֻ ĻƵ ëƬΪƵžžŷһëŷƬ йһشƬ ĻƵ йƵ žžžƵŷ йһɫӰ žžž԰̬ͼ žžžŷƵ ɫһƬ Ļ ƬBBAVwww޹ŷ߲һ ɫһëëƬ Ƶ ɫһɫƬ Ļ׵Ӱ ɫһƬaa Ļʮҳ ΪƵ ɫһƬۿ ëƬѵӰ
ENTER NUMBET 0018