Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(qe): support MySQL base64 newlines #4955

Merged
merged 1 commit into from
Jul 17, 2024
Merged

fix(qe): support MySQL base64 newlines #4955

merged 1 commit into from
Jul 17, 2024

Conversation

Weakky
Copy link
Member

@Weakky Weakky commented Jul 17, 2024

Overview

fixes prisma/prisma#23262

MySQL encodes base64 strings with a newline every 76 characters. Rust's base64 crate does not support newlines and suggests sanitizing the input before passing it down. This PR sanitizes MySQL's base64 outputs.

Credit to @aqrln for the fast™ sanitization.

With read loop

fn read_loop(s: &str, buf: &mut [u8]) {
    let mut pos = 0;

    for c in s.chars() {
        if pos == buf.len() {
            break;
        }

        if c != '\n' {
            buf[pos] = c as u8;
            pos += 1;
        }
    }
}

image

Current implementation

fn sanitize_base64<'a>(mut s: &str, buf: &'a mut [u8]) -> &'a [u8] {
    let mut pos = 0;

    while !s.is_empty() && pos < buf.len() {
        let nl = s.find('\n').unwrap_or(s.len());
        let len = nl.min(buf.len() - pos);
        buf[pos..pos + len].copy_from_slice(&s.as_bytes()[..len]);
        pos += len;
        s = &s[(nl + 1).min(s.len())..];
    }

    &buf[0..pos]
}

image

Co-authored-by: Alexey Orlenko <alex@aqrln.net>
Copy link
Contributor

github-actions bot commented Jul 17, 2024

WASM Query Engine file Size

Engine This PR Base branch Diff
Postgres 2.056MiB 2.053MiB 2.986KiB
Postgres (gzip) 820.389KiB 820.249KiB 144.000B
Mysql 2.026MiB 2.022MiB 4.068KiB
Mysql (gzip) 808.375KiB 806.666KiB 1.709KiB
Sqlite 1.925MiB 1.922MiB 2.986KiB
Sqlite (gzip) 768.531KiB 768.553KiB -23.000B

Copy link

codspeed-hq bot commented Jul 17, 2024

CodSpeed Performance Report

Merging #4955 will not alter performance

Comparing fix/mysql-base64 (62d56b4) with main (7484fd8)

Summary

✅ 11 untouched benchmarks

@Weakky Weakky added this to the 5.18.0 milestone Jul 17, 2024
@Weakky Weakky marked this pull request as ready for review July 17, 2024 15:28
@Weakky Weakky requested a review from a team as a code owner July 17, 2024 15:28
@Weakky Weakky requested review from Druue and removed request for a team July 17, 2024 15:28
Copy link
Contributor

@jkomyno jkomyno left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! For future reference, is there a link to the source of the benchmarks and plots above?

@Weakky Weakky merged commit 15f8fe4 into main Jul 17, 2024
223 checks passed
@Weakky Weakky deleted the fix/mysql-base64 branch July 17, 2024 15:43
@Weakky
Copy link
Member Author

Weakky commented Jul 17, 2024

Nice! For future reference, is there a link to the source of the benchmarks and plots above?

use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn read_loop(s: &str, buf: &mut [u8]) {
    let mut pos = 0;

    for c in s.chars() {
        if pos == buf.len() {
            break;
        }

        if c != '\n' {
            buf[pos] = c as u8;
            pos += 1;
        }
    }
}

fn read_memcpy(mut s: &str, buf: &mut [u8]) {
    let mut pos = 0;

    while !s.is_empty() && pos < buf.len() {
        let nl = s.find('\n').unwrap_or(s.len());
        let len = nl.min(buf.len() - pos);
        buf[pos..pos + len].copy_from_slice(&s.as_bytes()[..len]);
        pos += len;
        s = &s[(nl + 1).min(s.len())..];
    }
}

fn prepare_string(len: usize) -> String {
    let mut s = String::with_capacity(len);

    for i in 0..len {
        s.push(if len % 77 == 0 {
            '\n'
        } else {
            (i % 26 + 97) as u8 as char
        })
    }

    s
}

fn benchmark(c: &mut Criterion) {
    let mut buf = [0; 1024];
    let s = prepare_string(buf.len());

    c.bench_function("read_loop", |b| {
        b.iter(|| {
            read_loop(black_box(&s), black_box(&mut buf));
        })
    });

    c.bench_function("read_memcpy", |b| {
        b.iter(|| {
            read_memcpy(black_box(&s), black_box(&mut buf));
        })
    });
}

criterion_group!(benches, benchmark);
criterion_main!(benches);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants