# How to uppercase and lowercase ASCII strings using SIMD in Mojo

**URL:** <https://forum.modular.com/t/how-to-uppercase-and-lowercase-ascii-strings-using-simd-in-mojo/301>\
**Category:** Mojo\
**Tags:** discussion, 24\_5, docs\
**Created:** [December 14, 2024, 2:32am UTC](https://forum.modular.com/t/how-to-uppercase-and-lowercase-ascii-strings-using-simd-in-mojo/301 "2024-12-14T02:32:01Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![martinvuyk](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/martinvuyk/32/48_2.png) [@martinvuyk](https://forum.modular.com/u/martinvuyk)\
**Post date:** [December 14, 2024, 2:32am UTC](https://forum.modular.com/t/how-to-uppercase-and-lowercase-ascii-strings-using-simd-in-mojo/301/1 "2024-12-14T02:32:01Z")

</div>

```mojo
from memory import Span
from sys.info import simdwidthof

fn apply[
    D: DType,
    O: MutableOrigin, //,
    func: fn[w: Int] (SIMD[D, w]) -> SIMD[D, w],
    *,
    where: fn[w: Int] (SIMD[D, w]) -> SIMD[DType.bool, w],
](span: Span[Scalar[D], O]):
    """Apply the function to the `Span` inplace.

    Parameters:
        D: The DType.
        O: The origin of the `Span`.
        func: The function to evaluate.
        where: The condition to apply the function.
    """

    alias widths = (256, 128, 64, 32, 16, 8, 4)
    var ptr = span.unsafe_ptr()
    var length = len(span)
    var processed = 0

    @parameter
    for i in range(len(widths)):
        alias w = widths.get[i, Int]()

        @parameter
        if simdwidthof[D]() >= w:
            for _ in range((length - processed) // w):
                var p_curr = ptr + processed
                var vec = p_curr.load[width=w]()                
                p_curr.store(where(vec).select(func(vec), vec))
                processed += w

    for i in range(length - processed):
        var vec = ptr[processed + i]
        if where(vec):
            (ptr + processed + i).init_pointee_move(func(vec))

fn is_lower_ascii[w: Int](value: SIMD[DType.uint8, w]) -> SIMD[DType.bool, w]:
    alias `a` = Byte(ord("a"))
    alias `z` = Byte(ord("z"))
    return (value >= `a`) & (value <= `z`)

fn is_upper_ascii[w: Int](value: SIMD[DType.uint8, w]) -> SIMD[DType.bool, w]:
    alias `A` = Byte(ord("A"))
    alias `Z` = Byte(ord("Z"))
    return (value >= `A`) & (value <= `Z`)

fn toggle_case[w: Int](value: SIMD[DType.uint8, w]) -> SIMD[DType.uint8, w]:
    alias `a` = Byte(ord("a"))
    alias `A` = Byte(ord("A"))
    return value ^ (`A` ^ `a`)

fn main():
    items = List[Byte](
        ord("a"), ord("a"), ord("a"), ord("A"), ord("A"), ord("A")
    )
    tmp_copy = items
    span = Span(tmp_copy)
    apply[func=toggle_case, where=is_lower_ascii](span)
    for i in range(len(items)):
        if i < 3:
            print(span[i] == ord("A")) # True
        else:
            print(span[i] == ord("A")) # True

    tmp_copy = items
    span = Span(tmp_copy)

    apply[func=toggle_case, where=is_upper_ascii](span)
    for i in range(len(items)):
        if i < 3:
            print(span[i] == ord("a")) # True
        else:
            print(span[i] == ord("a")) # True

```

---

<div class="post-metadata">

**Author:** ![owenhilyard](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/owenhilyard/32/41_2.png) [@owenhilyard](https://forum.modular.com/u/owenhilyard)\
**Post date:** [December 14, 2024, 7:41pm UTC](https://forum.modular.com/t/how-to-uppercase-and-lowercase-ascii-strings-using-simd-in-mojo/301/2 "2024-12-14T19:41:41Z")

</div>

You might want to use the fact that you can `or` with 0x20 (SPACE) to upper case ascii text and `and` with the inverse to lowercase.

---

<div class="post-metadata">

**Author:** ![martinvuyk](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/martinvuyk/32/48_2.png) [@martinvuyk](https://forum.modular.com/u/martinvuyk)\
**Post date:** [December 14, 2024, 8:09pm UTC](https://forum.modular.com/t/how-to-uppercase-and-lowercase-ascii-strings-using-simd-in-mojo/301/3 "2024-12-14T20:09:23Z")

</div>

That’s a neat trick, now that I looked for the stdlib impl I see it used to do (before adding full unicode casing) `char ^ (1 << 5)` which would be `^ 0x20`. Which operation is faster? I imagine each CPU manufacturer has different amount of bitwise-op circuits. I also assumed the compiler would optimize sequential ops with aliased values to some such clever bitwise op

---

<div class="post-metadata">

**Author:** ![owenhilyard](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/owenhilyard/32/41_2.png) [@owenhilyard](https://forum.modular.com/u/owenhilyard)\
**Post date:** [December 14, 2024, 8:17pm UTC](https://forum.modular.com/t/how-to-uppercase-and-lowercase-ascii-strings-using-simd-in-mojo/301/4 "2024-12-14T20:17:21Z")

</div>

`^ 0x20` inverts the case, meaning that upper case letters at the start of a sentence will become lower case. Bitwise operations are generally 1 op per cycle even for 64 byte wide vector instructions, and they’re very cheap gate wise (literally 1 gate per bit) so I think that almost any ALU will be able to service them. The compiler may be fixing it, but this is such a well-known trick that I don’t see many issues with using it directly.

---

<div class="post-metadata">

**Author:** ![martinvuyk](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/martinvuyk/32/48_2.png) [@martinvuyk](https://forum.modular.com/u/martinvuyk)\
**Post date:** [December 14, 2024, 8:27pm UTC](https://forum.modular.com/t/how-to-uppercase-and-lowercase-ascii-strings-using-simd-in-mojo/301/5 "2024-12-14T20:27:29Z")

</div>

> [@martinvuyk](#):
>
> ```auto
> fn toggle_case[w: Int](value: SIMD[DType.uint8, w]) -> SIMD[DType.uint8, w]:
> alias `a` = Byte(ord("a"))
> alias `A` = Byte(ord("A"))
> return value ^ (`A` ^ `a`)
> 
> ```

Edited the post to add a bit less “magical-number-using” implementation

---

<div class="post-metadata">

**Author:** ![owenhilyard](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/owenhilyard/32/41_2.png) [@owenhilyard](https://forum.modular.com/u/owenhilyard)\
**Post date:** [December 14, 2024, 8:29pm UTC](https://forum.modular.com/t/how-to-uppercase-and-lowercase-ascii-strings-using-simd-in-mojo/301/6 "2024-12-14T20:29:11Z")

</div>

I think this may actually be a less recognizable version of the trick. You may want to write up the “why” and just use space directly.

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex001/uploads/modular/original/1X/2751e0fbdc595a99718b216730957e9db4448cfd.jpeg) [@system](https://forum.modular.com/u/system)\
**Post date:** [June 12, 2025, 8:29pm UTC](https://forum.modular.com/t/how-to-uppercase-and-lowercase-ascii-strings-using-simd-in-mojo/301/7 "2025-06-12T20:29:51Z")

</div>

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.
