Merge branch 'master' into stable

This commit is contained in:
Kogia-sima 2020-07-11 17:54:29 +09:00
commit 67f2bc2870
32 changed files with 415 additions and 693 deletions

27
Cargo.lock generated
View File

@ -661,11 +661,11 @@ dependencies = [
[[package]]
name = "integration-tests"
version = "0.1.2"
version = "0.1.3"
dependencies = [
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"sailfish 0.1.2",
"sailfish-macros 0.1.2",
"sailfish 0.1.3",
"sailfish-macros 0.1.3",
"trybuild 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -693,6 +693,11 @@ name = "itoa"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itoap"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kernel32-sys"
version = "0.2.2"
@ -1040,15 +1045,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "sailfish"
version = "0.1.2"
version = "0.1.3"
dependencies = [
"itoap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sailfish-compiler"
version = "0.1.2"
version = "0.1.3"
dependencies = [
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1060,20 +1066,20 @@ dependencies = [
[[package]]
name = "sailfish-examples"
version = "0.1.2"
version = "0.1.3"
dependencies = [
"actix-rt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"actix-web 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sailfish 0.1.2",
"sailfish-macros 0.1.2",
"sailfish 0.1.3",
"sailfish-macros 0.1.3",
]
[[package]]
name = "sailfish-macros"
version = "0.1.2"
version = "0.1.3"
dependencies = [
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
"sailfish-compiler 0.1.2",
"sailfish-compiler 0.1.3",
]
[[package]]
@ -1493,6 +1499,7 @@ dependencies = [
"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
"checksum ipconfig 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7"
"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
"checksum itoap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e804a5759b475f44377998918a7e3be9da3767056f5e77751ef7803893db0e9"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"

View File

@ -30,8 +30,8 @@ Dependencies:
```toml
[dependencies]
sailfish = "0.1.2"
sailfish-macros = "0.1.2"
sailfish = "0.1.3"
sailfish-macros = "0.1.3"
```
Template file (templates/hello.stpl):

8
benches/Cargo.lock generated
View File

@ -207,6 +207,7 @@ dependencies = [
"serde_json",
"serde_yaml",
"tera",
"v_htmlescape",
"yarte",
]
@ -918,6 +919,12 @@ version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
[[package]]
name = "itoap"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e804a5759b475f44377998918a7e3be9da3767056f5e77751ef7803893db0e9"
[[package]]
name = "jobserver"
version = "0.1.21"
@ -1795,6 +1802,7 @@ checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
name = "sailfish"
version = "0.1.2"
dependencies = [
"itoap",
"ryu",
"version_check 0.9.2",
]

View File

@ -1,6 +1,6 @@
[package]
name = "benches"
version = "0.1.0"
version = "0.1.3"
authors = ["Dirkjan Ochtman <dirkjan@ochtman.nl>", "Ryohei Machida <orcinus4627@gmail.com>"]
build = "src/build.rs"
edition = "2018"
@ -23,6 +23,7 @@ serde_derive = "1"
serde_json = "1"
serde_yaml = "0.8"
tera = { git = "https://github.com/Keats/tera" }
v_htmlescape = "0.9.1"
yarte = { git = "https://github.com/botika/yarte", features = ["bytes-buf", "fixed"] }
[build-dependencies]

View File

@ -42,5 +42,7 @@ $ cargo bench
## License
Copyright (c) 2020 Dirkjan Ochtman and Ryohei Machida
This benchmark code is distributed under the special permission granted by [Dirkjan Ochtman](https://github.com/djc) (See [this issue](https://github.com/djc/template-benchmarks-rs/issues/26)).
**You cannot modify or redistribute the source code without an explicit permission**.

View File

@ -1,4 +1,5 @@
use std::io::Write;
use v_htmlescape::escape;
use criterion;
@ -68,8 +69,8 @@ pub fn teams(b: &mut criterion::Bencher<'_>, _: &usize) {
&mut output,
"<li class=\"{champion}\">
<b>{name}</b>: {score}",
champion = champion,
name = team.name,
champion = escape(champion),
name = escape(&team.name),
score = team.score
)
.unwrap();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

View File

@ -4,8 +4,8 @@ In order to use sailfish templates, you have add two dependencies in your `Cargo
```toml
[dependencies]
sailfish = "0.1.2"
sailfish-macros = "0.1.2"
sailfish = "0.1.3"
sailfish-macros = "0.1.3"
```
`sailfish` crate contains runtime for rendering contents, and `sailfish-macros` serves you derive macros to compile and import the template files.

View File

@ -1,6 +1,6 @@
[package]
name = "sailfish-examples"
version = "0.1.2"
version = "0.1.3"
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
edition = "2018"
publish = false

View File

@ -1,6 +1,6 @@
[package]
name = "integration-tests"
version = "0.1.2"
version = "0.1.3"
authors = ["Kogia-sima <orcinus4627@gmail.com>"]
edition = "2018"
publish = false

View File

@ -1,6 +1,6 @@
[package]
name = "sailfish-compiler"
version = "0.1.2"
version = "0.1.3"
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
description = "Really fast, intuitive template engine for Rust"
homepage = "https://github.com/Kogia-sima/sailfish"

View File

@ -181,13 +181,15 @@ fn derive_template_impl(tokens: TokenStream) -> Result<TokenStream, syn::Error>
}
}
let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect(
"Internal error: environmental variable `CARGO_MANIFEST_DIR` is not set.",
));
#[cfg(feature = "config")]
let mut config = Config::search_file_and_read(&*manifest_dir)
.map_err(|e| syn::Error::new(Span::call_site(), e))?;
let mut config = {
let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").expect(
"Internal error: environmental variable `CARGO_MANIFEST_DIR` is not set.",
));
Config::search_file_and_read(&*manifest_dir)
.map_err(|e| syn::Error::new(Span::call_site(), e))?
};
#[cfg(not(feature = "config"))]
let mut config = Config::default();

View File

@ -1,6 +1,6 @@
[package]
name = "sailfish-macros"
version = "0.1.2"
version = "0.1.3"
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
description = "Really fast, intuitive template engine for Rust"
homepage = "https://github.com/Kogia-sima/sailfish"
@ -29,6 +29,6 @@ proc-macro2 = "1.0.11"
[dependencies.sailfish-compiler]
path = "../sailfish-compiler"
version = "0.1.2"
version = "0.1.3"
default-features = false
features = ["procmacro"]

View File

@ -1,6 +1,6 @@
[package]
name = "sailfish"
version = "0.1.2"
version = "0.1.3"
authors = ["Ryohei Machida <orcinus4627@gmail.com>"]
description = "Really fast, intuitive template engine for Rust"
homepage = "https://github.com/Kogia-sima/sailfish"
@ -17,6 +17,7 @@ default = ["perf-inline"]
perf-inline = []
[dependencies]
itoap = "0.1.0"
ryu = "1.0.4"
[build-dependencies]

View File

@ -44,8 +44,8 @@ pub use runtime::{RenderError, RenderResult};
pub trait TemplateOnce: Sized {
/// Render the template and return the rendering result as `RenderResult`
///
/// This method never returns `Err`, unless you manually implement `Render` trait
/// for custom type and return `Err` inside it.
/// This method never returns `Err`, unless you explicitly return RenderError
/// inside templates
#[inline]
fn render_once(self) -> runtime::RenderResult {
let mut buf = String::new();
@ -54,10 +54,13 @@ pub trait TemplateOnce: Sized {
}
/// Render the template and append the result to `buf`.
///
/// This method never returns `Err`, unless you explicitly return RenderError
/// inside templates
fn render_once_to_string(self, buf: &mut String) -> Result<(), RenderError>;
}
/// WIP
/// Work in Progress
pub trait Template {
fn render(&self) -> runtime::RenderResult;
}

View File

@ -4,20 +4,6 @@ use std::mem::{align_of, ManuallyDrop};
use std::ops::{Add, AddAssign};
use std::ptr;
#[cfg(sailfish_nightly)]
macro_rules! unlikely {
($val:expr) => {
std::intrinsics::unlikely($val)
};
}
#[cfg(not(sailfish_nightly))]
macro_rules! unlikely {
($val:expr) => {
$val
};
}
/// Buffer for rendered contents
///
/// This struct is quite simular to `String`, but some methods are
@ -41,7 +27,7 @@ impl Buffer {
#[cfg_attr(feature = "perf-inline", inline)]
pub fn with_capacity(n: usize) -> Buffer {
unsafe {
if n == 0 {
if unlikely!(n == 0) {
Self::new()
} else {
let layout = Layout::from_size_align_unchecked(n, 1);
@ -103,6 +89,7 @@ impl Buffer {
if unlikely!(self.len + size > self.capacity) {
self.reserve_internal(size);
}
debug_assert!(self.len + size <= self.capacity);
}
#[inline]
@ -115,6 +102,7 @@ impl Buffer {
/// This consumes the `Buffer`, so we do not need to copy its contents.
#[inline]
pub fn into_string(self) -> String {
debug_assert!(self.len <= self.capacity);
let buf = ManuallyDrop::new(self);
unsafe { String::from_raw_parts(buf.data, buf.len, buf.capacity) }
}
@ -128,6 +116,7 @@ impl Buffer {
std::ptr::copy_nonoverlapping(data.as_ptr(), p, size);
self.len += size;
}
debug_assert!(self.len <= self.capacity);
}
#[inline]
@ -137,31 +126,35 @@ impl Buffer {
}
#[cfg_attr(feature = "perf-inline", inline)]
#[cold]
fn reserve_internal(&mut self, size: usize) {
unsafe {
let new_capacity = std::cmp::max(self.capacity * 2, self.len + size);
self.data = self.realloc(new_capacity);
debug_assert!(new_capacity > self.capacity);
self.data = safe_realloc(self.data, self.capacity, new_capacity);
self.capacity = new_capacity;
}
debug_assert!(!self.data.is_null());
debug_assert!(self.len <= self.capacity);
}
}
#[cold]
unsafe fn safe_realloc(ptr: *mut u8, capacity: usize, new_capacity: usize) -> *mut u8 {
assert!(new_capacity <= std::usize::MAX / 2, "capacity is too large");
let data = if unlikely!(capacity == 0) {
let new_layout = Layout::from_size_align_unchecked(new_capacity, 1);
alloc(new_layout)
} else {
let old_layout = Layout::from_size_align_unchecked(capacity, 1);
realloc(ptr, old_layout, new_capacity)
};
if data.is_null() {
handle_alloc_error(Layout::from_size_align_unchecked(new_capacity, 1));
}
#[cfg_attr(feature = "perf-inline", inline)]
unsafe fn realloc(&self, cap: usize) -> *mut u8 {
let data = if unlikely!(self.capacity == 0) {
let new_layout = Layout::from_size_align_unchecked(cap, 1);
alloc(new_layout)
} else {
debug_assert!(cap <= std::usize::MAX / 2, "capacity is too large");
let old_layout = Layout::from_size_align_unchecked(self.capacity, 1);
realloc(self.data, old_layout, cap)
};
if data.is_null() {
handle_alloc_error(Layout::from_size_align_unchecked(cap, 1));
}
data
}
data
}
impl Clone for Buffer {

View File

@ -7,7 +7,6 @@ use std::arch::x86_64::*;
use std::slice;
use super::super::Buffer;
use super::{naive, sse2};
use super::{ESCAPED, ESCAPED_LEN, ESCAPE_LUT};
const VECTOR_BYTES: usize = std::mem::size_of::<__m256i>();
@ -15,15 +14,12 @@ const VECTOR_ALIGN: usize = VECTOR_BYTES - 1;
#[target_feature(enable = "avx2")]
pub unsafe fn escape(feed: &str, buffer: &mut Buffer) {
debug_assert!(feed.len() >= 16);
let len = feed.len();
if len < 8 {
let start_ptr = feed.as_ptr();
let end_ptr = start_ptr.add(len);
naive::escape(buffer, start_ptr, start_ptr, end_ptr);
return;
} else if len < VECTOR_BYTES {
sse2::escape(feed, buffer);
if len < VECTOR_BYTES {
escape_small(feed, buffer);
return;
}
@ -35,11 +31,11 @@ pub unsafe fn escape(feed: &str, buffer: &mut Buffer) {
let v_key1 = _mm256_set1_epi8(0x27);
let v_key2 = _mm256_set1_epi8(0x3e);
let maskgen = |x: __m256i| -> i32 {
let maskgen = |x: __m256i| -> u32 {
_mm256_movemask_epi8(_mm256_or_si256(
_mm256_cmpeq_epi8(_mm256_or_si256(x, v_independent1), v_key1),
_mm256_cmpeq_epi8(_mm256_or_si256(x, v_independent2), v_key2),
))
)) as u32
};
let mut ptr = start_ptr;
@ -97,5 +93,106 @@ pub unsafe fn escape(feed: &str, buffer: &mut Buffer) {
next_ptr = next_ptr.add(VECTOR_BYTES);
}
sse2::escape_aligned(buffer, start_ptr, ptr, end_ptr);
debug_assert!(next_ptr > end_ptr);
if ptr < end_ptr {
debug_assert!((end_ptr as usize - ptr as usize) < VECTOR_BYTES);
let backs = VECTOR_BYTES - (end_ptr as usize - ptr as usize);
let mut mask =
maskgen(_mm256_loadu_si256(ptr.sub(backs) as *const __m256i)) >> backs;
while mask != 0 {
let trailing_zeros = mask.trailing_zeros() as usize;
let ptr2 = ptr.add(trailing_zeros);
let c = ESCAPE_LUT[*ptr2 as usize] as usize;
if c < ESCAPED_LEN {
if start_ptr < ptr2 {
let slc = slice::from_raw_parts(
start_ptr,
ptr2 as usize - start_ptr as usize,
);
buffer.push_str(std::str::from_utf8_unchecked(slc));
}
buffer.push_str(*ESCAPED.get_unchecked(c));
start_ptr = ptr2.add(1);
}
mask ^= 1 << trailing_zeros;
}
}
if end_ptr > start_ptr {
let slc = slice::from_raw_parts(start_ptr, end_ptr as usize - start_ptr as usize);
buffer.push_str(std::str::from_utf8_unchecked(slc));
}
}
#[inline]
#[target_feature(enable = "avx2")]
unsafe fn escape_small(feed: &str, buffer: &mut Buffer) {
debug_assert!(feed.len() >= 16);
debug_assert!(feed.len() < VECTOR_BYTES);
let len = feed.len();
let mut start_ptr = feed.as_ptr();
let mut ptr = start_ptr;
let end_ptr = start_ptr.add(len);
let v_independent1 = _mm_set1_epi8(5);
let v_independent2 = _mm_set1_epi8(2);
let v_key1 = _mm_set1_epi8(0x27);
let v_key2 = _mm_set1_epi8(0x3e);
let maskgen = |x: __m128i| -> u32 {
_mm_movemask_epi8(_mm_or_si128(
_mm_cmpeq_epi8(_mm_or_si128(x, v_independent1), v_key1),
_mm_cmpeq_epi8(_mm_or_si128(x, v_independent2), v_key2),
)) as u32
};
let mut mask = maskgen(_mm_loadu_si128(ptr as *const __m128i));
while mask != 0 {
let trailing_zeros = mask.trailing_zeros() as usize;
let ptr2 = ptr.add(trailing_zeros);
let c = ESCAPE_LUT[*ptr2 as usize] as usize;
if c < ESCAPED_LEN {
if start_ptr < ptr2 {
let slc =
slice::from_raw_parts(start_ptr, ptr2 as usize - start_ptr as usize);
buffer.push_str(std::str::from_utf8_unchecked(slc));
}
buffer.push_str(*ESCAPED.get_unchecked(c));
start_ptr = ptr2.add(1);
}
mask ^= 1 << trailing_zeros;
}
if len != 16 {
ptr = ptr.add(16);
let read_ptr = end_ptr.sub(16);
let backs = 32 - len;
let mut mask = maskgen(_mm_loadu_si128(read_ptr as *const __m128i)) >> backs;
while mask != 0 {
let trailing_zeros = mask.trailing_zeros() as usize;
let ptr2 = ptr.add(trailing_zeros);
let c = ESCAPE_LUT[*ptr2 as usize] as usize;
if c < ESCAPED_LEN {
if start_ptr < ptr2 {
let slc = slice::from_raw_parts(
start_ptr,
ptr2 as usize - start_ptr as usize,
);
buffer.push_str(std::str::from_utf8_unchecked(slc));
}
buffer.push_str(*ESCAPED.get_unchecked(c));
start_ptr = ptr2.add(1);
}
mask ^= 1 << trailing_zeros;
}
}
if end_ptr > start_ptr {
let slc = slice::from_raw_parts(start_ptr, end_ptr as usize - start_ptr as usize);
buffer.push_str(std::str::from_utf8_unchecked(slc));
}
}

View File

@ -49,7 +49,7 @@ pub unsafe fn escape(feed: &str, buffer: &mut Buffer) {
return;
}
let ptr = start_ptr;
let mut ptr = start_ptr;
let aligned_ptr = ptr.add(USIZE_BYTES - (start_ptr as usize & USIZE_ALIGN));
debug_assert_eq!(aligned_ptr as usize % USIZE_BYTES, 0);
debug_assert!(aligned_ptr <= end_ptr);
@ -59,15 +59,8 @@ pub unsafe fn escape(feed: &str, buffer: &mut Buffer) {
start_ptr = naive::proceed(buffer, start_ptr, ptr, aligned_ptr);
}
escape_aligned(buffer, start_ptr, aligned_ptr, end_ptr);
}
ptr = aligned_ptr;
pub unsafe fn escape_aligned(
buffer: &mut Buffer,
mut start_ptr: *const u8,
mut ptr: *const u8,
end_ptr: *const u8,
) {
while ptr.add(USIZE_BYTES) <= end_ptr {
debug_assert_eq!((ptr as usize) % USIZE_BYTES, 0);

View File

@ -1,10 +1,12 @@
//! HTML escaping
//! HTML escaping utilities
//!
//! By default sailfish replaces the characters `&"'<>` with the equivalent html.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod avx2;
mod fallback;
mod naive;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod sse2;
use std::mem;
@ -30,16 +32,12 @@ static ESCAPE_LUT: [u8; 256] = [
const ESCAPED: [&str; 5] = ["&quot;", "&amp;", "&#039;", "&lt;", "&gt;"];
const ESCAPED_LEN: usize = 5;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
static FN: AtomicPtr<()> = AtomicPtr::new(escape as FnRaw);
#[cfg(target_feature = "avx2")]
#[inline]
fn escape(feed: &str, buf: &mut Buffer) {
unsafe { avx2::escape(feed, buf) }
}
#[cfg(not(target_feature = "avx2"))]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn escape(feed: &str, buf: &mut Buffer) {
debug_assert!(feed.len() >= 16);
let fun = if is_x86_feature_detected!("avx2") {
avx2::escape
} else if is_x86_feature_detected!("sse2") {
@ -53,20 +51,37 @@ fn escape(feed: &str, buf: &mut Buffer) {
}
/// Change the default escape function
#[deprecated(since = "0.1.2", note = "This function does not anything any more")]
#[doc(hidden)]
pub fn register_escape_fn(_fun: fn(&str, &mut Buffer)) {}
pub(crate) fn escape_to_buf(feed: &str, buf: &mut Buffer) {
/// write the escaped contents into `Buffer`
#[cfg_attr(feature = "perf-inline", inline)]
pub fn escape_to_buf(feed: &str, buf: &mut Buffer) {
unsafe {
if feed.len() < 16 {
let start_ptr = feed.as_ptr();
let end_ptr = start_ptr.add(feed.len());
naive::escape(buf, start_ptr, start_ptr, end_ptr);
return;
}
buf.reserve(feed.len() * 6);
let l = naive::escape_small(feed, buf.as_mut_ptr().add(buf.len()));
buf.set_len(buf.len() + l);
} else {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
#[cfg(target_feature = "avx2")]
{
avx2::escape(feed, buf);
}
let fun = FN.load(Ordering::Relaxed);
mem::transmute::<FnRaw, fn(&str, &mut Buffer)>(fun)(feed, buf);
#[cfg(not(target_feature = "avx2"))]
{
let fun = FN.load(Ordering::Relaxed);
mem::transmute::<FnRaw, fn(&str, &mut Buffer)>(fun)(feed, buf);
}
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
{
fallback::escape(feed, buf);
}
}
}
}
@ -149,31 +164,38 @@ mod tests {
let mut buf3 = Buffer::new();
for len in 0..100 {
data.clear();
for _ in 0..len {
// xorshift
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
for _ in 0..10 {
data.clear();
for _ in 0..len {
// xorshift
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
let idx = state as usize % ASCII_CHARS.len();
data.push(ASCII_CHARS[idx]);
let idx = state as usize % ASCII_CHARS.len();
data.push(ASCII_CHARS[idx]);
}
let s = unsafe { std::str::from_utf8_unchecked(&*data) };
buf1.clear();
buf2.clear();
buf3.clear();
unsafe {
escape_to_buf(s, &mut buf1);
fallback::escape(s, &mut buf2);
naive::escape(
&mut buf3,
s.as_ptr(),
s.as_ptr(),
s.as_ptr().add(s.len()),
);
}
assert_eq!(buf1.as_str(), buf3.as_str());
assert_eq!(buf2.as_str(), buf3.as_str());
}
let s = unsafe { std::str::from_utf8_unchecked(&*data) };
buf1.clear();
buf2.clear();
buf3.clear();
unsafe {
escape_to_buf(s, &mut buf1);
fallback::escape(s, &mut buf2);
naive::escape(&mut buf3, s.as_ptr(), s.as_ptr(), s.as_ptr().add(s.len()));
}
assert_eq!(buf1.as_str(), buf3.as_str());
assert_eq!(buf2.as_str(), buf3.as_str());
}
}
}

View File

@ -1,5 +1,6 @@
use core::slice;
use super::super::utils::memcpy_16;
use super::super::Buffer;
use super::{ESCAPED, ESCAPED_LEN, ESCAPE_LUT};
@ -12,7 +13,7 @@ pub(super) unsafe fn escape(
) {
start_ptr = proceed(buffer, start_ptr, ptr, end_ptr);
if end_ptr > start_ptr {
if likely!(end_ptr > start_ptr) {
let slc = slice::from_raw_parts(start_ptr, end_ptr as usize - start_ptr as usize);
buffer.push_str(std::str::from_utf8_unchecked(slc));
}
@ -29,7 +30,7 @@ pub(super) unsafe fn proceed(
debug_assert!(start_ptr <= ptr);
let idx = ESCAPE_LUT[*ptr as usize] as usize;
debug_assert!(idx <= 9);
if idx < ESCAPED_LEN {
if unlikely!(idx < ESCAPED_LEN) {
if ptr > start_ptr {
let slc =
slice::from_raw_parts(start_ptr, ptr as usize - start_ptr as usize);
@ -45,3 +46,41 @@ pub(super) unsafe fn proceed(
debug_assert!(start_ptr <= ptr);
start_ptr
}
pub(super) unsafe fn escape_small(feed: &str, mut buf: *mut u8) -> usize {
let mut start_ptr = feed.as_ptr();
let mut ptr = start_ptr;
let end_ptr = start_ptr.add(feed.len());
let buf_begin = buf;
while ptr < end_ptr {
debug_assert!(start_ptr <= ptr);
let idx = *ESCAPE_LUT.get_unchecked(*ptr as usize) as usize;
debug_assert!(idx <= 9);
if unlikely!(idx < ESCAPED_LEN) {
let escaped = ESCAPED.get_unchecked(idx);
if ptr > start_ptr {
let slc =
slice::from_raw_parts(start_ptr, ptr as usize - start_ptr as usize);
memcpy_16(slc.as_ptr(), buf, slc.len());
buf = buf.add(slc.len());
}
memcpy_16(escaped.as_ptr(), buf, escaped.len());
buf = buf.add(escaped.len());
start_ptr = ptr.add(1);
}
ptr = ptr.add(1);
}
debug_assert_eq!(ptr, end_ptr);
debug_assert!(start_ptr <= ptr);
if likely!(end_ptr > start_ptr) {
let slc = slice::from_raw_parts(start_ptr, end_ptr as usize - start_ptr as usize);
memcpy_16(slc.as_ptr(), buf, slc.len());
buf = buf.add(slc.len());
}
buf as usize - buf_begin as usize
}

View File

@ -7,23 +7,14 @@ use std::arch::x86_64::*;
use std::slice;
use super::super::Buffer;
use super::naive;
use super::{ESCAPED, ESCAPED_LEN, ESCAPE_LUT};
const VECTOR_BYTES: usize = std::mem::size_of::<__m128i>();
const VECTOR_ALIGN: usize = VECTOR_BYTES - 1;
#[target_feature(enable = "sse2")]
#[inline]
pub unsafe fn escape(feed: &str, buffer: &mut Buffer) {
let len = feed.len();
if len < VECTOR_BYTES {
let start_ptr = feed.as_ptr();
let end_ptr = start_ptr.add(len);
naive::escape(buffer, start_ptr, start_ptr, end_ptr);
return;
}
let mut start_ptr = feed.as_ptr();
let end_ptr = start_ptr.add(len);
@ -32,11 +23,11 @@ pub unsafe fn escape(feed: &str, buffer: &mut Buffer) {
let v_key1 = _mm_set1_epi8(0x27);
let v_key2 = _mm_set1_epi8(0x3e);
let maskgen = |x: __m128i| -> i32 {
let maskgen = |x: __m128i| -> u32 {
_mm_movemask_epi8(_mm_or_si128(
_mm_cmpeq_epi8(_mm_or_si128(x, v_independent1), v_key1),
_mm_cmpeq_epi8(_mm_or_si128(x, v_independent2), v_key2),
))
)) as u32
};
let mut ptr = start_ptr;
@ -68,29 +59,7 @@ pub unsafe fn escape(feed: &str, buffer: &mut Buffer) {
}
ptr = aligned_ptr;
escape_aligned(buffer, start_ptr, ptr, end_ptr);
}
#[target_feature(enable = "sse2")]
#[cfg_attr(feature = "perf-inline", inline)]
pub unsafe fn escape_aligned(
buffer: &mut Buffer,
mut start_ptr: *const u8,
mut ptr: *const u8,
end_ptr: *const u8,
) {
let mut next_ptr = ptr.add(VECTOR_BYTES);
let v_independent1 = _mm_set1_epi8(5);
let v_independent2 = _mm_set1_epi8(2);
let v_key1 = _mm_set1_epi8(0x27);
let v_key2 = _mm_set1_epi8(0x3e);
let maskgen = |x: __m128i| -> i32 {
_mm_movemask_epi8(_mm_or_si128(
_mm_cmpeq_epi8(_mm_or_si128(x, v_independent1), v_key1),
_mm_cmpeq_epi8(_mm_or_si128(x, v_independent2), v_key2),
))
};
while next_ptr <= end_ptr {
debug_assert_eq!((ptr as usize) % VECTOR_BYTES, 0);
@ -117,10 +86,14 @@ pub unsafe fn escape_aligned(
next_ptr = next_ptr.add(VECTOR_BYTES);
}
next_ptr = ptr.add(8);
if next_ptr <= end_ptr {
debug_assert_eq!((ptr as usize) % VECTOR_BYTES, 0);
let mut mask = maskgen(_mm_loadl_epi64(ptr as *const __m128i));
debug_assert!(next_ptr > end_ptr);
if ptr < end_ptr {
debug_assert!((end_ptr as usize - ptr as usize) < VECTOR_BYTES);
let backs = VECTOR_BYTES - (end_ptr as usize - ptr as usize);
let read_ptr = ptr.sub(backs);
let mut mask = maskgen(_mm_loadu_si128(read_ptr as *const __m128i)) >> backs;
while mask != 0 {
let trailing_zeros = mask.trailing_zeros() as usize;
let ptr2 = ptr.add(trailing_zeros);
@ -138,11 +111,10 @@ pub unsafe fn escape_aligned(
}
mask ^= 1 << trailing_zeros;
}
ptr = next_ptr;
}
debug_assert!(ptr <= end_ptr);
debug_assert!(start_ptr <= ptr);
naive::escape(buffer, start_ptr, ptr, end_ptr);
if end_ptr > start_ptr {
let slc = slice::from_raw_parts(start_ptr, end_ptr as usize - start_ptr as usize);
buffer.push_str(std::str::from_utf8_unchecked(slc));
}
}

View File

@ -1,512 +0,0 @@
use std::ptr;
const DEC_DIGITS_LUT: &'static [u8] = b"\
0001020304050607080910111213141516171819\
2021222324252627282930313233343536373839\
4041424344454647484950515253545556575859\
6061626364656667686970717273747576777879\
8081828384858687888990919293949596979899";
macro_rules! lookup {
($idx:expr) => {
DEC_DIGITS_LUT.as_ptr().add(($idx as usize) << 1)
};
}
/// write integer smaller than 10000
#[inline]
unsafe fn write_small(n: u16, buf: *mut u8) -> usize {
debug_assert!(n < 10000);
if n < 100 {
if n < 10 {
*buf = n as u8 + 0x30;
1
} else {
ptr::copy_nonoverlapping(lookup!(n), buf, 2);
2
}
} else {
if n < 1000 {
let d1 = (n / 100) as u8;
let d2 = n % 100;
*buf = d1 + 0x30;
ptr::copy_nonoverlapping(lookup!(d2), buf.add(1), 2);
3
} else {
let d1 = n / 100;
let d2 = n % 100;
ptr::copy_nonoverlapping(lookup!(d1), buf, 2);
ptr::copy_nonoverlapping(lookup!(d2), buf.add(2), 2);
4
}
}
}
/// write integer smaller with 0 padding
#[inline]
unsafe fn write_small_pad(n: u16, buf: *mut u8) {
debug_assert!(n < 10000);
let d1 = n / 100;
let d2 = n % 100;
ptr::copy_nonoverlapping(lookup!(d1), buf, 2);
ptr::copy_nonoverlapping(lookup!(d2), buf.add(2), 2);
}
unsafe fn write_u8(n: u8, buf: *mut u8) -> usize {
if n < 10 {
*buf = n + 0x30;
1
} else if n < 100 {
ptr::copy_nonoverlapping(lookup!(n), buf, 2);
2
} else {
let d1 = n / 100;
let d2 = n % 100;
*buf = d1 + 0x30;
ptr::copy_nonoverlapping(lookup!(d2), buf.add(1), 2);
3
}
}
unsafe fn write_u16(n: u16, buf: *mut u8) -> usize {
if n < 100 {
if n < 10 {
*buf = n as u8 + 0x30;
1
} else {
ptr::copy_nonoverlapping(lookup!(n), buf, 2);
2
}
} else if n < 10000 {
if n < 1000 {
let d1 = (n / 100) as u8;
let d2 = n % 100;
*buf = d1 + 0x30;
ptr::copy_nonoverlapping(lookup!(d2), buf.add(1), 2);
3
} else {
let d1 = n / 100;
let d2 = n % 100;
ptr::copy_nonoverlapping(lookup!(d1), buf, 2);
ptr::copy_nonoverlapping(lookup!(d2), buf.add(2), 2);
4
}
} else {
let b = (n / 10000) as u8; // 1 to 6
let c = n % 10000;
*buf = b + 0x30;
write_small_pad(c, buf.add(1));
5
}
}
unsafe fn write_u32(mut n: u32, buf: *mut u8) -> usize {
if n < 10000 {
write_small(n as u16, buf)
} else if n < 100000000 {
let b = n / 10000;
let c = n % 10000;
let l = write_small(b as u16, buf);
write_small_pad(c as u16, buf.add(l));
l + 4
} else {
let a = n / 100000000; // 1 to 42
n %= 100000000;
let l = if a >= 10 {
ptr::copy_nonoverlapping(lookup!(a), buf, 2);
2
} else {
*buf = a as u8 + 0x30;
1
};
let b = n / 10000;
let c = n % 10000;
write_small_pad(b as u16, buf.add(l));
write_small_pad(c as u16, buf.add(l + 4));
l + 8
}
}
unsafe fn write_u64(mut n: u64, buf: *mut u8) -> usize {
if n < 10000 {
write_small(n as u16, buf)
} else if n < 100000000 {
let n = n as u32;
let b = n / 10000;
let c = n % 10000;
let l = write_small(b as u16, buf);
write_small_pad(c as u16, buf.add(l));
l + 4
} else if n < 10000000000000000 {
let v0 = n / 100000000;
let v1 = (n % 100000000) as u32;
let l = if v0 < 10000 {
write_small(v0 as u16, buf)
} else {
let b0 = v0 / 10000;
let c0 = v0 % 10000;
let l = write_small(b0 as u16, buf);
write_small_pad(c0 as u16, buf.add(l));
l + 4
};
let b1 = v1 / 10000;
let c1 = v1 % 10000;
write_small_pad(b1 as u16, buf.add(l));
write_small_pad(c1 as u16, buf.add(l + 4));
l + 8
} else {
let a = n / 10000000000000000; // 1 to 1844
n %= 10000000000000000;
let v0 = (n / 100000000) as u32;
let v1 = (n % 100000000) as u32;
let b0 = v0 / 10000;
let c0 = v0 % 10000;
let b1 = v1 / 10000;
let c1 = v1 % 10000;
let l = write_small(a as u16, buf);
write_small_pad(b0 as u16, buf.add(l));
write_small_pad(c0 as u16, buf.add(l + 4));
write_small_pad(b1 as u16, buf.add(l + 8));
write_small_pad(c1 as u16, buf.add(l + 12));
l + 16
}
}
unsafe fn write_u128(n: u128, buf: *mut u8) -> usize {
if n <= std::u64::MAX as u128 {
write_u64(n as u64, buf)
} else if n < 100000000000000000000000000000000 {
let a0 = (n / 10000000000000000) as u64;
let a1 = (n % 10000000000000000) as u64;
let b0 = (a1 / 100000000) as u32;
let b1 = (a1 / 100000000) as u32;
let c0 = (b0 / 10000) as u16;
let c1 = (b0 % 10000) as u16;
let c2 = (b1 / 10000) as u16;
let c3 = (b1 % 10000) as u16;
let l = write_u64(a0, buf);
write_small_pad(c0, buf.add(l));
write_small_pad(c1, buf.add(l + 4));
write_small_pad(c2, buf.add(l + 8));
write_small_pad(c3, buf.add(l + 12));
l + 16
} else {
let a0 = (n / 100000000000000000000000000000000) as u32; // 1 to 3402823
let a1 = n % 100000000000000000000000000000000;
let b0 = (a1 / 10000000000000000) as u64;
let b1 = (a1 % 10000000000000000) as u64;
let c0 = (b0 / 100000000) as u32;
let c1 = (b0 % 100000000) as u32;
let c2 = (b1 / 100000000) as u32;
let c3 = (b1 % 100000000) as u32;
let d0 = (c0 / 10000) as u16;
let d1 = (c0 % 10000) as u16;
let d2 = (c1 / 10000) as u16;
let d3 = (c1 % 10000) as u16;
let d4 = (c2 / 10000) as u16;
let d5 = (c2 % 10000) as u16;
let d6 = (c3 / 10000) as u16;
let d7 = (c3 % 10000) as u16;
let l = if a0 < 10000 {
write_small(a0 as u16, buf)
} else {
let b0 = (a0 / 10000) as u16;
let b1 = (a0 % 10000) as u16;
let l = write_small(b0, buf);
write_small_pad(b1, buf.add(l));
l + 4
};
write_small_pad(d0, buf.add(l));
write_small_pad(d1, buf.add(l + 4));
write_small_pad(d2, buf.add(l + 8));
write_small_pad(d3, buf.add(l + 12));
write_small_pad(d4, buf.add(l + 16));
write_small_pad(d5, buf.add(l + 20));
write_small_pad(d6, buf.add(l + 24));
write_small_pad(d7, buf.add(l + 28));
l + 32
}
}
pub trait Integer {
const MAX_LEN: usize;
unsafe fn write_to(self, buf: *mut u8) -> usize;
}
macro_rules! impl_integer {
($unsigned:ty, $signed:ty, $conv:ty, $func:ident, $max_len:expr) => {
impl Integer for $unsigned {
const MAX_LEN: usize = $max_len;
#[inline]
unsafe fn write_to(self, buf: *mut u8) -> usize {
$func(self as $conv, buf)
}
}
impl Integer for $signed {
const MAX_LEN: usize = $max_len + 1;
#[inline]
unsafe fn write_to(self, mut buf: *mut u8) -> usize {
if self >= 0 {
$func(self as $conv, buf)
} else {
*buf = b'-';
buf = buf.add(1);
let n = (!(self as $conv)).wrapping_add(1);
$func(n, buf) + 1
}
}
}
};
}
impl_integer!(u8, i8, u8, write_u8, 3);
impl_integer!(u16, i16, u16, write_u16, 5);
impl_integer!(u32, i32, u32, write_u32, 10);
impl_integer!(u64, i64, u64, write_u64, 20);
impl_integer!(u128, i128, u128, write_u128, 39);
#[cfg(target_pointer_width = "16")]
impl_integer!(usize, isize, u16, write_u16, 6);
#[cfg(target_pointer_width = "32")]
impl_integer!(usize, isize, u32, write_u32, 11);
#[cfg(target_pointer_width = "64")]
impl_integer!(usize, isize, u64, write_u64, 20);
#[cfg(test)]
mod tests {
// comprehenisive test
#[test]
fn test_i8_all() {
use super::Integer;
let mut buf = Vec::with_capacity(i8::MAX_LEN);
for n in std::i8::MIN..=std::i8::MAX {
unsafe {
let l = n.write_to(buf.as_mut_ptr());
buf.set_len(l);
assert_eq!(std::str::from_utf8_unchecked(&*buf), format!("{}", n));
}
}
}
// random test
#[test]
fn test_u64_random() {
use super::Integer;
let mut buf = Vec::with_capacity(u64::MAX_LEN);
let mut state = 88172645463325252u64;
for _ in 0..100 {
// xorshift
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
unsafe {
let l = state.write_to(buf.as_mut_ptr());
buf.set_len(l);
assert_eq!(std::str::from_utf8_unchecked(&*buf), format!("{}", state));
}
}
}
macro_rules! make_test {
($name:ident, $type:ty, $($value:expr),*) => {
#[test]
fn $name() {
use super::Integer;
unsafe fn test_write(val: $type, buf: &mut Vec<u8>) {
let l = val.write_to(buf.as_mut_ptr());
buf.set_len(l);
assert_eq!(
std::str::from_utf8_unchecked(&*buf),
format!("{}", val)
);
}
let mut buf = Vec::with_capacity(<$type>::MAX_LEN);
unsafe {
$(
test_write($value as $type, &mut buf);
)*
}
}
}
}
// boundary tests
make_test!(test_u8, u8, 0, 1, 9, 10, 99, 100, 254, 255);
make_test!(test_u16, u16, 0, 9, 10, 99, 100, 999, 1000, 9999, 10000, 65535);
make_test!(
test_u32, u32, 0, 9, 10, 99, 100, 999, 1000, 9999, 10000, 99999, 100000, 999999,
1000000, 9999999, 10000000, 99999999, 100000000, 999999999, 1000000000,
4294967295
);
make_test!(
test_u64,
u64,
0,
9,
10,
99,
100,
999,
1000,
9999,
10000,
99999,
100000,
999999,
1000000,
9999999,
10000000,
99999999,
100000000,
999999999,
1000000000,
9999999999,
10000000000,
99999999999,
100000000000,
999999999999,
1000000000000,
9999999999999,
10000000000000,
99999999999999,
100000000000000,
999999999999999,
1000000000000000,
9999999999999999,
10000000000000000,
99999999999999999,
100000000000000000,
999999999999999999,
1000000000000000000,
9999999999999999999,
10000000000000000000,
18446744073709551615
);
make_test!(
test_u128,
u128,
0,
9,
10,
99,
100,
999,
1000,
9999,
10000,
99999,
100000,
999999,
1000000,
9999999,
10000000,
99999999,
100000000,
999999999,
1000000000,
9999999999,
10000000000,
99999999999,
100000000000,
999999999999,
1000000000000,
9999999999999,
10000000000000,
99999999999999,
100000000000000,
999999999999999,
1000000000000000,
9999999999999999,
10000000000000000,
99999999999999999,
100000000000000000,
999999999999999999,
1000000000000000000,
9999999999999999999,
10000000000000000000,
99999999999999999999,
100000000000000000000,
999999999999999999999,
1000000000000000000000,
9999999999999999999999,
10000000000000000000000,
99999999999999999999999,
100000000000000000000000,
100000000000000000000000,
999999999999999999999999,
1000000000000000000000000,
9999999999999999999999999,
10000000000000000000000000,
99999999999999999999999999,
100000000000000000000000000,
999999999999999999999999999,
1000000000000000000000000000,
9999999999999999999999999999,
10000000000000000000000000000,
99999999999999999999999999999,
100000000000000000000000000000,
999999999999999999999999999999,
1000000000000000000000000000000,
9999999999999999999999999999999,
10000000000000000000000000000000,
99999999999999999999999999999999,
100000000000000000000000000000000,
999999999999999999999999999999999,
1000000000000000000000000000000000,
9999999999999999999999999999999999,
10000000000000000000000000000000000,
99999999999999999999999999999999999,
100000000000000000000000000000000000,
999999999999999999999999999999999999,
1000000000000000000000000000000000000,
9999999999999999999999999999999999999,
10000000000000000000000000000000000000,
99999999999999999999999999999999999999,
100000000000000000000000000000000000000,
340282366920938463463374607431768211455
);
make_test!(test_i8, i8, std::i8::MIN, std::i8::MAX);
make_test!(test_i16, i16, std::i16::MIN, std::i16::MAX);
make_test!(test_i32, i32, std::i32::MIN, std::i32::MAX);
make_test!(test_i64, i64, std::i64::MIN, std::i64::MAX);
make_test!(test_i128, i128, std::i128::MIN, std::i128::MAX);
}

View File

@ -1,8 +1,10 @@
//! Sailfish runtime
#[macro_use]
mod utils;
mod buffer;
pub mod escape;
mod integer;
mod macros;
mod render;
mod size_hint;
@ -30,6 +32,7 @@ pub struct RenderError {
}
impl RenderError {
/// Construct a new error with custom message
pub fn new(msg: &str) -> Self {
Self {
kind: RenderErrorKind::Msg(msg.to_owned()),
@ -66,6 +69,7 @@ impl From<fmt::Error> for RenderError {
pub type RenderResult = Result<String, RenderError>;
#[doc(hidden)]
pub struct Context {
#[doc(hidden)]
pub buf: Buffer,

View File

@ -23,13 +23,15 @@ use super::{escape, RenderError};
/// }
/// ```
pub trait Render {
/// render to `Buffer` without escaping
fn render(&self, b: &mut Buffer) -> Result<(), RenderError>;
/// render to `Buffer` with HTML escaping
#[inline]
fn render_escaped(&self, b: &mut Buffer) -> Result<(), RenderError> {
let mut tmp = Buffer::new();
self.render(&mut tmp)?;
b.push_str(tmp.as_str());
escape::escape_to_buf(tmp.as_str(), b);
Ok(())
}
}
@ -85,6 +87,7 @@ impl Render for char {
'&' => b.push_str("&amp;"),
'<' => b.push_str("&lt;"),
'>' => b.push_str("&gt;"),
'\'' => b.push_str("&#039;"),
_ => b.push(*self),
}
Ok(())
@ -148,17 +151,18 @@ macro_rules! render_int {
($($int:ty),*) => {
$(
impl Render for $int {
#[inline]
#[cfg_attr(feature = "perf-inline", inline)]
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
use super::integer::Integer;
use itoap::Integer;
b.reserve(Self::MAX_LEN);
unsafe {
let ptr = b.as_mut_ptr().add(b.len());
let l = self.write_to(ptr);
let l = itoap::write_to_ptr(ptr, *self);
b.set_len(b.len() + l);
}
debug_assert!(b.len() <= b.capacity());
Ok(())
}
@ -175,14 +179,15 @@ macro_rules! render_int {
render_int!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize);
impl Render for f32 {
#[inline]
#[cfg_attr(feature = "perf-inline", inline)]
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
if self.is_finite() {
if likely!(self.is_finite()) {
unsafe {
b.reserve(16);
let ptr = b.as_mut_ptr().add(b.len());
let l = ryu::raw::format32(*self, ptr);
b.set_len(b.len() + l);
debug_assert!(b.len() <= b.capacity());
}
} else if self.is_nan() {
b.push_str("NaN");
@ -203,14 +208,15 @@ impl Render for f32 {
}
impl Render for f64 {
#[inline]
#[cfg_attr(feature = "perf-inline", inline)]
fn render(&self, b: &mut Buffer) -> Result<(), RenderError> {
if self.is_finite() {
if likely!(self.is_finite()) {
unsafe {
b.reserve(24);
let ptr = b.as_mut_ptr().add(b.len());
let l = ryu::raw::format64(*self, ptr);
b.set_len(b.len() + l);
debug_assert!(b.len() <= b.capacity());
}
} else if self.is_nan() {
b.push_str("NaN");

View File

@ -1,7 +1,7 @@
use std::sync::atomic::{AtomicUsize, Ordering};
/// Dynamically updated size hint
#[derive(Debug, Default)]
#[doc(hidden)]
pub struct SizeHint {
value: AtomicUsize,
}
@ -25,8 +25,8 @@ impl SizeHint {
/// as the value passed on update()
#[inline]
pub fn update(&self, mut value: usize) {
value = value + value / 8;
if self.get() < value {
value = value + value / 4;
if unlikely!(self.get() < value) {
self.value.store(value, Ordering::Release);
}
}

View File

@ -0,0 +1,58 @@
use std::ptr;
#[cfg(sailfish_nightly)]
macro_rules! likely {
($val:expr) => {
std::intrinsics::likely($val)
};
}
#[cfg(not(sailfish_nightly))]
macro_rules! likely {
($val:expr) => {
$val
};
}
#[cfg(sailfish_nightly)]
macro_rules! unlikely {
($val:expr) => {
std::intrinsics::unlikely($val)
};
}
#[cfg(not(sailfish_nightly))]
macro_rules! unlikely {
($val:expr) => {
$val
};
}
/// memcpy implementation based on glibc (https://github.molgen.mpg.de/git-mirror/glibc/blob/master/sysdeps/x86_64/multiarch/memcpy-avx-unaligned.S)
#[allow(clippy::cast_ptr_alignment)]
pub unsafe fn memcpy_16(src: *const u8, dst: *mut u8, len: usize) {
debug_assert!(len <= 16);
let len_u8 = len as u8;
if len_u8 >= 8 {
let offset = len - 8;
let t2 = ptr::read_unaligned(src.add(offset) as *const u64);
let t1 = ptr::read_unaligned(src as *const u64);
ptr::write_unaligned(dst.add(offset) as *mut u64, t2);
ptr::write_unaligned(dst as *mut u64, t1);
} else if len_u8 >= 4 {
let offset = len - 4;
let t2 = ptr::read_unaligned(src.add(offset) as *const u32);
let t1 = ptr::read_unaligned(src as *const u32);
ptr::write_unaligned(dst.add(offset) as *mut u32, t2);
ptr::write_unaligned(dst as *mut u32, t1);
} else if len_u8 >= 2 {
let offset = len - 2;
let t2 = ptr::read_unaligned(src.add(offset) as *const u16);
let t1 = ptr::read_unaligned(src as *const u16);
ptr::write_unaligned(dst.add(offset) as *mut u16, t2);
ptr::write_unaligned(dst as *mut u16, t1);
} else if len_u8 >= 1 {
*dst = *src;
}
}

19
syntax/vim/README.md Normal file
View File

@ -0,0 +1,19 @@
# Syntax Highlighting for Sailfish Templates in Vim
## Installation
### Using dein.vim
```
call dein#add('Kogia-sima/sailfish', {'rtp': 'syntax/vim'})
```
### Using vim-plug
```
Plug 'Kogia-sima/sailfish', { 'rtp': 'syntax/vim' }
```
## Screenshot
![Screenshot](./screenshot.png)

BIN
syntax/vim/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@ -1 +1,2 @@
/test.stpl
*.vsix

View File

@ -1,4 +1,5 @@
.vscode/**
.vscode-test/**
.gitignore
*.vsix
vsc-extension-quickstart.md

View File

@ -2,6 +2,8 @@
This directory contains Syntax Highlighting extension for sailfish templates in Visual Studio Code.
The extension is available at [VisualStudio Marketplace](https://marketplace.visualstudio.com/items?itemName=kogia-sima.vscode-sailfish).
## Features
- Full Rust syntax highlighting rules inside code blocks

View File

@ -4,6 +4,8 @@
"description": "Syntax highlighting for sailfish templates in VSCode",
"version": "0.1.0",
"author": "Ryohei Machida <orcinus4627@gmail.com>",
"publisher": "kogia-sima",
"repository": "Kogia-sima/sailfish",
"license": "MIT",
"engines": {
"vscode": "^1.45.0"